@jasonwch/nodebb-plugin-meilisearch-r 1.0.2 → 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 +13 -2
- package/library.js +437 -18
- package/package.json +5 -4
- package/plugin.json +27 -2
- package/static/languages/en-GB/meilisearch.json +22 -9
- package/static/languages/pl/meilisearch.json +21 -8
- package/static/languages/zh-CN/meilisearch.json +21 -8
- package/static/lib/admin.js +26 -3
- package/static/lib/chat-search-global.js +237 -0
- package/static/templates/admin/plugins/meilisearch/partials/rankingRules/form.tpl +1 -1
- package/static/templates/admin/plugins/meilisearch/partials/stopWords/form.tpl +1 -1
- package/static/templates/admin/plugins/meilisearch.tpl +175 -137
- package/static/templates/client/partials/chats/search-bar.tpl +9 -0
- package/static/templates/client/partials/chats/search-result.tpl +38 -0
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "nodebb-plugin-meilisearch-r",
|
|
3
3
|
"name": "NodeBB meilisearch refactored",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "Enhanced plugin for integrating MeiliSearch with NodeBB",
|
|
5
5
|
"url": "https://github.com/jasonwch/nodebb-plugin-meilisearch-r",
|
|
6
6
|
"library": "./library.js",
|
|
7
7
|
"hooks": [
|
|
@@ -89,6 +89,22 @@
|
|
|
89
89
|
"hook": "action:topics.scheduled.notify",
|
|
90
90
|
"method": "onScheduledPublish"
|
|
91
91
|
},
|
|
92
|
+
{
|
|
93
|
+
"hook": "action:messaging.save",
|
|
94
|
+
"method": "indexMessage"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"hook": "action:messaging.edit",
|
|
98
|
+
"method": "indexMessage"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"hook": "action:messaging.restore",
|
|
102
|
+
"method": "indexMessage"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"hook": "action:messaging.delete",
|
|
106
|
+
"method": "deindexMessage"
|
|
107
|
+
},
|
|
92
108
|
{
|
|
93
109
|
"hook": "filter:search.query",
|
|
94
110
|
"method": "search"
|
|
@@ -97,9 +113,17 @@
|
|
|
97
113
|
"hook": "filter:topic.search",
|
|
98
114
|
"method": "search"
|
|
99
115
|
},
|
|
116
|
+
{
|
|
117
|
+
"hook": "filter:messaging.searchMessages",
|
|
118
|
+
"method": "searchMessages"
|
|
119
|
+
},
|
|
100
120
|
{
|
|
101
121
|
"hook": "filter:settings.set",
|
|
102
122
|
"method": "saveSettings"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"hook": "filter:config.get",
|
|
126
|
+
"method": "addConfig"
|
|
103
127
|
}
|
|
104
128
|
],
|
|
105
129
|
"staticDirs": {
|
|
@@ -115,7 +139,8 @@
|
|
|
115
139
|
"../admin/plugins/meilisearch.js": "./static/lib/admin.js"
|
|
116
140
|
},
|
|
117
141
|
"scripts": [
|
|
118
|
-
"static/lib/client/quick-search.js"
|
|
142
|
+
"static/lib/client/quick-search.js",
|
|
143
|
+
"static/lib/chat-search-global.js"
|
|
119
144
|
],
|
|
120
145
|
"templates": "static/templates",
|
|
121
146
|
"settingsRoute": "/admin/plugins/meilisearch",
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
{
|
|
2
|
-
"admin.settings": "Meilisearch
|
|
2
|
+
"admin.settings": "Meilisearch",
|
|
3
3
|
"admin.connection": "Connection settings",
|
|
4
4
|
"admin.host": "Meilisearch URL",
|
|
5
5
|
"admin.apiKey": "API Key",
|
|
6
6
|
"admin.healthCheckInterval": "Health check interval (in seconds)",
|
|
7
7
|
"admin.search": "Search options",
|
|
8
|
-
"admin.maxDocuments": "Max number of
|
|
8
|
+
"admin.maxDocuments": "Max number of topics/posts returned",
|
|
9
|
+
"admin.globalChatSearchEnabled": "Global chat search",
|
|
10
|
+
"admin.globalChatSearchLimit": "Global chat search result fix limit at 200",
|
|
11
|
+
"admin.perRoomSearchLimit": "The limit above only applies to posts/topics. Per-chatroom search result fix limit at 100",
|
|
12
|
+
"chatSearch.room.self-chat": "Self chat",
|
|
13
|
+
"chatSearch.room.and-more-users": "%1 and %2 more users",
|
|
14
|
+
"chatSearch.client.placeholder": "Search messages...",
|
|
15
|
+
"chatSearch.client.searching": "Searching...",
|
|
16
|
+
"chatSearch.client.error": "Error",
|
|
17
|
+
"chatSearch.client.no-results": "No results found.",
|
|
18
|
+
"chatSearch.client.unknown-user": "Unknown",
|
|
9
19
|
"admin.searchMinTermLength": "Minimum search term length",
|
|
10
20
|
"admin.searchMinTermLengthHelp": "Queries in which every word is shorter than this (in characters) are skipped. Minimum 2. Dodges a NodeBB core highlight bug that surfaces on 1-character searches.",
|
|
11
21
|
"admin.rankingRules": "Ranking rules",
|
|
@@ -32,27 +42,30 @@
|
|
|
32
42
|
"admin.typoToleranceDisableOnWordsHelp": "Typo tolerance is disabled only when one of these words is typed exactly in the search query; that query word will not match typo variants in the index. Note: searching a misspelling of a disabled word will still match the correctly-spelled document via tolerance. For more information see:",
|
|
33
43
|
"admin.typoToleranceDisableOnWordsFormLabel": "Typo-tolerance disabled word",
|
|
34
44
|
"admin.addTypoToleranceDisabledWord": "Add a typo-tolerance disabled word",
|
|
35
|
-
"admin.
|
|
36
|
-
"admin.
|
|
45
|
+
"admin.indexResult": "Index result",
|
|
46
|
+
"admin.indexAction": "Index action",
|
|
37
47
|
"admin.reindex": "Reindex",
|
|
38
48
|
"admin.forceReindex": "Force reindex (delete all documents)",
|
|
39
49
|
"admin.reindexHelp": "Index all posts and topics again, in case of a mismatch between Meilisearch and NodeBB. This can take a while depending on the number of posts and topics in your forum.",
|
|
40
50
|
"admin.confirmReindex": "Are you sure you want to start reindexing all posts and topics?<br>This operation can take a while.",
|
|
41
51
|
"admin.reindexPostsProgress": "Reindexing posts...",
|
|
42
52
|
"admin.reindexTopicsProgress": "Reindexing topics...",
|
|
43
|
-
"admin.
|
|
53
|
+
"admin.reindexMessagesProgress": "Reindexing chat messages...",
|
|
44
54
|
"admin.edit": "edit",
|
|
45
55
|
"admin.delete": "delete",
|
|
46
56
|
"admin.lastReindex": "Last reindex",
|
|
47
|
-
"admin.
|
|
48
|
-
"admin.
|
|
49
|
-
"admin.lastReindexSuccess": "
|
|
57
|
+
"admin.lastReindexNever": "Never",
|
|
58
|
+
"admin.lastReindexDate": "Finished",
|
|
59
|
+
"admin.lastReindexSuccess": "Success",
|
|
50
60
|
"admin.lastReindexFailed": "Failed",
|
|
51
|
-
"admin.lastReindexStatus": "Status",
|
|
52
61
|
"admin.lastReindexTopics": "Topics",
|
|
53
62
|
"admin.lastReindexPosts": "Posts",
|
|
63
|
+
"admin.lastReindexMessages": "Chat messages",
|
|
54
64
|
"admin.lastReindexSkippedDeletedTopics": "Skipped (deleted topics)",
|
|
55
65
|
"admin.lastReindexSkippedDeletedPosts": "Skipped (deleted posts)",
|
|
66
|
+
"admin.lastReindexSkippedDeletedMessages": "Skipped (deleted messages)",
|
|
67
|
+
"admin.lastReindexSkippedSystemMessages": "Skipped (system messages)",
|
|
68
|
+
"admin.lastReindexSkippedOrphanMessages": "Skipped (orphaned — room deleted)",
|
|
56
69
|
"admin.lastReindexError": "Error",
|
|
57
70
|
"admin.reindexingCompleted": "Reindexing Completed",
|
|
58
71
|
"admin.reindexingCompletedBody": "Reindexing completed successfully",
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
{
|
|
2
|
-
"admin.settings": "
|
|
2
|
+
"admin.settings": "Meilisearch",
|
|
3
3
|
"admin.connection": "Ustawienia połączenia",
|
|
4
4
|
"admin.host": "Meilisearch URL",
|
|
5
5
|
"admin.apiKey": "Klucz API",
|
|
6
6
|
"admin.healthCheckInterval": "Odstęp między sprawdzeniami stanu MeiliSearch (w sekundach)",
|
|
7
7
|
"admin.search": "Ustawienia wyszukiwania",
|
|
8
|
-
"admin.maxDocuments": "Maksymalna liczba zwracanych
|
|
8
|
+
"admin.maxDocuments": "Maksymalna liczba zwracanych tematów/postów",
|
|
9
|
+
"admin.globalChatSearchEnabled": "Wyszukiwanie globalne czatu",
|
|
10
|
+
"admin.globalChatSearchLimit": "Maksymalna liczba wyników wyszukiwania globalnego czatu: 200",
|
|
11
|
+
"admin.perRoomSearchLimit": "Powyższy limit dotyczy tylko tematów/postów. Maksymalna liczba wyników wyszukiwania w pojedynczym pokoju czatu: 100",
|
|
12
|
+
"chatSearch.room.self-chat": "Czat sam ze sobą",
|
|
13
|
+
"chatSearch.room.and-more-users": "%1 i %2 więcej użytkowników",
|
|
14
|
+
"chatSearch.client.placeholder": "Szukaj wiadomości...",
|
|
15
|
+
"chatSearch.client.searching": "Szukanie...",
|
|
16
|
+
"chatSearch.client.error": "Błąd",
|
|
17
|
+
"chatSearch.client.no-results": "Nie znaleziono wyników.",
|
|
18
|
+
"chatSearch.client.unknown-user": "Nieznany",
|
|
9
19
|
"admin.searchMinTermLength": "Minimalna długość wyszukiwanego słowa",
|
|
10
20
|
"admin.searchMinTermLengthHelp": "Zapytania, w których każde słowo jest krótsze niż ta wartość (w znakach), są pomijane. Minimum 2. Omija błąd podświetlania w NodeBB ujawniający się przy wyszukiwaniu jednoliterowym.",
|
|
11
21
|
"admin.rankingRules": "Zasady sortowania",
|
|
@@ -32,27 +42,30 @@
|
|
|
32
42
|
"admin.typoToleranceDisableOnWordsHelp": "Meilisearch nie będzie tolerowało literówek dla tych wyrazów. By dowiedzieć się więcej zobacz dokumentację:",
|
|
33
43
|
"admin.typoToleranceDisableOnWordsFormLabel": "Słowo nie tolerujące literówek",
|
|
34
44
|
"admin.addTypoToleranceDisabledWord": "Dodaj słowo nie tolerujące literówek",
|
|
35
|
-
"admin.
|
|
36
|
-
"admin.
|
|
45
|
+
"admin.indexResult": "Wynik indeksowania",
|
|
46
|
+
"admin.indexAction": "Akcja indeksowania",
|
|
37
47
|
"admin.reindex": "Ponownie indeksuj",
|
|
38
48
|
"admin.forceReindex": "Wymuś indeksowanie (usuń wszystkie dokumenty)",
|
|
39
49
|
"admin.reindexHelp": "Ponownie indeksuj wszystkie posty i tematy, w wypadku gdyby dane w Meilisearch nie zgadzały się z NodeBB. Ten proces może zająć dłuższy czas w zależności od ilości postów i tematów na forum.",
|
|
40
50
|
"admin.confirmReindex": "Czy jesteś pewien, że chcesz ponownie indeksować wszystkie posty i tematy?<br>To może zająć trochę czasu",
|
|
41
51
|
"admin.reindexPostsProgress": "Ponowne indeksowanie postów...",
|
|
42
52
|
"admin.reindexTopicsProgress": "Ponowne indeksowanie tematów...",
|
|
43
|
-
"admin.
|
|
53
|
+
"admin.reindexMessagesProgress": "Ponowne indeksowanie wiadomości czatu...",
|
|
44
54
|
"admin.edit": "edytuj",
|
|
45
55
|
"admin.delete": "usuń",
|
|
46
56
|
"admin.lastReindex": "Ostatnie indeksowanie",
|
|
57
|
+
"admin.lastReindexNever": "Nigdy",
|
|
47
58
|
"admin.lastReindexDate": "zakończone",
|
|
48
|
-
"admin.
|
|
49
|
-
"admin.lastReindexSuccess": "Ukończono pomyślnie",
|
|
59
|
+
"admin.lastReindexSuccess": "Sukces",
|
|
50
60
|
"admin.lastReindexFailed": "Niepowodzenie",
|
|
51
|
-
"admin.lastReindexStatus": "Status",
|
|
52
61
|
"admin.lastReindexTopics": "Tematy",
|
|
53
62
|
"admin.lastReindexPosts": "Posty",
|
|
63
|
+
"admin.lastReindexMessages": "Wiadomości czatu",
|
|
54
64
|
"admin.lastReindexSkippedDeletedTopics": "Pominięto (usunięte tematy)",
|
|
55
65
|
"admin.lastReindexSkippedDeletedPosts": "Pominięto (usunięte posty)",
|
|
66
|
+
"admin.lastReindexSkippedDeletedMessages": "Pominięto (usunięte wiadomości)",
|
|
67
|
+
"admin.lastReindexSkippedSystemMessages": "Pominięto (wiadomości systemowe)",
|
|
68
|
+
"admin.lastReindexSkippedOrphanMessages": "Pominięto (osierocone — pokój usunięty)",
|
|
56
69
|
"admin.lastReindexError": "Błąd",
|
|
57
70
|
"admin.reindexingCompleted": "Indeksowanie ukończone",
|
|
58
71
|
"admin.reindexingCompletedBody": "Indeksowanie zakończone pomyślnie",
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
{
|
|
2
|
-
"admin.settings": "Meilisearch
|
|
2
|
+
"admin.settings": "Meilisearch",
|
|
3
3
|
"admin.connection": "连接设置",
|
|
4
4
|
"admin.host": "Meilisearch URL",
|
|
5
5
|
"admin.apiKey": "API Key",
|
|
6
6
|
"admin.healthCheckInterval": "运行状态检查间隔(秒)",
|
|
7
7
|
"admin.search": "搜索选项",
|
|
8
|
-
"admin.maxDocuments": "
|
|
8
|
+
"admin.maxDocuments": "返回主题/帖子的最大数量",
|
|
9
|
+
"admin.globalChatSearchEnabled": "全局聊天搜索",
|
|
10
|
+
"admin.globalChatSearchLimit": "全局聊天搜索结果固定上限为 200",
|
|
11
|
+
"admin.perRoomSearchLimit": "上述限制仅适用于主题/帖子。单聊天室搜索结果固定上限为 100",
|
|
12
|
+
"chatSearch.room.self-chat": "自我聊天",
|
|
13
|
+
"chatSearch.room.and-more-users": "%1 及其他 %2 位用户",
|
|
14
|
+
"chatSearch.client.placeholder": "搜索消息...",
|
|
15
|
+
"chatSearch.client.searching": "搜索中...",
|
|
16
|
+
"chatSearch.client.error": "错误",
|
|
17
|
+
"chatSearch.client.no-results": "未找到结果。",
|
|
18
|
+
"chatSearch.client.unknown-user": "未知",
|
|
9
19
|
"admin.searchMinTermLength": "最短搜索词长度",
|
|
10
20
|
"admin.searchMinTermLengthHelp": "若查询中每个词的字符数都小于该值,则跳过搜索。最小为 2。用于规避 NodeBB 在单字符搜索时的高亮显示 bug。",
|
|
11
21
|
"admin.rankingRules": "排序规则",
|
|
@@ -32,27 +42,30 @@
|
|
|
32
42
|
"admin.typoToleranceDisableOnWordsHelp": "仅当搜索查询中精确输入了这些单词之一时,拼写容错才会被禁用;该查询词将不会匹配索引中的拼写变体。注意:搜索禁用词的拼写错误版本仍会通过容错机制匹配正确拼写的文档。如需了解更多信息,请参阅:",
|
|
33
43
|
"admin.typoToleranceDisableOnWordsFormLabel": "禁用拼写容错的单词",
|
|
34
44
|
"admin.addTypoToleranceDisabledWord": "添加禁用拼写容错的单词",
|
|
35
|
-
"admin.
|
|
36
|
-
"admin.
|
|
45
|
+
"admin.indexResult": "索引结果",
|
|
46
|
+
"admin.indexAction": "索引操作",
|
|
37
47
|
"admin.reindex": "重新索引",
|
|
38
48
|
"admin.forceReindex": "强制重新索引(删除所有文档)",
|
|
39
49
|
"admin.reindexHelp": "在 Meilisearch 和 NodeBB 不匹配的情况下,重新索引所有帖子和主题。这可能需要一段时间,具体取决于您论坛中帖子和主题的数量。",
|
|
40
50
|
"admin.confirmReindex": "您确定要开始重新索引所有帖子和主题吗?<br>这项操作可能需要一段时间。",
|
|
41
51
|
"admin.reindexPostsProgress": "重新索引帖子中...",
|
|
42
52
|
"admin.reindexTopicsProgress": "重新索引主题中...",
|
|
43
|
-
"admin.
|
|
53
|
+
"admin.reindexMessagesProgress": "重新索引聊天消息中...",
|
|
44
54
|
"admin.edit": "编辑",
|
|
45
55
|
"admin.delete": "删除",
|
|
46
56
|
"admin.lastReindex": "最近一次重新索引",
|
|
57
|
+
"admin.lastReindexNever": "从未",
|
|
47
58
|
"admin.lastReindexDate": "完成时间",
|
|
48
|
-
"admin.
|
|
49
|
-
"admin.lastReindexSuccess": "已成功完成",
|
|
59
|
+
"admin.lastReindexSuccess": "成功",
|
|
50
60
|
"admin.lastReindexFailed": "失败",
|
|
51
|
-
"admin.lastReindexStatus": "状态",
|
|
52
61
|
"admin.lastReindexTopics": "主题",
|
|
53
62
|
"admin.lastReindexPosts": "帖子",
|
|
63
|
+
"admin.lastReindexMessages": "聊天消息",
|
|
54
64
|
"admin.lastReindexSkippedDeletedTopics": "已跳过(已删除的主题)",
|
|
55
65
|
"admin.lastReindexSkippedDeletedPosts": "已跳过(已删除的帖子)",
|
|
66
|
+
"admin.lastReindexSkippedDeletedMessages": "已跳过(已删除的消息)",
|
|
67
|
+
"admin.lastReindexSkippedSystemMessages": "已跳过(系统消息)",
|
|
68
|
+
"admin.lastReindexSkippedOrphanMessages": "已跳过(孤立的 — 房间已删除)",
|
|
56
69
|
"admin.lastReindexError": "错误",
|
|
57
70
|
"admin.reindexingCompleted": "重新索引完成",
|
|
58
71
|
"admin.reindexingCompletedBody": "重新索引已成功完成",
|
package/static/lib/admin.js
CHANGED
|
@@ -12,14 +12,18 @@ define('admin/plugins/meilisearch', [
|
|
|
12
12
|
'modals',
|
|
13
13
|
], (settings, api, alerts, modals) => {
|
|
14
14
|
const ACP = {};
|
|
15
|
+
let reindexCompleteShown = false;
|
|
15
16
|
|
|
16
17
|
function onReindex(data) {
|
|
17
|
-
if (!data || !data.topic_progress || !data.post_progress) {
|
|
18
|
+
if (!data || !data.topic_progress || !data.post_progress || !data.message_progress) {
|
|
18
19
|
return;
|
|
19
20
|
}
|
|
21
|
+
if (data.running) {
|
|
22
|
+
reindexCompleteShown = false;
|
|
23
|
+
}
|
|
20
24
|
const progressBarContainers = document.querySelectorAll('.reindex-progress-container');
|
|
21
25
|
const hasProgress = data.running ||
|
|
22
|
-
Number(data.topic_progress.total) > 0 || Number(data.post_progress.total) > 0;
|
|
26
|
+
Number(data.topic_progress.total) > 0 || Number(data.post_progress.total) > 0 || Number(data.message_progress.total) > 0;
|
|
23
27
|
if (data.running || hasProgress) {
|
|
24
28
|
progressBarContainers.forEach((container) => {
|
|
25
29
|
container.classList.remove('hidden');
|
|
@@ -28,11 +32,15 @@ define('admin/plugins/meilisearch', [
|
|
|
28
32
|
setProgress(topicProgressBar, data.topic_progress.current, data.topic_progress.total);
|
|
29
33
|
const postProgressBar = document.getElementById('post-reindex-progress');
|
|
30
34
|
setProgress(postProgressBar, data.post_progress.current, data.post_progress.total);
|
|
35
|
+
const messageProgressBar = document.getElementById('message-reindex-progress');
|
|
36
|
+
setProgress(messageProgressBar, data.message_progress.current, data.message_progress.total);
|
|
31
37
|
|
|
32
38
|
document.getElementById('topic-reindex-progress-text').innerText =
|
|
33
39
|
`${Number(data.topic_progress.current) || 0}/${Number(data.topic_progress.total) || 0}`;
|
|
34
40
|
document.getElementById('post-reindex-progress-text').innerText =
|
|
35
41
|
`${Number(data.post_progress.current) || 0}/${Number(data.post_progress.total) || 0}`;
|
|
42
|
+
document.getElementById('message-reindex-progress-text').innerText =
|
|
43
|
+
`${Number(data.message_progress.current) || 0}/${Number(data.message_progress.total) || 0}`;
|
|
36
44
|
} else {
|
|
37
45
|
progressBarContainers.forEach((container) => {
|
|
38
46
|
container.classList.add('hidden');
|
|
@@ -40,8 +48,11 @@ define('admin/plugins/meilisearch', [
|
|
|
40
48
|
}
|
|
41
49
|
if (
|
|
42
50
|
!data.running && data.topic_progress.total === data.topic_progress.current &&
|
|
43
|
-
data.post_progress.total === data.post_progress.current
|
|
51
|
+
data.post_progress.total === data.post_progress.current &&
|
|
52
|
+
data.message_progress.total === data.message_progress.current &&
|
|
53
|
+
!reindexCompleteShown
|
|
44
54
|
) {
|
|
55
|
+
reindexCompleteShown = true;
|
|
45
56
|
alerts.alert({
|
|
46
57
|
title: '[[meilisearch:admin.reindexingCompleted]]',
|
|
47
58
|
message: '[[meilisearch:admin.reindexingCompletedBody]]',
|
|
@@ -59,14 +70,20 @@ define('admin/plugins/meilisearch', [
|
|
|
59
70
|
ACP.init = function () {
|
|
60
71
|
app.enterRoom('admin/plugins/meilisearch');
|
|
61
72
|
settings.load('meilisearch', $('.meilisearch-settings'), () => {
|
|
73
|
+
toggleLimitNotice();
|
|
62
74
|
});
|
|
63
75
|
$('#save').on('click', saveSettings);
|
|
64
76
|
$('#reindex').on('click', reindex);
|
|
65
77
|
formatLocalTimes();
|
|
78
|
+
$('#globalChatSearchEnabled').on('change', toggleLimitNotice);
|
|
66
79
|
socket.removeListener('plugins.meilisearch.reindex', onReindex);
|
|
67
80
|
socket.on('plugins.meilisearch.reindex', onReindex);
|
|
68
81
|
};
|
|
69
82
|
|
|
83
|
+
function toggleLimitNotice() {
|
|
84
|
+
$('#globalChatSearchLimitNotice').toggle($('#globalChatSearchEnabled').is(':checked'));
|
|
85
|
+
}
|
|
86
|
+
|
|
70
87
|
function setProgress(element, current, total) {
|
|
71
88
|
current = Number(current) || 0;
|
|
72
89
|
total = Number(total) || 0;
|
|
@@ -88,6 +105,11 @@ define('admin/plugins/meilisearch', [
|
|
|
88
105
|
|
|
89
106
|
function saveSettings() {
|
|
90
107
|
settings.save('meilisearch', $('.meilisearch-settings'), () => {
|
|
108
|
+
const saveBtn = $('#save').get(0);
|
|
109
|
+
if (saveBtn) {
|
|
110
|
+
saveBtn.classList.toggle('saved', true);
|
|
111
|
+
setTimeout(() => { saveBtn.classList.toggle('saved', false); }, 1500);
|
|
112
|
+
}
|
|
91
113
|
alerts.alert({
|
|
92
114
|
type: 'success',
|
|
93
115
|
alert_id: 'meilisearch-saved',
|
|
@@ -118,6 +140,7 @@ define('admin/plugins/meilisearch', [
|
|
|
118
140
|
? api.post('/plugins/meilisearch/reindex')
|
|
119
141
|
: api.del('/plugins/meilisearch/reindex');
|
|
120
142
|
request.then(() => {
|
|
143
|
+
document.getElementById('index-action').scrollIntoView({ behavior: 'smooth' });
|
|
121
144
|
alerts.alert({
|
|
122
145
|
type: 'info',
|
|
123
146
|
alert_id: 'meilisearch-reindex-started',
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
window.chatSearchState = window.chatSearchState || {
|
|
4
|
+
query: '',
|
|
5
|
+
resultsHtml: '',
|
|
6
|
+
isOpen: false,
|
|
7
|
+
lastScroll: 0,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
$(document).ready(function () {
|
|
11
|
+
let observer = null;
|
|
12
|
+
let debounceTimer = null;
|
|
13
|
+
let searchSeq = 0;
|
|
14
|
+
const SEARCH_DEBOUNCE_MS = 350;
|
|
15
|
+
|
|
16
|
+
// Coexistence with nodebb-plugin-chat-search:
|
|
17
|
+
// Both plugins share the same #global-chat-search-container ID and window.chatSearchState
|
|
18
|
+
// global. When both are active, chat-search's script runs first (bundled earlier) and
|
|
19
|
+
// injects its container with hardcoded colors. Our action:ajaxify.end handler fires second,
|
|
20
|
+
// detects the foreign container (lacks data-plugin="meilisearch"), removes it, and injects
|
|
21
|
+
// our own with theme-aware CSS variables + our socket handler bindings. Chat-search's
|
|
22
|
+
// event handlers are left on removed DOM elements (dead references, no interference).
|
|
23
|
+
// The window.chatSearchState object is reset identically by both scripts on non-chat
|
|
24
|
+
// navigation, so no mutation conflicts occur.
|
|
25
|
+
|
|
26
|
+
$(window).on('action:ajaxify.end', function (ev, data) {
|
|
27
|
+
if (observer) observer.disconnect();
|
|
28
|
+
const isChatUrl = data.url && data.url.match(/^(user\/[^/]+\/)?chats/);
|
|
29
|
+
const isChatTemplate = data.template && (data.template.name === 'chats' || data.template === 'chats');
|
|
30
|
+
|
|
31
|
+
if ((isChatUrl || isChatTemplate) && config.globalChatSearchEnabled !== false) {
|
|
32
|
+
initSearchInjection();
|
|
33
|
+
} else {
|
|
34
|
+
window.chatSearchState = { query: '', resultsHtml: '', isOpen: false, lastScroll: 0 };
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
$(window).on('action:chat.loaded', function () {
|
|
39
|
+
highlightActiveChat();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (ajaxify.data.template && (ajaxify.data.template.name === 'chats' || ajaxify.data.template === 'chats') && config.globalChatSearchEnabled !== false) {
|
|
43
|
+
initSearchInjection();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function initSearchInjection() {
|
|
47
|
+
const container = findContainer();
|
|
48
|
+
if (container.length > 0) {
|
|
49
|
+
injectSearchBar(container);
|
|
50
|
+
} else {
|
|
51
|
+
const targetNode = document.body;
|
|
52
|
+
const config = { childList: true, subtree: true };
|
|
53
|
+
observer = new MutationObserver(function () {
|
|
54
|
+
const c = findContainer();
|
|
55
|
+
if (c.length > 0) {
|
|
56
|
+
injectSearchBar(c);
|
|
57
|
+
observer.disconnect();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
observer.observe(targetNode, config);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function findContainer() {
|
|
65
|
+
let container = $('[component="chat/nav-wrapper"]');
|
|
66
|
+
if (container.length === 0) container = $('.chats-page').find('.col-md-4').first();
|
|
67
|
+
if (container.length === 0) container = $('[component="chat/list"]').parent();
|
|
68
|
+
return container;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function injectSearchBar(containerElement) {
|
|
72
|
+
const container = containerElement || findContainer();
|
|
73
|
+
if (container.length === 0) return false;
|
|
74
|
+
if ($('#global-chat-search-container[data-plugin="meilisearch"]').length > 0) {
|
|
75
|
+
// Already ours, just re-bind events (e.g. after ajaxify navigation).
|
|
76
|
+
attachEvents();
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Render the template FIRST (async), then atomically remove + prepend.
|
|
81
|
+
// This prevents chat-search's MutationObserver from re-injecting its
|
|
82
|
+
// container during the await gap (which caused duplicate search boxes).
|
|
83
|
+
const html = await app.parseAndTranslate('client/partials/chats/search-bar', {});
|
|
84
|
+
$('#global-chat-search-container').remove();
|
|
85
|
+
container.prepend(html);
|
|
86
|
+
restoreState();
|
|
87
|
+
attachEvents();
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// injectSearchBar is async (uses app.parseAndTranslate). Callers don't await it —
|
|
92
|
+
// that's fine because attachEvents binds by ID and restoreState reads from
|
|
93
|
+
// window.chatSearchState, both available once prepended.
|
|
94
|
+
|
|
95
|
+
function attachEvents() {
|
|
96
|
+
$('#btn-chat-search').off('click').on('click', function () {
|
|
97
|
+
clearTimeout(debounceTimer);
|
|
98
|
+
executeSearch();
|
|
99
|
+
});
|
|
100
|
+
const input = $('#global-chat-search');
|
|
101
|
+
input.off('keypress').on('keypress', function (e) {
|
|
102
|
+
if (e.which === 13) {
|
|
103
|
+
clearTimeout(debounceTimer);
|
|
104
|
+
executeSearch();
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
input.off('input').on('input', function () {
|
|
108
|
+
window.chatSearchState.query = $(this).val();
|
|
109
|
+
clearTimeout(debounceTimer);
|
|
110
|
+
debounceTimer = setTimeout(executeSearch, SEARCH_DEBOUNCE_MS);
|
|
111
|
+
});
|
|
112
|
+
$('#global-search-results').off('scroll').on('scroll', function () {
|
|
113
|
+
window.chatSearchState.lastScroll = $(this).scrollTop();
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function restoreState() {
|
|
118
|
+
const input = $('#global-chat-search');
|
|
119
|
+
const results = $('#global-search-results');
|
|
120
|
+
if (window.chatSearchState.query) input.val(window.chatSearchState.query);
|
|
121
|
+
if (window.chatSearchState.isOpen && window.chatSearchState.resultsHtml) {
|
|
122
|
+
results.html(window.chatSearchState.resultsHtml).show();
|
|
123
|
+
if ($.fn.timeago) results.find('.timeago').timeago();
|
|
124
|
+
if (window.chatSearchState.lastScroll > 0) results.scrollTop(window.chatSearchState.lastScroll);
|
|
125
|
+
highlightActiveChat();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function cleanContent(content) {
|
|
130
|
+
if (!content) return '';
|
|
131
|
+
let text;
|
|
132
|
+
try {
|
|
133
|
+
const doc = new DOMParser().parseFromString(content, 'text/html');
|
|
134
|
+
text = (doc.body && doc.body.textContent) || '';
|
|
135
|
+
} catch (e) {
|
|
136
|
+
text = content.replace(/<[^>]*>/g, ' ');
|
|
137
|
+
}
|
|
138
|
+
return text.replace(/\s+/g, ' ').trim();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function translateHtml(html) {
|
|
142
|
+
return new Promise(function (resolve) {
|
|
143
|
+
require(['translator'], function (translator) {
|
|
144
|
+
translator.translate(html, function (translated) {
|
|
145
|
+
resolve(translator.unescape(translated));
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function executeSearch() {
|
|
152
|
+
const query = $('#global-chat-search').val();
|
|
153
|
+
const resultsContainer = $('#global-search-results');
|
|
154
|
+
|
|
155
|
+
if (!query) {
|
|
156
|
+
resultsContainer.hide();
|
|
157
|
+
window.chatSearchState.isOpen = false;
|
|
158
|
+
window.chatSearchState.resultsHtml = '';
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const spinnerHtml = await translateHtml('<div class="text-center" style="padding:10px;"><i class="fa fa-spinner fa-spin"></i> [[meilisearch:chatSearch.client.searching]]</div>');
|
|
163
|
+
resultsContainer.show().html(spinnerHtml);
|
|
164
|
+
window.chatSearchState.isOpen = true;
|
|
165
|
+
|
|
166
|
+
const payload = { query: query };
|
|
167
|
+
|
|
168
|
+
const seq = ++searchSeq;
|
|
169
|
+
socket.emit('plugins.meilisearch.chatSearchGlobal', payload, async function (err, messages) {
|
|
170
|
+
if (seq !== searchSeq) return;
|
|
171
|
+
if (err) {
|
|
172
|
+
const errorHtml = await translateHtml('<div class="alert alert-danger" style="margin:5px;">[[meilisearch:chatSearch.client.error]]</div>');
|
|
173
|
+
resultsContainer.html(errorHtml);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (!messages || messages.length === 0) {
|
|
177
|
+
const noResHtml = await translateHtml('<div class="text-center" style="padding:10px; color: var(--bs-secondary-color);">[[meilisearch:chatSearch.client.no-results]]</div>');
|
|
178
|
+
resultsContainer.html(noResHtml);
|
|
179
|
+
window.chatSearchState.resultsHtml = noResHtml;
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Pre-process messages for the template
|
|
184
|
+
const preparedMessages = messages.map(function (msg) {
|
|
185
|
+
var ts = parseInt(msg.timestamp, 10);
|
|
186
|
+
var isoTime = '';
|
|
187
|
+
if (ts) {
|
|
188
|
+
try { isoTime = new Date(ts).toISOString(); } catch (e) { isoTime = ''; }
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
var mid = parseInt(msg.mid, 10);
|
|
192
|
+
var roomId = parseInt(msg.roomId, 10);
|
|
193
|
+
var rank = parseInt(msg.rank, 10);
|
|
194
|
+
var hasRank = rank >= 0 && app.user && app.user.userslug;
|
|
195
|
+
var chatLink = hasRank
|
|
196
|
+
? (config.relative_path || '') + '/user/' + app.user.userslug + '/chats/' + roomId + '/' + (rank + 1)
|
|
197
|
+
: (config.relative_path || '') + '/message/' + mid;
|
|
198
|
+
var senderName = (msg.user && msg.user.username) ? msg.user.username : '[[meilisearch:chatSearch.client.unknown-user]]';
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
mid: mid,
|
|
202
|
+
roomId: roomId,
|
|
203
|
+
chatLink: chatLink,
|
|
204
|
+
roomName: msg.roomName || '',
|
|
205
|
+
mainParticipant: (msg.participants && msg.participants[0]) || null,
|
|
206
|
+
user: msg.user,
|
|
207
|
+
senderName: senderName,
|
|
208
|
+
cleanedContent: cleanContent(msg.content),
|
|
209
|
+
isoTime: isoTime,
|
|
210
|
+
};
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const html = await app.parseAndTranslate('client/partials/chats/search-result', { messages: preparedMessages });
|
|
214
|
+
resultsContainer.html(html);
|
|
215
|
+
|
|
216
|
+
if ($.fn.timeago) {
|
|
217
|
+
resultsContainer.find('.timeago').timeago();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
window.chatSearchState.resultsHtml = resultsContainer[0].innerHTML;
|
|
221
|
+
window.chatSearchState.lastScroll = 0;
|
|
222
|
+
highlightActiveChat();
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function highlightActiveChat() {
|
|
227
|
+
let currentRoomId = ajaxify.data.roomId;
|
|
228
|
+
if (!currentRoomId) {
|
|
229
|
+
const match = window.location.pathname.match(/chats\/(\d+)/);
|
|
230
|
+
if (match) currentRoomId = match[1];
|
|
231
|
+
}
|
|
232
|
+
if (!currentRoomId) return;
|
|
233
|
+
$('.search-result').removeClass('active');
|
|
234
|
+
const activeItem = $('.search-result[data-roomid="' + currentRoomId + '"]');
|
|
235
|
+
activeItem.addClass('active');
|
|
236
|
+
}
|
|
237
|
+
});
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
<div class="form-group">
|
|
3
3
|
<label for="rule">{{tx("meilisearch:admin.rankingRulesFormLabel")}}</label>
|
|
4
4
|
<input type="text" id="rule" name="rule" class="form-control" placeholder="eg. cid:asc" />
|
|
5
|
-
<p class="help-block">{{tx("meilisearch:admin.rankingRulesFormHelp")}} <a href="https://docs.meilisearch.com/learn/core_concepts/relevancy.html#custom-rules">https://docs.meilisearch.com/learn/core_concepts/relevancy.html#custom-rules</a></p>
|
|
5
|
+
<p class="help-block">{{tx("meilisearch:admin.rankingRulesFormHelp")}} <a href="https://docs.meilisearch.com/learn/core_concepts/relevancy.html#custom-rules" style="word-break: break-all;">https://docs.meilisearch.com/learn/core_concepts/relevancy.html#custom-rules</a></p>
|
|
6
6
|
</div>
|
|
7
7
|
</form>
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
<div class="form-group">
|
|
3
3
|
<label for="word">{{tx("meilisearch:admin.stopWordsFormLabel")}}</label>
|
|
4
4
|
<input type="text" id="word" name="word" class="form-control" placeholder="the" />
|
|
5
|
-
<p class="help-block">{{tx("meilisearch:admin.stopWordsFormHelp")}} <a href="https://docs.meilisearch.com/reference/api/settings.html#stop-words">https://docs.meilisearch.com/reference/api/settings.html#stop-words</a></p>
|
|
5
|
+
<p class="help-block">{{tx("meilisearch:admin.stopWordsFormHelp")}} <a href="https://docs.meilisearch.com/reference/api/settings.html#stop-words" style="word-break: break-all;">https://docs.meilisearch.com/reference/api/settings.html#stop-words</a></p>
|
|
6
6
|
</div>
|
|
7
7
|
</form>
|