From 77f8b76d4e13c35ea3451622176bbb69a4d39a32 Mon Sep 17 00:00:00 2001 From: "Sam X. Chen" Date: Thu, 10 Oct 2019 22:57:40 -0400 Subject: Add Features: allowing lists to be sorted by modifiedAt when not in draggable mode --- models/boards.js | 15 +++++++++++++++ models/cards.js | 17 +++++++++++++++++ models/swimlanes.js | 15 +++++++++++++++ 3 files changed, 47 insertions(+) (limited to 'models') diff --git a/models/boards.js b/models/boards.js index a9348478..c7f93022 100644 --- a/models/boards.js +++ b/models/boards.js @@ -409,6 +409,21 @@ Boards.helpers({ }, lists() { + const enabled = Meteor.user().hasShowDesktopDragHandles(); + return enabled ? this.draggableLists() : this.newestLists(); + }, + + newestLists() { + // sorted lists from newest to the oldest, by its creation date or its cards' last modification date + return Lists.find( + { + boardId: this._id, + archived: false, + }, + { sort: { updatedAt: -1 } }, + ); + }, + draggableLists() { return Lists.find({ boardId: this._id }, { sort: { sort: 1 } }); }, diff --git a/models/cards.js b/models/cards.js index 371ad185..35d596d6 100644 --- a/models/cards.js +++ b/models/cards.js @@ -1695,6 +1695,23 @@ if (Meteor.isServer) { const oldvalue = doc[action] || ''; const activityType = `a-${action}`; const card = Cards.findOne(doc._id); + const list = card.list(); + if (list) { + // change list modifiedAt + const modifiedAt = new Date(); + const boardId = list.boardId; + Lists.direct.update( + { + _id: list._id, + }, + { + $set: { + modifiedAt, + boardId, + }, + }, + ); + } const username = Users.findOne(userId).username; const activity = { userId, diff --git a/models/swimlanes.js b/models/swimlanes.js index 46e410da..4cd35574 100644 --- a/models/swimlanes.js +++ b/models/swimlanes.js @@ -174,6 +174,21 @@ Swimlanes.helpers({ }, lists() { + const enabled = Meteor.user().hasShowDesktopDragHandles(); + return enabled ? this.draggableLists() : this.newestLists(); + }, + newestLists() { + // sorted lists from newest to the oldest, by its creation date or its cards' last modification date + return Lists.find( + { + boardId: this.boardId, + swimlaneId: { $in: [this._id, ''] }, + archived: false, + }, + { sort: { updatedAt: -1 } }, + ); + }, + draggableLists() { return Lists.find( { boardId: this.boardId, -- cgit v1.2.3-1-g7c22 From f53c624b0f6c6ebcc20c378a153e5cda8d73463c Mon Sep 17 00:00:00 2001 From: "Sam X. Chen" Date: Fri, 11 Oct 2019 11:01:10 -0400 Subject: Buf Fix #2093: the broken should be prior to file attachment feature introduced --- models/export.js | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'models') diff --git a/models/export.js b/models/export.js index a69be970..3f4c8590 100644 --- a/models/export.js +++ b/models/export.js @@ -50,12 +50,18 @@ if (Meteor.isServer) { }); } +// exporter maybe is broken since Gridfs introduced, add fs and path + export class Exporter { constructor(boardId) { this._boardId = boardId; } build() { + const fs = Npm.require('fs'); + const os = Npm.require('os'); + const path = Npm.require('path'); + const byBoard = { boardId: this._boardId }; const byBoardNoLinked = { boardId: this._boardId, @@ -134,6 +140,9 @@ export class Exporter { const getBase64Data = function(doc, callback) { let buffer = new Buffer(0); // callback has the form function (err, res) {} + const tmpWriteable = fs.createWriteStream( + path.join(os.tmpdir(), `tmpexport${process.pid}`), + ); const readStream = doc.createReadStream(); readStream.on('data', function(chunk) { buffer = Buffer.concat([buffer, chunk]); @@ -145,6 +154,7 @@ export class Exporter { // done callback(null, buffer.toString('base64')); }); + readStream.pipe(tmpWriteable); }; const getBase64DataSync = Meteor.wrapAsync(getBase64Data); result.attachments = Attachments.find(byBoard) -- cgit v1.2.3-1-g7c22 From 2737d6b23f3a0fd2314236a85fbdee536df745a2 Mon Sep 17 00:00:00 2001 From: "Sam X. Chen" Date: Fri, 11 Oct 2019 11:56:44 -0400 Subject: Bug Fix:2093, need to clean up the temporary file --- models/export.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'models') diff --git a/models/export.js b/models/export.js index 3f4c8590..5d356487 100644 --- a/models/export.js +++ b/models/export.js @@ -140,9 +140,11 @@ export class Exporter { const getBase64Data = function(doc, callback) { let buffer = new Buffer(0); // callback has the form function (err, res) {} - const tmpWriteable = fs.createWriteStream( - path.join(os.tmpdir(), `tmpexport${process.pid}`), + const tmpFile = path.join( + os.tmpdir(), + `tmpexport${process.pid}${Math.radom()}`, ); + const tmpWriteable = fs.createWriteStream(tmpFile); const readStream = doc.createReadStream(); readStream.on('data', function(chunk) { buffer = Buffer.concat([buffer, chunk]); @@ -152,6 +154,9 @@ export class Exporter { }); readStream.on('end', function() { // done + fs.unlink(tmpFile, () => { + //ignored + }); callback(null, buffer.toString('base64')); }); readStream.pipe(tmpWriteable); -- cgit v1.2.3-1-g7c22 From bc2a20f04e32607f8488a9cecd815647fb43e40e Mon Sep 17 00:00:00 2001 From: "Sam X. Chen" Date: Fri, 18 Oct 2019 16:44:09 -0400 Subject: Add Feature: allow user to sort Lists in Board by his own preference, boardadmin can star list --- models/boards.js | 8 +++++--- models/cards.js | 6 ++++-- models/lists.js | 22 +++++++++++++++++++- models/swimlanes.js | 6 +++--- models/users.js | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 91 insertions(+), 9 deletions(-) (limited to 'models') diff --git a/models/boards.js b/models/boards.js index c7f93022..85a7558c 100644 --- a/models/boards.js +++ b/models/boards.js @@ -409,18 +409,20 @@ Boards.helpers({ }, lists() { - const enabled = Meteor.user().hasShowDesktopDragHandles(); - return enabled ? this.draggableLists() : this.newestLists(); + const enabled = Meteor.user().hasSortBy(); + return enabled ? this.newestLists() : this.draggableLists(); }, newestLists() { // sorted lists from newest to the oldest, by its creation date or its cards' last modification date + const value = Meteor.user()._getListSortBy(); + const sortKey = { starred: -1, [value[0]]: value[1] }; // [["starred",-1],value]; return Lists.find( { boardId: this._id, archived: false, }, - { sort: { updatedAt: -1 } }, + { sort: sortKey }, ); }, draggableLists() { diff --git a/models/cards.js b/models/cards.js index 35d596d6..27dda0ee 100644 --- a/models/cards.js +++ b/models/cards.js @@ -1696,9 +1696,11 @@ if (Meteor.isServer) { const activityType = `a-${action}`; const card = Cards.findOne(doc._id); const list = card.list(); - if (list) { + if (list && action === 'endAt') { // change list modifiedAt - const modifiedAt = new Date(); + const modifiedAt = new Date( + new Date(value).getTime() - 365 * 24 * 3600 * 1e3, + ); // set it as 1 year before const boardId = list.boardId; Lists.direct.update( { diff --git a/models/lists.js b/models/lists.js index 9136c337..16ad434c 100644 --- a/models/lists.js +++ b/models/lists.js @@ -11,6 +11,15 @@ Lists.attachSchema( */ type: String, }, + starred: { + /** + * if a list is stared + * then we put it on the top + */ + type: Boolean, + optional: true, + defaultValue: false, + }, archived: { /** * is the list archived @@ -81,10 +90,14 @@ Lists.attachSchema( denyUpdate: false, // eslint-disable-next-line consistent-return autoValue() { - if (this.isInsert || this.isUpsert || this.isUpdate) { + // this is redundant with updatedAt + /*if (this.isInsert || this.isUpsert || this.isUpdate) { return new Date(); } else { this.unset(); + }*/ + if (!this.isSet) { + return new Date(); } }, }, @@ -252,6 +265,10 @@ Lists.helpers({ return this.type === 'template-list'; }, + isStarred() { + return this.starred === true; + }, + remove() { Lists.remove({ _id: this._id }); }, @@ -261,6 +278,9 @@ Lists.mutations({ rename(title) { return { $set: { title } }; }, + star(enable = true) { + return { $set: { starred: !!enable } }; + }, archive() { if (this.isTemplateList()) { diff --git a/models/swimlanes.js b/models/swimlanes.js index 4cd35574..831f1eff 100644 --- a/models/swimlanes.js +++ b/models/swimlanes.js @@ -174,8 +174,8 @@ Swimlanes.helpers({ }, lists() { - const enabled = Meteor.user().hasShowDesktopDragHandles(); - return enabled ? this.draggableLists() : this.newestLists(); + const enabled = Meteor.user().hasSortBy(); + return enabled ? this.newestLists() : this.draggableLists(); }, newestLists() { // sorted lists from newest to the oldest, by its creation date or its cards' last modification date @@ -185,7 +185,7 @@ Swimlanes.helpers({ swimlaneId: { $in: [this._id, ''] }, archived: false, }, - { sort: { updatedAt: -1 } }, + { sort: { modifiedAt: -1 } }, ); }, draggableLists() { diff --git a/models/users.js b/models/users.js index 93fb409e..83a224ba 100644 --- a/models/users.js +++ b/models/users.js @@ -4,6 +4,16 @@ const isSandstorm = Meteor.settings && Meteor.settings.public && Meteor.settings.public.sandstorm; Users = Meteor.users; +const allowedSortValues = [ + '-modifiedAt', + 'modifiedAt', + '-title', + 'title', + '-sort', + 'sort', +]; +const defaultSortBy = allowedSortValues[0]; + /** * A User in wekan */ @@ -191,6 +201,15 @@ Users.attachSchema( 'board-view-cal', ], }, + 'profile.listSortBy': { + /** + * default sort list for user + */ + type: String, + optional: true, + defaultValue: defaultSortBy, + allowedValues: allowedSortValues, + }, 'profile.templatesBoardId': { /** * Reference to the templates board @@ -365,6 +384,31 @@ Users.helpers({ return _.contains(invitedBoards, boardId); }, + _getListSortBy() { + const profile = this.profile || {}; + const sortBy = profile.listSortBy || defaultSortBy; + const keyPattern = /^(-{0,1})(.*$)/; + const ret = []; + if (keyPattern.exec(sortBy)) { + ret[0] = RegExp.$2; + ret[1] = RegExp.$1 ? -1 : 1; + } + return ret; + }, + hasSortBy() { + // if use doesn't have dragHandle, then we can let user to choose sort list by different order + return !this.hasShowDesktopDragHandles(); + }, + getListSortBy() { + return this._getListSortBy()[0]; + }, + getListSortTypes() { + return allowedSortValues; + }, + getListSortByDirection() { + return this._getListSortBy()[1]; + }, + hasTag(tag) { const { tags = [] } = this.profile || {}; return _.contains(tags, tag); @@ -485,6 +529,13 @@ Users.mutations({ else this.addTag(tag); }, + setListSortBy(value) { + return { + $set: { + 'profile.listSortBy': value, + }, + }; + }, toggleDesktopHandles(value = false) { return { $set: { @@ -569,6 +620,10 @@ Meteor.methods({ Users.update(userId, { $set: { username } }); } }, + setListSortBy(value) { + check(value, String); + Meteor.user().setListSortBy(value); + }, toggleDesktopDragHandles() { const user = Meteor.user(); user.toggleDesktopHandles(user.hasShowDesktopDragHandles()); @@ -800,6 +855,9 @@ if (Meteor.isServer) { if (Meteor.isServer) { // Let mongoDB ensure username unicity Meteor.startup(() => { + allowedSortValues.forEach(value => { + Lists._collection._ensureIndex(value); + }); Users._collection._ensureIndex({ modifiedAt: -1 }); Users._collection._ensureIndex( { -- cgit v1.2.3-1-g7c22 From 32f50e16586696ec7d100ce0438d1030ae1f606e Mon Sep 17 00:00:00 2001 From: "Sam X. Chen" Date: Sat, 19 Oct 2019 00:28:49 -0400 Subject: Add Feature: allow user to search Lists in Board --- models/lists.js | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'models') diff --git a/models/lists.js b/models/lists.js index 16ad434c..f06b15b1 100644 --- a/models/lists.js +++ b/models/lists.js @@ -269,6 +269,10 @@ Lists.helpers({ return this.starred === true; }, + absoluteUrl() { + const card = Cards.findOne({ listId: this._id }); + return card && card.absoluteUrl(); + }, remove() { Lists.remove({ _id: this._id }); }, -- cgit v1.2.3-1-g7c22 From e195c731de88aba4026c239f4552ae821d522ec7 Mon Sep 17 00:00:00 2001 From: "Sam X. Chen" Date: Tue, 29 Oct 2019 17:45:06 -0400 Subject: Change the radom to random typo in export.js --- models/cards.js | 8 +++++--- models/export.js | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'models') diff --git a/models/cards.js b/models/cards.js index 27dda0ee..a43402bb 100644 --- a/models/cards.js +++ b/models/cards.js @@ -1696,10 +1696,12 @@ if (Meteor.isServer) { const activityType = `a-${action}`; const card = Cards.findOne(doc._id); const list = card.list(); - if (list && action === 'endAt') { - // change list modifiedAt + if (list) { + // change list modifiedAt, when user modified the key values in timingaction array, if it's endAt, put the modifiedAt of list back to one year ago for sorting purpose const modifiedAt = new Date( - new Date(value).getTime() - 365 * 24 * 3600 * 1e3, + new Date(value).getTime() - (action === 'endAt') + ? 365 * 24 * 3600 * 1e3 + : 0, ); // set it as 1 year before const boardId = list.boardId; Lists.direct.update( diff --git a/models/export.js b/models/export.js index 5d356487..056eefdc 100644 --- a/models/export.js +++ b/models/export.js @@ -142,7 +142,7 @@ export class Exporter { // callback has the form function (err, res) {} const tmpFile = path.join( os.tmpdir(), - `tmpexport${process.pid}${Math.radom()}`, + `tmpexport${process.pid}${Math.random()}`, ); const tmpWriteable = fs.createWriteStream(tmpFile); const readStream = doc.createReadStream(); -- cgit v1.2.3-1-g7c22 From 3308d90a3a6a1ddeed33966767937cd2c2c90cb5 Mon Sep 17 00:00:00 2001 From: "Sam X. Chen" Date: Wed, 30 Oct 2019 12:26:39 -0400 Subject: Fix: List last modify time will be affected by cards dueAt, endAt --- models/cards.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'models') diff --git a/models/cards.js b/models/cards.js index a43402bb..1c56a6d2 100644 --- a/models/cards.js +++ b/models/cards.js @@ -1699,9 +1699,8 @@ if (Meteor.isServer) { if (list) { // change list modifiedAt, when user modified the key values in timingaction array, if it's endAt, put the modifiedAt of list back to one year ago for sorting purpose const modifiedAt = new Date( - new Date(value).getTime() - (action === 'endAt') - ? 365 * 24 * 3600 * 1e3 - : 0, + new Date(value).getTime() - + (action === 'endAt' ? 365 * 24 * 3600 * 1e3 : 0), ); // set it as 1 year before const boardId = list.boardId; Lists.direct.update( -- cgit v1.2.3-1-g7c22