merge master changes

This commit is contained in:
viehlieb 2022-07-08 11:55:32 +02:00
commit 5df5c7f5d7
401 changed files with 142692 additions and 15007 deletions

View file

@ -51,7 +51,7 @@ AccountSettings.allow({
if (Meteor.isServer) {
Meteor.startup(() => {
AccountSettings._collection._ensureIndex({ modifiedAt: -1 });
AccountSettings._collection.createIndex({ modifiedAt: -1 });
AccountSettings.upsert(
{ _id: 'accounts-allowEmailChange' },
{

View file

@ -32,7 +32,7 @@ Actions.helpers({
if (Meteor.isServer) {
Meteor.startup(() => {
Actions._collection._ensureIndex({ modifiedAt: -1 });
Actions._collection.createIndex({ modifiedAt: -1 });
});
}

View file

@ -82,19 +82,19 @@ if (Meteor.isServer) {
// creation in conjunction with the card or board id, as corresponding views
// are largely used in the App. See #524.
Meteor.startup(() => {
Activities._collection._ensureIndex({ createdAt: -1 });
Activities._collection._ensureIndex({ modifiedAt: -1 });
Activities._collection._ensureIndex({ cardId: 1, createdAt: -1 });
Activities._collection._ensureIndex({ boardId: 1, createdAt: -1 });
Activities._collection._ensureIndex(
Activities._collection.createIndex({ createdAt: -1 });
Activities._collection.createIndex({ modifiedAt: -1 });
Activities._collection.createIndex({ cardId: 1, createdAt: -1 });
Activities._collection.createIndex({ boardId: 1, createdAt: -1 });
Activities._collection.createIndex(
{ commentId: 1 },
{ partialFilterExpression: { commentId: { $exists: true } } },
);
Activities._collection._ensureIndex(
Activities._collection.createIndex(
{ attachmentId: 1 },
{ partialFilterExpression: { attachmentId: { $exists: true } } },
);
Activities._collection._ensureIndex(
Activities._collection.createIndex(
{ customFieldId: 1 },
{ partialFilterExpression: { customFieldId: { $exists: true } } },
);

View file

@ -56,7 +56,7 @@ Announcements.allow({
if (Meteor.isServer) {
Meteor.startup(() => {
Announcements._collection._ensureIndex({ modifiedAt: -1 });
Announcements._collection.createIndex({ modifiedAt: -1 });
const announcements = Announcements.findOne({});
if (!announcements) {
Announcements.insert({ enabled: false, sort: 0 });

View file

@ -4,7 +4,7 @@ import { createBucket } from './lib/grid/createBucket';
import fs from 'fs';
import path from 'path';
import { AttachmentStoreStrategyFilesystem, AttachmentStoreStrategyGridFs} from '/models/lib/attachmentStoreStrategy';
import FileStoreStrategyFactory, {moveToStorage, STORAGE_NAME_FILESYSTEM, STORAGE_NAME_GRIDFS} from '/models/lib/fileStoreStrategy';
import FileStoreStrategyFactory, {moveToStorage, rename, STORAGE_NAME_FILESYSTEM, STORAGE_NAME_GRIDFS} from '/models/lib/fileStoreStrategy';
let attachmentBucket;
let storagePath;
@ -34,12 +34,13 @@ Attachments = new FilesCollection({
return ret;
},
onAfterUpload(fileObj) {
let storage = fileObj.meta.copyStorage || STORAGE_NAME_GRIDFS;
// current storage is the filesystem, update object and database
Object.keys(fileObj.versions).forEach(versionName => {
fileObj.versions[versionName].storage = STORAGE_NAME_FILESYSTEM;
});
Attachments.update({ _id: fileObj._id }, { $set: { "versions" : fileObj.versions } });
moveToStorage(fileObj, STORAGE_NAME_GRIDFS, fileStoreStrategyFactory);
moveToStorage(fileObj, storage, fileStoreStrategyFactory);
},
interceptDownload(http, fileObj, versionName) {
const ret = fileStoreStrategyFactory.getFileStrategy(fileObj, versionName).interceptDownload(http, this.cacheControl);
@ -86,10 +87,17 @@ if (Meteor.isServer) {
const fileObj = Attachments.findOne({_id: fileObjId});
moveToStorage(fileObj, storageDestination, fileStoreStrategyFactory);
},
renameAttachment(fileObjId, newName) {
check(fileObjId, String);
check(newName, String);
const fileObj = Attachments.findOne({_id: fileObjId});
rename(fileObj, newName, fileStoreStrategyFactory);
},
});
Meteor.startup(() => {
Attachments.collection._ensureIndex({ 'meta.cardId': 1 });
Attachments.collection.createIndex({ 'meta.cardId': 1 });
const storagePath = fileStoreStrategyFactory.storagePath;
if (!fs.existsSync(storagePath)) {
console.log("create storagePath because it doesn't exist: " + storagePath);

View file

@ -1,3 +1,5 @@
import escapeForRegex from 'escape-string-regexp';
import { TAPi18n } from '/imports/i18n';
import {
ALLOWED_BOARD_COLORS,
ALLOWED_COLORS,
@ -7,7 +9,7 @@ import {
} from '/config/const';
import Users from "./users";
const escapeForRegex = require('escape-string-regexp');
// const escapeForRegex = require('escape-string-regexp');
Boards = new Mongo.Collection('boards');
@ -1737,15 +1739,15 @@ Boards.before.insert((userId, doc) => {
if (Meteor.isServer) {
// Let MongoDB ensure that a member is not included twice in the same board
Meteor.startup(() => {
Boards._collection._ensureIndex({ modifiedAt: -1 });
Boards._collection._ensureIndex(
Boards._collection.createIndex({ modifiedAt: -1 });
Boards._collection.createIndex(
{
_id: 1,
'members.userId': 1,
},
{ unique: true },
);
Boards._collection._ensureIndex({ 'members.userId': 1 });
Boards._collection.createIndex({ 'members.userId': 1 });
});
// Genesis: the first activity of the newly created board

View file

@ -54,6 +54,6 @@ CardCommentReactions.allow({
if (Meteor.isServer) {
Meteor.startup(() => {
CardCommentReactions._collection._ensureIndex({ cardCommentId: 1 }, { unique: true });
CardCommentReactions._collection.createIndex({ cardCommentId: 1 }, { unique: true });
});
}

View file

@ -1,4 +1,5 @@
const escapeForRegex = require('escape-string-regexp');
import escapeForRegex from 'escape-string-regexp';
CardComments = new Mongo.Collection('card_comments');
/**
@ -178,8 +179,8 @@ if (Meteor.isServer) {
// Comments are often fetched within a card, so we create an index to make these
// queries more efficient.
Meteor.startup(() => {
CardComments._collection._ensureIndex({ modifiedAt: -1 });
CardComments._collection._ensureIndex({ cardId: 1, createdAt: -1 });
CardComments._collection.createIndex({ modifiedAt: -1 });
CardComments._collection.createIndex({ cardId: 1, createdAt: -1 });
});
CardComments.after.insert((userId, doc) => {

View file

@ -1,10 +1,12 @@
import moment from 'moment/min/moment-with-locales';
import {
ALLOWED_COLORS,
TYPE_CARD,
TYPE_LINKED_BOARD,
TYPE_LINKED_CARD,
} from '../config/const';
import Attachments from "./attachments";
import Attachments, { fileStoreStrategyFactory } from "./attachments";
import { copyFile } from './lib/fileStoreStrategy.js';
Cards = new Mongo.Collection('cards');
@ -585,11 +587,11 @@ Cards.helpers({
const _id = Cards.insert(this);
// Copy attachments
oldCard.attachments().forEach(att => {
att.cardId = _id;
delete att._id;
return Attachments.insert(att);
});
oldCard.attachments()
.map(att => att.get())
.forEach(att => {
copyFile(att, _id, fileStoreStrategyFactory);
});
// copy checklists
Checklists.find({ cardId: oldId }).forEach(ch => {
@ -689,6 +691,53 @@ Cards.helpers({
return _.contains(this.labelIds, labelId);
},
/** returns the sort number of a list
* @param listId a list id
* @param swimlaneId a swimlane id
* top sorting of the card at the top if true, or from the bottom if false
*/
getSort(listId, swimlaneId, top) {
if (!_.isBoolean(top)) {
top = true;
}
if (!listId) {
listId = this.listId;
}
if (!swimlaneId) {
swimlaneId = this.swimlaneId;
}
const selector = {
listId: listId,
swimlaneId: swimlaneId,
archived: false,
};
const sorting = top ? 1 : -1;
const card = Cards.findOne(selector, { sort: { sort: sorting } });
let ret = null
if (card) {
ret = card.sort;
}
return ret;
},
/** returns the sort number of a list from the card at the top
* @param listId a list id
* @param swimlaneId a swimlane id
*/
getMinSort(listId, swimlaneId) {
const ret = this.getSort(listId, swimlaneId, true);
return ret;
},
/** returns the sort number of a list from the card at the bottom
* @param listId a list id
* @param swimlaneId a swimlane id
*/
getMaxSort(listId, swimlaneId) {
const ret = this.getSort(listId, swimlaneId, false);
return ret;
},
user() {
return Users.findOne(this.userId);
},
@ -737,17 +786,15 @@ Cards.helpers({
},
attachments() {
let id = this._id;
if (this.isLinkedCard()) {
return Attachments.find(
{ 'meta.cardId': this.linkedId },
{ sort: { uploadedAt: -1 } },
).each();
} else {
return Attachments.find(
{ 'meta.cardId': this._id },
{ sort: { uploadedAt: -1 } },
).each();
id = this.linkedId;
}
let ret = Attachments.find(
{ 'meta.cardId': id },
{ sort: { uploadedAt: -1 } },
).each();
return ret;
},
cover() {
@ -2992,14 +3039,14 @@ if (Meteor.isServer) {
// Cards are often fetched within a board, so we create an index to make these
// queries more efficient.
Meteor.startup(() => {
Cards._collection._ensureIndex({ modifiedAt: -1 });
Cards._collection._ensureIndex({ boardId: 1, createdAt: -1 });
Cards._collection.createIndex({ modifiedAt: -1 });
Cards._collection.createIndex({ boardId: 1, createdAt: -1 });
// https://github.com/wekan/wekan/issues/1863
// Swimlane added a new field in the cards collection of mongodb named parentId.
// When loading a board, mongodb is searching for every cards, the id of the parent (in the swinglanes collection).
// With a huge database, this result in a very slow app and high CPU on the mongodb side.
// To correct it, add Index to parentId:
Cards._collection._ensureIndex({ parentId: 1 });
Cards._collection.createIndex({ parentId: 1 });
// let notifydays = parseInt(process.env.NOTIFY_DUE_DAYS_BEFORE_AND_AFTER) || 2; // default as 2 days b4 and after
// let notifyitvl = parseInt(process.env.NOTIFY_DUE_AT_HOUR_OF_DAY) || 3600 * 24 * 1e3; // default interval as one day
// Meteor.call("findDueCards",notifydays,notifyitvl);

View file

@ -213,9 +213,9 @@ function publishChekListUncompleted(userId, doc) {
// Activities
if (Meteor.isServer) {
Meteor.startup(() => {
ChecklistItems._collection._ensureIndex({ modifiedAt: -1 });
ChecklistItems._collection._ensureIndex({ checklistId: 1 });
ChecklistItems._collection._ensureIndex({ cardId: 1 });
ChecklistItems._collection.createIndex({ modifiedAt: -1 });
ChecklistItems._collection.createIndex({ checklistId: 1 });
ChecklistItems._collection.createIndex({ cardId: 1 });
});
ChecklistItems.after.update((userId, doc, fieldNames) => {

View file

@ -195,8 +195,8 @@ Checklists.mutations({
if (Meteor.isServer) {
Meteor.startup(() => {
Checklists._collection._ensureIndex({ modifiedAt: -1 });
Checklists._collection._ensureIndex({ cardId: 1, createdAt: 1 });
Checklists._collection.createIndex({ modifiedAt: -1 });
Checklists._collection.createIndex({ cardId: 1, createdAt: 1 });
});
Checklists.after.insert((userId, doc) => {

View file

@ -231,8 +231,8 @@ function customFieldEdit(userId, doc) {
if (Meteor.isServer) {
Meteor.startup(() => {
CustomFields._collection._ensureIndex({ modifiedAt: -1 });
CustomFields._collection._ensureIndex({ boardIds: 1 });
CustomFields._collection.createIndex({ modifiedAt: -1 });
CustomFields._collection.createIndex({ boardIds: 1 });
});
CustomFields.after.insert((userId, doc) => {

View file

@ -1,6 +1,10 @@
import { Exporter } from './exporter';
import { Meteor } from 'meteor/meteor';
/* global JsonRoutes */
if (Meteor.isServer) {
import { Picker } from 'meteor/communitypackages:picker';
// todo XXX once we have a real API in place, move that route there
// todo XXX also share the route definition between the client and the server
// so that we could use something like

View file

@ -1,3 +1,4 @@
import { TAPi18n } from '/imports/i18n';
import { runOnServer } from './runOnServer';
runOnServer(function() {
@ -5,6 +6,7 @@ runOnServer(function() {
// it here we use runOnServer to have it inside a function instead of an
// if (Meteor.isServer) block
import { ExporterExcel } from './server/ExporterExcel';
import { Picker } from 'meteor/communitypackages:picker';
// todo XXX once we have a real API in place, move that route there
// todo XXX also share the route definition between the client and the server
@ -49,12 +51,12 @@ runOnServer(function() {
isAdmin: true,
});
}
let userLanguage = 'en';
if(user && user.profile){
userLanguage = user.profile.language
}
const exporterExcel = new ExporterExcel(boardId, userLanguage);
if (exporterExcel.canExport(user) || impersonateDone) {
if (impersonateDone) {

View file

@ -1,3 +1,4 @@
import { TAPi18n } from '/imports/i18n';
import { runOnServer } from './runOnServer';
runOnServer(function() {
@ -5,6 +6,7 @@ runOnServer(function() {
// it here we use runOnServer to have it inside a function instead of an
// if (Meteor.isServer) block
import { ExporterCardPDF } from './server/ExporterCardPDF';
import { Picker } from 'meteor/communitypackages:picker';
// todo XXX once we have a real API in place, move that route there
// todo XXX also share the route definition between the client and the server

View file

@ -1,4 +1,8 @@
import moment from 'moment/min/moment-with-locales';
const Papa = require('papaparse');
import { TAPi18n } from '/imports/i18n';
//const stringify = require('csv-stringify');
//const stringify = require('csv-stringify');

View file

@ -121,8 +121,8 @@ Integrations.allow({
//INTEGRATIONS REST API
if (Meteor.isServer) {
Meteor.startup(() => {
Integrations._collection._ensureIndex({ modifiedAt: -1 });
Integrations._collection._ensureIndex({ boardId: 1 });
Integrations._collection.createIndex({ modifiedAt: -1 });
Integrations._collection.createIndex({ boardId: 1 });
});
/**

View file

@ -65,7 +65,7 @@ InvitationCodes.helpers({
if (Meteor.isServer) {
Meteor.startup(() => {
InvitationCodes._collection._ensureIndex({ modifiedAt: -1 });
InvitationCodes._collection.createIndex({ modifiedAt: -1 });
});
Boards.deny({
fetch: ['members'],

View file

@ -114,6 +114,13 @@ class FileStoreStrategy {
unlink() {
}
/** rename the file (physical)
* @li at database the filename is updated after this method
* @param newFilePath the new file path
*/
rename(newFilePath) {
}
/** return the storage name
* @return the storage name
*/
@ -287,6 +294,14 @@ export class FileStoreStrategyFilesystem extends FileStoreStrategy {
fs.unlink(filePath, () => {});
}
/** rename the file (physical)
* @li at database the filename is updated after this method
* @param newFilePath the new file path
*/
rename(newFilePath) {
fs.renameSync(this.fileObj.versions[this.versionName].path, newFilePath);
}
/** return the storage name
* @return the storage name
*/
@ -312,11 +327,11 @@ export const moveToStorage = function(fileObj, storageDestination, fileStoreStra
const writeStream = strategyWrite.getWriteStream(filePath);
writeStream.on('error', error => {
console.error('[writeStream error]: ', error, fileObjId);
console.error('[writeStream error]: ', error, fileObj._id);
});
readStream.on('error', error => {
console.error('[readStream error]: ', error, fileObjId);
console.error('[readStream error]: ', error, fileObj._id);
});
writeStream.on('finish', Meteor.bindEnvironment((finishedData) => {
@ -336,3 +351,69 @@ export const moveToStorage = function(fileObj, storageDestination, fileStoreStra
}
});
};
export const copyFile = function(fileObj, newCardId, fileStoreStrategyFactory) {
const versionName = "original";
const strategyRead = fileStoreStrategyFactory.getFileStrategy(fileObj, versionName);
const readStream = strategyRead.getReadStream();
const strategyWrite = fileStoreStrategyFactory.getFileStrategy(fileObj, versionName, STORAGE_NAME_FILESYSTEM);
const tempPath = path.join(fileStoreStrategyFactory.storagePath, Random.id() + "-" + versionName + "-" + fileObj.name);
const writeStream = strategyWrite.getWriteStream(tempPath);
writeStream.on('error', error => {
console.error('[writeStream error]: ', error, fileObj._id);
});
readStream.on('error', error => {
console.error('[readStream error]: ', error, fileObj._id);
});
// https://forums.meteor.com/t/meteor-code-must-always-run-within-a-fiber-try-wrapping-callbacks-that-you-pass-to-non-meteor-libraries-with-meteor-bindenvironmen/40099/8
readStream.on('end', Meteor.bindEnvironment(() => {
const fileId = Random.id();
Attachments.addFile(
tempPath,
{
fileName: fileObj.name,
type: fileObj.type,
meta: {
boardId: fileObj.meta.boardId,
cardId: newCardId,
listId: fileObj.meta.listId,
swimlaneId: fileObj.meta.swimlaneId,
source: 'copy',
copyFrom: fileObj._id,
copyStorage: strategyRead.getStorageName(),
},
userId: fileObj.userId,
size: fileObj.fileSize,
fileId,
},
(err, fileRef) => {
if (err) {
console.log(err);
} else {
// Set the userId again
Attachments.update({ _id: fileRef._id }, { $set: { userId: fileObj.userId } });
}
},
true,
);
}));
readStream.pipe(writeStream);
};
export const rename = function(fileObj, newName, fileStoreStrategyFactory) {
Object.keys(fileObj.versions).forEach(versionName => {
const strategy = fileStoreStrategyFactory.getFileStrategy(fileObj, versionName);
const newFilePath = strategy.getNewPath(fileStoreStrategyFactory.storagePath, newName);
strategy.rename(newFilePath);
Attachments.update({ _id: fileObj._id }, { $set: {
"name": newName,
[`versions.${versionName}.path`]: newFilePath,
} });
});
};

View file

@ -0,0 +1,51 @@
import { Meteor } from 'meteor/meteor';
import fs from 'fs';
export const createOnAfterUpload = bucket =>
function onAfterUpload(file) {
const self = this;
// here you could manipulate your file
// and create a new version, for example a scaled 'thumbnail'
// ...
// then we read all versions we have got so far
Object.keys(file.versions).forEach(versionName => {
const metadata = { ...file.meta, versionName, fileId: file._id };
fs.createReadStream(file.versions[versionName].path)
// this is where we upload the binary to the bucket using bucket.openUploadStream
// see http://mongodb.github.io/node-mongodb-native/3.2/api/GridFSBucket.html#openUploadStream
.pipe(
bucket.openUploadStream(file.name, {
contentType: file.type || 'binary/octet-stream',
metadata,
}),
)
// and we unlink the file from the fs on any error
// that occurred during the upload to prevent zombie files
.on('error', err => {
// console.error("[createOnAfterUpload error]", err);
self.unlink(this.collection.findOne(file._id), versionName); // Unlink files from FS
})
// once we are finished, we attach the gridFS Object id on the
// FilesCollection document's meta section and finally unlink the
// upload file from the filesystem
.on(
'finish',
Meteor.bindEnvironment(ver => {
const property = `versions.${versionName}.meta.gridFsFileId`;
self.collection.update(file._id, {
$set: {
[property]: ver._id.toHexString(),
},
});
self.unlink(this.collection.findOne(file._id), versionName); // Unlink files from FS
}),
);
});
};

View file

@ -415,9 +415,9 @@ Lists.hookOptions.after.update = { fetchPrevious: false };
if (Meteor.isServer) {
Meteor.startup(() => {
Lists._collection._ensureIndex({ modifiedAt: -1 });
Lists._collection._ensureIndex({ boardId: 1 });
Lists._collection._ensureIndex({ archivedAt: -1 });
Lists._collection.createIndex({ modifiedAt: -1 });
Lists._collection.createIndex({ boardId: 1 });
Lists._collection.createIndex({ archivedAt: -1 });
});
Lists.after.insert((userId, doc) => {

View file

@ -267,8 +267,8 @@ if (Meteor.isServer) {
if (Meteor.isServer) {
// Index for Organization name.
Meteor.startup(() => {
// Org._collection._ensureIndex({ name: -1 });
Org._collection._ensureIndex({ orgDisplayName: 1 });
// Org._collection.createIndex({ name: -1 });
Org._collection.createIndex({ orgDisplayName: 1 });
});
}

View file

@ -74,8 +74,8 @@ OrgUser.attachSchema(
if (Meteor.isServer) {
// Index for Organization User.
Meteor.startup(() => {
OrgUser._collection._ensureIndex({ orgId: -1 });
OrgUser._collection._ensureIndex({ orgId: -1, userId: -1 });
OrgUser._collection.createIndex({ orgId: -1 });
OrgUser._collection.createIndex({ orgId: -1, userId: -1 });
});
}

View file

@ -1,12 +0,0 @@
if (Meteor.isServer) {
Meteor.startup(() => {
// Date of 7 days ago
let lastWeek = new Date();
lastWeek.setDate(lastWeek.getDate() - 7);
presences.remove({ ttl: { $lte: lastWeek } });
// Create index for serverId that is queried often
presences._collection._ensureIndex({ serverId: -1 });
});
}

View file

@ -87,7 +87,7 @@ Rules.allow({
if (Meteor.isServer) {
Meteor.startup(() => {
Rules._collection._ensureIndex({ modifiedAt: -1 });
Rules._collection.createIndex({ modifiedAt: -1 });
});
}

View file

@ -1,3 +1,5 @@
import moment from 'moment/min/moment-with-locales';
import { TAPi18n } from '/imports/i18n';
import { createWorkbook } from './createWorkbook';
// exporter maybe is broken since Gridfs introduced, add fs and path
@ -432,7 +434,7 @@ class ExporterExcel {
//add blank row
ws.addRow().values = ['', '', '', '', '', ''];
//add board description
ws.addRow().values = [
TAPi18n.__('description','',this.userLanguage),
@ -442,7 +444,7 @@ class ExporterExcel {
ws.mergeCells('B3:H3');
ws.getRow(3).height = 40;
// In MS Excel, we can't use the AutoFit feature on a column that contains a cell merged with cells in other columns.
// Likewise, we can't use AutoFit on a row that contains a cell merged with cells in other rows.
// Likewise, we can't use AutoFit on a row that contains a cell merged with cells in other rows.
ws.getRow(3).font = {
name: TAPi18n.__('excel-font'),
size: 10,
@ -459,7 +461,7 @@ class ExporterExcel {
vertical: 'middle',
};
cellCenter('A3');
//add blank row
ws.addRow().values = ['', '', '', '', '', ''];
@ -494,7 +496,7 @@ class ExporterExcel {
},
numFmt: 'yyyy/mm/dd hh:mm:ss',
};
cellCenter('A5');
cellCenter('B5');
cellCenter('C5');
@ -502,7 +504,7 @@ class ExporterExcel {
cellCenter('E5');
cellLeft('F5');
ws.getRow(5).height = 20;
allBorder('A5');
allBorder('B5');
allBorder('C5');
@ -786,7 +788,7 @@ class ExporterExcel {
},
};
}
//add title line
ws2.mergeCells('A1:F1');
ws2.getCell('A1').value = result.title;
@ -813,7 +815,7 @@ class ExporterExcel {
TAPi18n.__('card','',this.userLanguage),
TAPi18n.__('owner','',this.userLanguage),
TAPi18n.__('createdAt','',this.userLanguage),
TAPi18n.__('last-modified-at','',this.userLanguage),
TAPi18n.__('last-modified-at','',this.userLanguage),
];
ws2.getRow(3).height = 20;
ws2.getRow(3).font = {

View file

@ -1,3 +1,4 @@
import { TAPi18n } from '/imports/i18n';
//var nodemailer = require('nodemailer');
// Sandstorm context is detected using the METEOR_SETTINGS environment variable
@ -153,7 +154,7 @@ Settings.allow({
if (Meteor.isServer) {
Meteor.startup(() => {
Settings._collection._ensureIndex({ modifiedAt: -1 });
Settings._collection.createIndex({ modifiedAt: -1 });
const setting = Settings.findOne({});
if (!setting) {
const now = new Date();

View file

@ -331,8 +331,8 @@ Swimlanes.hookOptions.after.update = { fetchPrevious: false };
if (Meteor.isServer) {
Meteor.startup(() => {
Swimlanes._collection._ensureIndex({ modifiedAt: -1 });
Swimlanes._collection._ensureIndex({ boardId: 1 });
Swimlanes._collection.createIndex({ modifiedAt: -1 });
Swimlanes._collection.createIndex({ boardId: 1 });
});
Swimlanes.after.insert((userId, doc) => {

View file

@ -51,7 +51,7 @@ TableVisibilityModeSettings.allow({
if (Meteor.isServer) {
Meteor.startup(() => {
TableVisibilityModeSettings._collection._ensureIndex({ modifiedAt: -1 });
TableVisibilityModeSettings._collection.createIndex({ modifiedAt: -1 });
TableVisibilityModeSettings.upsert(
{ _id: 'tableVisibilityMode-allowPrivateOnly' },
{

View file

@ -264,7 +264,7 @@ if (Meteor.isServer) {
if (Meteor.isServer) {
// Index for Team name.
Meteor.startup(() => {
Team._collection._ensureIndex({ teamDisplayName: 1 });
Team._collection.createIndex({ teamDisplayName: 1 });
});
}

View file

@ -1,4 +1,5 @@
import Attachments from "./attachments";
import moment from 'moment/min/moment-with-locales';
import { TAPi18n } from '/imports/i18n';
const DateString = Match.Where(function(dateAsString) {
check(dateAsString, String);
@ -447,13 +448,11 @@ export class TrelloCreator {
});
}
};
// TODO: Add import attachment with Trello API key
// like Python code at wekan/trello/ of https://github.com/wekan/wekan
//if (att.url) {
// Attachment.load(att.url, opts, cb, true);
//} else if (att.file) {
// Attachment.write(att.file, opts, cb, true);
//}
if (att.url) {
Attachment.load(att.url, opts, cb, true);
} else if (att.file) {
Attachment.write(att.file, opts, cb, true);
}
});
if (links) {

View file

@ -70,7 +70,7 @@ Triggers.helpers({
if (Meteor.isServer) {
Meteor.startup(() => {
Triggers._collection._ensureIndex({ modifiedAt: -1 });
Triggers._collection.createIndex({ modifiedAt: -1 });
});
}

View file

@ -56,8 +56,8 @@ if (Meteor.isServer) {
return userId === doc.userId && fieldNames.indexOf('userId') === -1;
}
Meteor.startup(() => {
UnsavedEditCollection._collection._ensureIndex({ modifiedAt: -1 });
UnsavedEditCollection._collection._ensureIndex({ userId: 1 });
UnsavedEditCollection._collection.createIndex({ modifiedAt: -1 });
UnsavedEditCollection._collection.createIndex({ userId: 1 });
});
UnsavedEditCollection.allow({
insert: isAuthor,

View file

@ -1,5 +1,6 @@
//var nodemailer = require('nodemailer');
import { SyncedCron } from 'meteor/percolate:synced-cron';
import { TAPi18n } from '/imports/i18n';
import ImpersonatedUsers from './impersonatedUsers';
// Sandstorm context is detected using the METEOR_SETTINGS environment variable
@ -1660,12 +1661,13 @@ if (Meteor.isServer) {
// Let mongoDB ensure username unicity
Meteor.startup(() => {
allowedSortValues.forEach((value) => {
Lists._collection._ensureIndex(value);
Lists._collection.createIndex(value);
});
Users._collection._ensureIndex({
Users._collection.createIndex({
modifiedAt: -1,
});
Users._collection._ensureIndex(
/* Commented out extra index because of IndexOptionsConflict.
Users._collection.createIndex(
{
username: 1,
},
@ -1673,6 +1675,7 @@ if (Meteor.isServer) {
unique: true,
},
);
*/
Meteor.defer(() => {
addCronJob();
});

View file

@ -1,3 +1,5 @@
import moment from 'moment/min/moment-with-locales';
const DateString = Match.Where(function(dateAsString) {
check(dateAsString, String);
return moment(dateAsString, moment.ISO_8601).isValid();