nodebb-plugin-topic-search-button 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/library.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const routeHelpers = nodebb.require('./src/routes/helpers');
|
|
4
|
+
const settings = nodebb.require('./src/meta/settings');
|
|
5
|
+
|
|
6
|
+
const plugin = {};
|
|
7
|
+
|
|
8
|
+
plugin.id = 'topic-search-button';
|
|
9
|
+
plugin.defaults = {
|
|
10
|
+
enabled: false,
|
|
11
|
+
};
|
|
12
|
+
plugin.enabled = false;
|
|
13
|
+
|
|
14
|
+
function normalizeEnabled(stored) {
|
|
15
|
+
if (typeof stored === 'boolean') {
|
|
16
|
+
return stored;
|
|
17
|
+
}
|
|
18
|
+
return stored === 'true' || stored === 'on' || stored === '1';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
plugin.init = async function (params) {
|
|
22
|
+
const { router } = params;
|
|
23
|
+
routeHelpers.setupAdminPageRoute(router, '/admin/plugins/topic-search-button', [], async (req, res) => {
|
|
24
|
+
res.render('admin/plugins/topic-search-button', {});
|
|
25
|
+
});
|
|
26
|
+
await settings.setOnEmpty(plugin.id, plugin.defaults);
|
|
27
|
+
const stored = await settings.getOne(plugin.id, 'enabled');
|
|
28
|
+
plugin.enabled = normalizeEnabled(stored);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
plugin.exposeConfig = async function (config) {
|
|
32
|
+
config.topicSearchButtonEnabled = plugin.enabled;
|
|
33
|
+
return config;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
plugin.onSettingsSave = async function (data) {
|
|
37
|
+
if (data && data.plugin === plugin.id && data.settings) {
|
|
38
|
+
plugin.enabled = normalizeEnabled(data.settings.enabled);
|
|
39
|
+
}
|
|
40
|
+
return data;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
plugin.addAdminNavigation = async function (header) {
|
|
44
|
+
header.plugins.push({
|
|
45
|
+
route: '/plugins/topic-search-button',
|
|
46
|
+
icon: 'fa-search',
|
|
47
|
+
name: 'Topic Search Button',
|
|
48
|
+
});
|
|
49
|
+
return header;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
module.exports = plugin;
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nodebb-plugin-topic-search-button",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Adds a 'Search in topic' button to the topic toolbar that pre-fills the search box with the in:topic-<tid> prefix.",
|
|
5
|
+
"main": "library.js",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"scripts": {},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"nodebb",
|
|
10
|
+
"plugin",
|
|
11
|
+
"search",
|
|
12
|
+
"topic"
|
|
13
|
+
],
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "JasonCH",
|
|
16
|
+
"email": "jasonwch@msn.com"
|
|
17
|
+
},
|
|
18
|
+
"nbbpm": {
|
|
19
|
+
"compatibility": "^4.14.0"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {}
|
|
22
|
+
}
|
package/plugin.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "nodebb-plugin-topic-search-button",
|
|
3
|
+
"name": "Topic Search Button",
|
|
4
|
+
"description": "Adds a 'Search in topic' button to the topic toolbar that pre-fills the search box with the in:topic-<tid> prefix.",
|
|
5
|
+
"library": "library.js",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{ "hook": "static:app.load", "method": "init" },
|
|
8
|
+
{ "hook": "filter:admin.header.build", "method": "addAdminNavigation" },
|
|
9
|
+
{ "hook": "filter:config.get", "method": "exposeConfig" },
|
|
10
|
+
{ "hook": "filter:settings.set", "method": "onSettingsSave" }
|
|
11
|
+
],
|
|
12
|
+
"staticDirs": {
|
|
13
|
+
"static": "./static"
|
|
14
|
+
},
|
|
15
|
+
"templates": "./static/templates",
|
|
16
|
+
"modules": {
|
|
17
|
+
"../admin/plugins/topic-search-button.js": "./static/lib/admin.js"
|
|
18
|
+
},
|
|
19
|
+
"scripts": [
|
|
20
|
+
"static/lib/client.js"
|
|
21
|
+
],
|
|
22
|
+
"languages": "static/languages",
|
|
23
|
+
"defaultLang": "en-GB",
|
|
24
|
+
"settingsRoute": "/admin/plugins/topic-search-button"
|
|
25
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"search-in-topic": "Search in topic",
|
|
3
|
+
"admin.title": "Topic Search Button",
|
|
4
|
+
"admin.enable": "Show \"Search in topic\" button",
|
|
5
|
+
"admin.enable-help": "Adds a button to the topic toolbar that opens the search dropdown pre-filled with the in:topic prefix, scoped to the current topic. Visible to any visitor with the search content privilege."
|
|
6
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
This file is located in the "modules" block of plugin.json.
|
|
5
|
+
It is only loaded when the user navigates to /admin/plugins/topic-search-button.
|
|
6
|
+
*/
|
|
7
|
+
define('admin/plugins/topic-search-button', [
|
|
8
|
+
'settings',
|
|
9
|
+
'alerts',
|
|
10
|
+
], (settings, alerts) => {
|
|
11
|
+
const ACP = {};
|
|
12
|
+
|
|
13
|
+
ACP.init = function () {
|
|
14
|
+
settings.load('topic-search-button', $('.topic-search-button-settings'), () => {
|
|
15
|
+
});
|
|
16
|
+
$('#save').on('click', saveSettings);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function saveSettings() {
|
|
20
|
+
settings.save('topic-search-button', $('.topic-search-button-settings'), () => {
|
|
21
|
+
alerts.alert({
|
|
22
|
+
type: 'success',
|
|
23
|
+
alert_id: 'topic-search-button-saved',
|
|
24
|
+
title: 'Settings Saved',
|
|
25
|
+
message: 'Your settings have been saved successfully.',
|
|
26
|
+
timeout: 5000,
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return ACP;
|
|
32
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
(function () {
|
|
4
|
+
require(['hooks'], function (hooks) {
|
|
5
|
+
var previousTid = null;
|
|
6
|
+
|
|
7
|
+
hooks.on('action:topic.loaded', function () {
|
|
8
|
+
if (!config.topicSearchButtonEnabled || !config.searchEnabled || !app.user.privileges['search:content']) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
injectButton();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
hooks.on('action:ajaxify.end', function () {
|
|
15
|
+
var currentTid = ajaxify.data && ajaxify.data.template && ajaxify.data.template.topic ?
|
|
16
|
+
String(ajaxify.data.tid) : null;
|
|
17
|
+
|
|
18
|
+
if (previousTid && previousTid !== currentTid) {
|
|
19
|
+
cleanSearchInputs();
|
|
20
|
+
}
|
|
21
|
+
previousTid = currentTid;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
function cleanSearchInputs() {
|
|
25
|
+
$('[component="search/fields"] input[name="query"]').each(function () {
|
|
26
|
+
var $input = $(this);
|
|
27
|
+
var val = $input.val();
|
|
28
|
+
if (val && val.indexOf('in:topic-') === 0) {
|
|
29
|
+
$input.val('');
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
$('[component="search/fields"] .quick-search-container').addClass('hidden');
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
function injectButton() {
|
|
37
|
+
require(['translator', 'utils'], function (translator, utils) {
|
|
38
|
+
translator.translate('[[topic-search-button:search-in-topic]]', function (label) {
|
|
39
|
+
var escapedLabel = utils.escapeHTML(label);
|
|
40
|
+
var buttonHtml =
|
|
41
|
+
'<button component="topic/search-in-topic" title="' + escapedLabel + '" ' +
|
|
42
|
+
'class="btn btn-ghost btn-sm ff-secondary d-flex gap-2 align-items-center text-truncate">' +
|
|
43
|
+
'<i class="fa fa-fw fa-magnifying-glass-plus text-primary"></i>' +
|
|
44
|
+
'<span class="d-none d-md-inline fw-semibold text-truncate text-nowrap">' + escapedLabel + '</span>' +
|
|
45
|
+
'</button>';
|
|
46
|
+
|
|
47
|
+
var $button = $(buttonHtml);
|
|
48
|
+
var injected = false;
|
|
49
|
+
|
|
50
|
+
// 1. Vertical sidebar panel (.topic-sidebar-tools, Harmony topicSidebarTools layout).
|
|
51
|
+
// Insert as the first item below the Reply button-container.
|
|
52
|
+
$('.topic-sidebar-tools [component="topic/reply/container"]').each(function () {
|
|
53
|
+
var $reply = $(this);
|
|
54
|
+
if ($reply.nextAll('[component="topic/search-in-topic"]').length === 0) {
|
|
55
|
+
$reply.after($button.clone(true));
|
|
56
|
+
injected = true;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// 2. Horizontal mobile/top toolbar (.topic-main-buttons, Harmony/Persona/Peace).
|
|
61
|
+
// Insert as first item in the left cluster.
|
|
62
|
+
$('.topic-main-buttons .d-flex.me-auto').each(function () {
|
|
63
|
+
var $cluster = $(this);
|
|
64
|
+
if ($cluster.children('[component="topic/search-in-topic"]').length === 0) {
|
|
65
|
+
$cluster.prepend($button.clone(true));
|
|
66
|
+
injected = true;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// 3. Fallback for Persona/Peace and other themes with topic/reply/container
|
|
71
|
+
// but without .d-flex.me-auto or .topic-sidebar-tools.
|
|
72
|
+
if (!injected) {
|
|
73
|
+
$('[component="topic/reply/container"]').each(function () {
|
|
74
|
+
var $reply = $(this);
|
|
75
|
+
if ($reply.nextAll('[component="topic/search-in-topic"]').length === 0) {
|
|
76
|
+
$reply.after($button.clone(true));
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
$(document).on('click', '[component="topic/search-in-topic"]', function () {
|
|
85
|
+
require(['search'], function (search) {
|
|
86
|
+
var form = null;
|
|
87
|
+
var $sidebar = $('[component="sidebar/right"]');
|
|
88
|
+
if ($sidebar.length && $sidebar.is(':visible')) {
|
|
89
|
+
form = $sidebar.find('[component="search/form"]');
|
|
90
|
+
}
|
|
91
|
+
if (!form || !form.length) {
|
|
92
|
+
var $bottombar = $('[component="bottombar"]:visible');
|
|
93
|
+
if ($bottombar.length) {
|
|
94
|
+
form = $bottombar.find('[component="search/form"]').first();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (!form || !form.length) {
|
|
98
|
+
form = $('[component="search/form"]').first();
|
|
99
|
+
}
|
|
100
|
+
if (!form || !form.length) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
var $input = form.find('[component="search/fields"] input[name="query"]');
|
|
104
|
+
var prefix = 'in:topic-' + ajaxify.data.tid + ' ';
|
|
105
|
+
if ($input.val().indexOf(prefix) !== 0) {
|
|
106
|
+
$input.val(prefix);
|
|
107
|
+
}
|
|
108
|
+
search.showAndFocusInput(form);
|
|
109
|
+
if (!$input.is(':focus')) {
|
|
110
|
+
$input.trigger('focus');
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}());
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<div class="card">
|
|
2
|
+
<div class="card-header">
|
|
3
|
+
<h2 class="card-title">{{tx("topic-search-button:admin.title")}}</h2>
|
|
4
|
+
</div>
|
|
5
|
+
<div class="card-body">
|
|
6
|
+
<form class="topic-search-button-settings">
|
|
7
|
+
<div class="mb-3">
|
|
8
|
+
<div class="form-check form-switch">
|
|
9
|
+
<input class="form-check-input" type="checkbox" role="switch" id="enabled" name="enabled" />
|
|
10
|
+
<label class="form-check-label" for="enabled">{{tx("topic-search-button:admin.enable")}}</label>
|
|
11
|
+
</div>
|
|
12
|
+
<p class="form-text text-xs">{{tx("topic-search-button:admin.enable-help")}}</p>
|
|
13
|
+
</div>
|
|
14
|
+
<button type="button" id="save" class="btn btn-primary btn-sm">{{tx("global:save")}}</button>
|
|
15
|
+
</form>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|