mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 15:30:13 +01:00
Added new function to add cumstom translation strings on Admin panel
This commit is contained in:
parent
4153fe7d0d
commit
b1525d4221
10 changed files with 599 additions and 2 deletions
|
|
@ -20,6 +20,10 @@ template(name="settingHeaderBar")
|
||||||
i.fa(class="fa-paperclip")
|
i.fa(class="fa-paperclip")
|
||||||
span {{_ 'attachments'}}
|
span {{_ 'attachments'}}
|
||||||
|
|
||||||
|
a.setting-header-btn.informations(href="{{pathFor 'translation'}}")
|
||||||
|
i.fa(class="fa-font")
|
||||||
|
span {{_ 'translation'}}
|
||||||
|
|
||||||
a.setting-header-btn.informations(href="{{pathFor 'information'}}")
|
a.setting-header-btn.informations(href="{{pathFor 'information'}}")
|
||||||
i.fa(class="fa-info-circle")
|
i.fa(class="fa-info-circle")
|
||||||
span {{_ 'info'}}
|
span {{_ 'info'}}
|
||||||
|
|
|
||||||
67
client/components/settings/translationBody.css
Normal file
67
client/components/settings/translationBody.css
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
.main-body {
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
table td,
|
||||||
|
table th {
|
||||||
|
border: 1px solid #d2d0d0;
|
||||||
|
text-align: left;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
table tr:nth-child(even) {
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
.ext-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
height: 34px;
|
||||||
|
}
|
||||||
|
.ext-box .ext-box-left {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.ext-box span {
|
||||||
|
vertical-align: center;
|
||||||
|
line-height: 34px;
|
||||||
|
}
|
||||||
|
.ext-box input,
|
||||||
|
.ext-box button {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.ext-box button {
|
||||||
|
min-width: 90px;
|
||||||
|
}
|
||||||
|
.content-wrapper {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.buttonsContainer {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.buttonsContainer input {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.buttonsContainer div {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
.more-settings-translation {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
#cancelBtn {
|
||||||
|
margin-left: 5% !important;
|
||||||
|
background: #ffa500;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
#deleteAction {
|
||||||
|
margin-left: 5% !important;
|
||||||
|
}
|
||||||
|
p.js-translation-language {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
p.js-translation-text {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
105
client/components/settings/translationBody.jade
Normal file
105
client/components/settings/translationBody.jade
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
template(name="translation")
|
||||||
|
.setting-content
|
||||||
|
unless currentUser.isAdmin
|
||||||
|
| {{_ 'error-notAuthorized'}}
|
||||||
|
else
|
||||||
|
.content-title.ext-box
|
||||||
|
.ext-box-left
|
||||||
|
if loading.get
|
||||||
|
+spinner
|
||||||
|
else if translationSetting.get
|
||||||
|
span
|
||||||
|
i.fa.fa-font
|
||||||
|
unless isMiniScreen
|
||||||
|
| {{_ 'translation'}}
|
||||||
|
input#searchTranslationInput(placeholder="{{_ 'search'}}")
|
||||||
|
button#searchTranslationButton
|
||||||
|
i.fa.fa-search
|
||||||
|
| {{_ 'search'}}
|
||||||
|
.ext-box-right
|
||||||
|
span {{#unless isMiniScreen}}{{_ 'translation-number'}}{{/unless}} #{translationNumber}
|
||||||
|
|
||||||
|
.content-body
|
||||||
|
.side-menu
|
||||||
|
ul
|
||||||
|
li.active
|
||||||
|
a.js-translation-menu(data-id="translation-setting")
|
||||||
|
i.fa.fa-font
|
||||||
|
| {{_ 'translation'}}
|
||||||
|
.main-body
|
||||||
|
if loading.get
|
||||||
|
+spinner
|
||||||
|
else if translationSetting.get
|
||||||
|
+translationGeneral
|
||||||
|
|
||||||
|
template(name="translationGeneral")
|
||||||
|
table
|
||||||
|
tbody
|
||||||
|
tr
|
||||||
|
th {{_ 'language'}}
|
||||||
|
th {{_ 'text'}}
|
||||||
|
th {{_ 'translation-text'}}
|
||||||
|
th
|
||||||
|
+newTranslationRow
|
||||||
|
each translation in translationList
|
||||||
|
+translationRow(translationId=translation._id)
|
||||||
|
|
||||||
|
template(name="newTranslationRow")
|
||||||
|
a.new-translation
|
||||||
|
i.fa.fa-plus-square
|
||||||
|
| {{_ 'new'}}
|
||||||
|
|
||||||
|
template(name="translationRow")
|
||||||
|
tr
|
||||||
|
td {{translationData.language}}
|
||||||
|
td {{translationData.text}}
|
||||||
|
td {{translationData.translationText}}
|
||||||
|
td
|
||||||
|
a.edit-translation
|
||||||
|
i.fa.fa-edit
|
||||||
|
| {{_ 'edit'}}
|
||||||
|
a.more-settings-translation
|
||||||
|
i.fa.fa-ellipsis-h
|
||||||
|
|
||||||
|
template(name="editTranslationPopup")
|
||||||
|
form
|
||||||
|
label
|
||||||
|
| {{_ 'language'}}
|
||||||
|
input.js-translation-language(type="text" value=translation.language required readonly)
|
||||||
|
label
|
||||||
|
| {{_ 'text'}}
|
||||||
|
input.js-translation-text(type="text" value=translation.text required readonly)
|
||||||
|
label
|
||||||
|
| {{_ 'translation-text'}}
|
||||||
|
input.js-translation-translation-text(type="text" value=translation.translationText)
|
||||||
|
hr
|
||||||
|
div.buttonsContainer
|
||||||
|
input.primary.wide(type="submit" value="{{_ 'save'}}")
|
||||||
|
|
||||||
|
template(name="newTranslationPopup")
|
||||||
|
form
|
||||||
|
label
|
||||||
|
| {{_ 'language'}}
|
||||||
|
input.js-translation-language(type="text" value="en" required)
|
||||||
|
label
|
||||||
|
| {{_ 'text'}}
|
||||||
|
span.error.hide.text-taken
|
||||||
|
| {{_ 'error-text-taken'}}
|
||||||
|
input.js-translation-text(type="text" value="" required)
|
||||||
|
label
|
||||||
|
| {{_ 'translation-text'}}
|
||||||
|
input.js-translation-translation-text(type="text" value="")
|
||||||
|
hr
|
||||||
|
div.buttonsContainer
|
||||||
|
input.primary.wide(type="submit" value="{{_ 'save'}}")
|
||||||
|
|
||||||
|
template(name="settingsTranslationPopup")
|
||||||
|
ul.pop-over-list
|
||||||
|
li
|
||||||
|
form
|
||||||
|
label
|
||||||
|
| {{_ 'delete-translation-confirm-popup'}}
|
||||||
|
br
|
||||||
|
label.hide.orgId(type="text" value=org._id)
|
||||||
|
div.buttonsContainer
|
||||||
|
input#deleteButton.card-details-red.right.wide(type="button" value="{{_ 'delete'}}")
|
||||||
214
client/components/settings/translationBody.js
Normal file
214
client/components/settings/translationBody.js
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
import { ReactiveCache } from '/imports/reactiveCache';
|
||||||
|
|
||||||
|
const translationsPerPage = 25;
|
||||||
|
|
||||||
|
BlazeComponent.extendComponent({
|
||||||
|
mixins() {
|
||||||
|
return [Mixins.InfiniteScrolling];
|
||||||
|
},
|
||||||
|
onCreated() {
|
||||||
|
this.error = new ReactiveVar('');
|
||||||
|
this.loading = new ReactiveVar(false);
|
||||||
|
this.translationSetting = new ReactiveVar(true);
|
||||||
|
this.findTranslationsOptions = new ReactiveVar({});
|
||||||
|
this.numberTranslations = new ReactiveVar(0);
|
||||||
|
|
||||||
|
this.page = new ReactiveVar(1);
|
||||||
|
this.loadNextPageLocked = false;
|
||||||
|
this.callFirstWith(null, 'resetNextPeak');
|
||||||
|
this.autorun(() => {
|
||||||
|
const limitTranslations = this.page.get() * translationsPerPage;
|
||||||
|
|
||||||
|
this.subscribe('translation', this.findTranslationsOptions.get(), 0, () => {
|
||||||
|
this.loadNextPageLocked = false;
|
||||||
|
const nextPeakBefore = this.callFirstWith(null, 'getNextPeak');
|
||||||
|
this.calculateNextPeak();
|
||||||
|
const nextPeakAfter = this.callFirstWith(null, 'getNextPeak');
|
||||||
|
if (nextPeakBefore === nextPeakAfter) {
|
||||||
|
this.callFirstWith(null, 'resetNextPeak');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
events() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'click #searchTranslationButton'() {
|
||||||
|
this.filterTranslation();
|
||||||
|
},
|
||||||
|
'keydown #searchTranslationInput'(event) {
|
||||||
|
if (event.keyCode === 13 && !event.shiftKey) {
|
||||||
|
this.filterTranslation();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'click #newTranslationButton'() {
|
||||||
|
Popup.open('newTranslation');
|
||||||
|
},
|
||||||
|
'click a.js-translation-menu': this.switchMenu,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
filterTranslation() {
|
||||||
|
const value = $('#searchTranslationInput').first().val();
|
||||||
|
if (value === '') {
|
||||||
|
this.findTranslationsOptions.set({});
|
||||||
|
} else {
|
||||||
|
const regex = new RegExp(value, 'i');
|
||||||
|
this.findTranslationsOptions.set({
|
||||||
|
$or: [
|
||||||
|
{ language: regex },
|
||||||
|
{ text: regex },
|
||||||
|
{ translationText: regex },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
loadNextPage() {
|
||||||
|
if (this.loadNextPageLocked === false) {
|
||||||
|
this.page.set(this.page.get() + 1);
|
||||||
|
this.loadNextPageLocked = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
calculateNextPeak() {
|
||||||
|
const element = this.find('.main-body');
|
||||||
|
if (element) {
|
||||||
|
const altitude = element.scrollHeight;
|
||||||
|
this.callFirstWith(this, 'setNextPeak', altitude);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reachNextPeak() {
|
||||||
|
this.loadNextPage();
|
||||||
|
},
|
||||||
|
setError(error) {
|
||||||
|
this.error.set(error);
|
||||||
|
},
|
||||||
|
setLoading(w) {
|
||||||
|
this.loading.set(w);
|
||||||
|
},
|
||||||
|
translationList() {
|
||||||
|
const translations = ReactiveCache.getTranslations(this.findTranslationsOptions.get(), {
|
||||||
|
sort: { modifiedAt: 1 },
|
||||||
|
fields: { _id: true },
|
||||||
|
});
|
||||||
|
this.numberTranslations.set(translations.length);
|
||||||
|
return translations;
|
||||||
|
},
|
||||||
|
translationNumber() {
|
||||||
|
return this.numberTranslations.get();
|
||||||
|
},
|
||||||
|
switchMenu(event) {
|
||||||
|
const target = $(event.target);
|
||||||
|
if (!target.hasClass('active')) {
|
||||||
|
$('.side-menu li.active').removeClass('active');
|
||||||
|
target.parent().addClass('active');
|
||||||
|
const targetID = target.data('id');
|
||||||
|
this.translationSetting.set('translation-setting' === targetID);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}).register('translation');
|
||||||
|
|
||||||
|
Template.translationRow.helpers({
|
||||||
|
translationData() {
|
||||||
|
return ReactiveCache.getTranslation(this.translationId);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.editTranslationPopup.helpers({
|
||||||
|
translation() {
|
||||||
|
return ReactiveCache.getTranslation(this.translationId);
|
||||||
|
},
|
||||||
|
errorMessage() {
|
||||||
|
return Template.instance().errorMessage.get();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.newTranslationPopup.onCreated(function () {
|
||||||
|
this.errorMessage = new ReactiveVar('');
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.newTranslationPopup.helpers({
|
||||||
|
translation() {
|
||||||
|
return ReactiveCache.getTranslation(this.translationId);
|
||||||
|
},
|
||||||
|
errorMessage() {
|
||||||
|
return Template.instance().errorMessage.get();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
BlazeComponent.extendComponent({
|
||||||
|
onCreated() {},
|
||||||
|
translation() {
|
||||||
|
return ReactiveCache.getTranslation(this.translationId);
|
||||||
|
},
|
||||||
|
events() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'click a.edit-translation': Popup.open('editTranslation'),
|
||||||
|
'click a.more-settings-translation': Popup.open('settingsTranslation'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
}).register('translationRow');
|
||||||
|
|
||||||
|
BlazeComponent.extendComponent({
|
||||||
|
events() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'click a.new-translation': Popup.open('newTranslation'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
}).register('newTranslationRow');
|
||||||
|
|
||||||
|
Template.editTranslationPopup.events({
|
||||||
|
submit(event, templateInstance) {
|
||||||
|
event.preventDefault();
|
||||||
|
const translation = ReactiveCache.getTranslation(this.translationId);
|
||||||
|
const translationText = templateInstance.find('.js-translation-translation-text').value.trim();
|
||||||
|
|
||||||
|
Meteor.call(
|
||||||
|
'setTranslationText',
|
||||||
|
translation,
|
||||||
|
translationText
|
||||||
|
);
|
||||||
|
|
||||||
|
Popup.back();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.newTranslationPopup.events({
|
||||||
|
submit(event, templateInstance) {
|
||||||
|
event.preventDefault();
|
||||||
|
const language = templateInstance.find('.js-translation-language').value.trim();
|
||||||
|
const text = templateInstance.find('.js-translation-text').value.trim();
|
||||||
|
const translationText = templateInstance.find('.js-translation-translation-text').value.trim();
|
||||||
|
|
||||||
|
Meteor.call(
|
||||||
|
'setCreateTranslation',
|
||||||
|
language,
|
||||||
|
text,
|
||||||
|
translationText,
|
||||||
|
function(error) {
|
||||||
|
const textMessageElement = templateInstance.$('.text-taken');
|
||||||
|
if (error) {
|
||||||
|
const errorElement = error.error;
|
||||||
|
if (errorElement === 'text-already-taken') {
|
||||||
|
textMessageElement.show();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
textMessageElement.hide();
|
||||||
|
Popup.back();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Popup.back();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.settingsTranslationPopup.events({
|
||||||
|
'click #deleteButton'(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
Translation.remove(this.translationId);
|
||||||
|
Popup.back();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -381,6 +381,30 @@ FlowRouter.route('/attachments', {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
FlowRouter.route('/translation', {
|
||||||
|
name: 'translation',
|
||||||
|
triggersEnter: [
|
||||||
|
AccountsTemplates.ensureSignedIn,
|
||||||
|
() => {
|
||||||
|
Session.set('currentBoard', null);
|
||||||
|
Session.set('currentList', null);
|
||||||
|
Session.set('currentCard', null);
|
||||||
|
Session.set('popupCardId', null);
|
||||||
|
Session.set('popupCardBoardId', null);
|
||||||
|
|
||||||
|
Filter.reset();
|
||||||
|
Session.set('sortBy', '');
|
||||||
|
EscapeActions.executeAll();
|
||||||
|
},
|
||||||
|
],
|
||||||
|
action() {
|
||||||
|
BlazeLayout.render('defaultLayout', {
|
||||||
|
headerBar: 'settingHeaderBar',
|
||||||
|
content: 'translation',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
FlowRouter.notFound = {
|
FlowRouter.notFound = {
|
||||||
action() {
|
action() {
|
||||||
BlazeLayout.render('defaultLayout', { content: 'notFound' });
|
BlazeLayout.render('defaultLayout', { content: 'notFound' });
|
||||||
|
|
|
||||||
|
|
@ -1229,5 +1229,10 @@
|
||||||
"allowed-avatar-filetypes": "Allowed avatar filetypes:",
|
"allowed-avatar-filetypes": "Allowed avatar filetypes:",
|
||||||
"invalid-file": "If filename is invalid, upload or rename is cancelled.",
|
"invalid-file": "If filename is invalid, upload or rename is cancelled.",
|
||||||
"preview-pdf-not-supported": "Your device does not support previewing PDF. Try downloading instead.",
|
"preview-pdf-not-supported": "Your device does not support previewing PDF. Try downloading instead.",
|
||||||
"drag-board": "Drag board"
|
"drag-board": "Drag board",
|
||||||
|
"translation-number": "The number of custom strings is: ",
|
||||||
|
"delete-translation-confirm-popup": "Are you sure you want to delete this custom string? There is no undo.",
|
||||||
|
"translation": "Translation",
|
||||||
|
"text": "Text",
|
||||||
|
"translation-text": "Translation text"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { Meteor } from 'meteor/meteor';
|
import { Meteor } from 'meteor/meteor';
|
||||||
import { ReactiveVar } from 'meteor/reactive-var';
|
import { ReactiveVar } from 'meteor/reactive-var';
|
||||||
|
import Translation from '/models/translation';
|
||||||
import i18next from 'i18next';
|
import i18next from 'i18next';
|
||||||
import sprintf from 'i18next-sprintf-postprocessor';
|
import sprintf from 'i18next-sprintf-postprocessor';
|
||||||
import languages from './languages';
|
import languages from './languages';
|
||||||
|
|
@ -45,7 +46,18 @@ export const TAPi18n = {
|
||||||
},
|
},
|
||||||
async loadLanguage(language) {
|
async loadLanguage(language) {
|
||||||
if (language in languages && 'load' in languages[language]) {
|
if (language in languages && 'load' in languages[language]) {
|
||||||
const data = await languages[language].load();
|
let data = await languages[language].load();
|
||||||
|
let custom_translations = [];
|
||||||
|
if (Meteor.isServer) {
|
||||||
|
custom_translations = Translation.find({language: language}, {fields: { text: true, translationText: true }}).fetch();
|
||||||
|
} else if (Meteor.isClient) {
|
||||||
|
await Meteor.subscribe('translation', {language: language}, 0);
|
||||||
|
custom_translations = ReactiveCache.getTranslations({language: language}, {fields: { text: true, translationText: true }});
|
||||||
|
}
|
||||||
|
if (custom_translations && custom_translations.length > 0) {
|
||||||
|
data = custom_translations.reduce((acc, cur) => (acc[cur.text]=cur.translationText, acc), data);
|
||||||
|
}
|
||||||
|
|
||||||
this.i18n.addResourceBundle(language, DEFAULT_NAMESPACE, data);
|
this.i18n.addResourceBundle(language, DEFAULT_NAMESPACE, data);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Language ${language} is not supported`);
|
throw new Error(`Language ${language} is not supported`);
|
||||||
|
|
|
||||||
|
|
@ -1261,6 +1261,17 @@ ReactiveCache = {
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
|
getTranslations(selector, options, getQuery) {
|
||||||
|
let ret = Translation.find(selector, options);
|
||||||
|
if (getQuery !== true) {
|
||||||
|
ret = ret.fetch();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
getTranslation(idOrFirstObjectSelector, options) {
|
||||||
|
const ret = Translation.findOne(idOrFirstObjectSelector, options);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client side little MiniMongo DB "Index"
|
// Client side little MiniMongo DB "Index"
|
||||||
|
|
|
||||||
128
models/translation.js
Normal file
128
models/translation.js
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
Translation = new Mongo.Collection('translation');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Organization User in wekan
|
||||||
|
*/
|
||||||
|
Translation.attachSchema(
|
||||||
|
new SimpleSchema({
|
||||||
|
language: {
|
||||||
|
/**
|
||||||
|
* the language
|
||||||
|
*/
|
||||||
|
type: String,
|
||||||
|
max: 5,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
/**
|
||||||
|
* the text
|
||||||
|
*/
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
translationText: {
|
||||||
|
/**
|
||||||
|
* the translation text
|
||||||
|
*/
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
/**
|
||||||
|
* creation date of the organization user
|
||||||
|
*/
|
||||||
|
type: Date,
|
||||||
|
// eslint-disable-next-line consistent-return
|
||||||
|
autoValue() {
|
||||||
|
if (this.isInsert) {
|
||||||
|
return new Date();
|
||||||
|
} else if (this.isUpsert) {
|
||||||
|
return { $setOnInsert: new Date() };
|
||||||
|
} else {
|
||||||
|
this.unset();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
modifiedAt: {
|
||||||
|
type: Date,
|
||||||
|
denyUpdate: false,
|
||||||
|
// eslint-disable-next-line consistent-return
|
||||||
|
autoValue() {
|
||||||
|
if (this.isInsert || this.isUpsert || this.isUpdate) {
|
||||||
|
return new Date();
|
||||||
|
} else {
|
||||||
|
this.unset();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Meteor.isServer) {
|
||||||
|
Translation.allow({
|
||||||
|
insert(userId, doc) {
|
||||||
|
const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
|
||||||
|
if (user?.isAdmin)
|
||||||
|
return true;
|
||||||
|
if (!user) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return doc._id === userId;
|
||||||
|
},
|
||||||
|
update(userId, doc) {
|
||||||
|
const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
|
||||||
|
if (user?.isAdmin)
|
||||||
|
return true;
|
||||||
|
if (!user) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return doc._id === userId;
|
||||||
|
},
|
||||||
|
remove(userId, doc) {
|
||||||
|
const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
|
||||||
|
if (user?.isAdmin)
|
||||||
|
return true;
|
||||||
|
if (!user) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return doc._id === userId;
|
||||||
|
},
|
||||||
|
fetch: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
Meteor.methods({
|
||||||
|
setCreateTranslation(
|
||||||
|
language,
|
||||||
|
text,
|
||||||
|
translationText,
|
||||||
|
) {
|
||||||
|
check(language, String);
|
||||||
|
check(text, String);
|
||||||
|
check(translationText, String);
|
||||||
|
|
||||||
|
const nTexts = ReactiveCache.getTranslations({ language, text }).length;
|
||||||
|
if (nTexts > 0) {
|
||||||
|
throw new Meteor.Error('text-already-taken');
|
||||||
|
} else {
|
||||||
|
Translation.insert({
|
||||||
|
language,
|
||||||
|
text,
|
||||||
|
translationText,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setTranslationText(translation, translationText) {
|
||||||
|
check(translation, Object);
|
||||||
|
check(translationText, String);
|
||||||
|
Translation.update(translation, {
|
||||||
|
$set: { translationText: translationText },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Meteor.isServer) {
|
||||||
|
// Index for Organization User.
|
||||||
|
Meteor.startup(() => {
|
||||||
|
Translation._collection.createIndex({ modifiedAt: -1 });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Translation;
|
||||||
27
server/publications/translation.js
Normal file
27
server/publications/translation.js
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { ReactiveCache } from '/imports/reactiveCache';
|
||||||
|
|
||||||
|
Meteor.publish('translation', function(query, limit) {
|
||||||
|
check(query, Match.OneOf(Object, null));
|
||||||
|
check(limit, Number);
|
||||||
|
|
||||||
|
let ret = [];
|
||||||
|
const user = ReactiveCache.getCurrentUser();
|
||||||
|
|
||||||
|
if (user && user.isAdmin) {
|
||||||
|
ret = ReactiveCache.getTranslations(query,
|
||||||
|
{
|
||||||
|
limit,
|
||||||
|
sort: { modifiedAt: -1 },
|
||||||
|
fields: {
|
||||||
|
language: 1,
|
||||||
|
text: 1,
|
||||||
|
translationText: 1,
|
||||||
|
createdAt: 1,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue