diff options
author | Lauri Ojansivu <x@xet7.org> | 2019-04-06 08:46:40 +0300 |
---|---|---|
committer | Lauri Ojansivu <x@xet7.org> | 2019-04-06 08:46:40 +0300 |
commit | 56cccc678107a94d4cadb13f3b6138cef93a18b0 (patch) | |
tree | c3bef1326c7d328506e3bc38e6223fbf3f205c25 | |
parent | b680bb53725103f186ac1c7cb604fbd4a5773051 (diff) | |
parent | 48216e16537d50a27579c545c93624c0302a5a78 (diff) | |
download | wekan-56cccc678107a94d4cadb13f3b6138cef93a18b0.tar.gz wekan-56cccc678107a94d4cadb13f3b6138cef93a18b0.tar.bz2 wekan-56cccc678107a94d4cadb13f3b6138cef93a18b0.zip |
Merge remote-tracking branch 'Angtrim/feature-duplicate' into edge
-rw-r--r-- | build/config.gypi | 70 | ||||
-rw-r--r-- | client/components/boards/boardsList.jade | 2 | ||||
-rw-r--r-- | client/components/boards/boardsList.js | 15 | ||||
-rw-r--r-- | client/components/boards/boardsList.styl | 13 | ||||
-rw-r--r-- | client/components/rules/triggers/boardTriggers.jade | 3 | ||||
-rw-r--r-- | client/components/rules/triggers/boardTriggers.js | 6 | ||||
-rw-r--r-- | i18n/en.i18n.json | 3 | ||||
-rw-r--r-- | logs.txt | 29 | ||||
-rw-r--r-- | models/cards.js | 1 | ||||
-rw-r--r-- | models/export.js | 27 | ||||
-rw-r--r-- | models/import.js | 19 | ||||
-rw-r--r-- | models/wekanCreator.js | 25 | ||||
-rw-r--r-- | models/wekanmapper.js | 24 | ||||
-rw-r--r-- | server/rulesHelper.js | 4 | ||||
-rw-r--r-- | server/triggersDef.js | 6 |
15 files changed, 221 insertions, 26 deletions
diff --git a/build/config.gypi b/build/config.gypi new file mode 100644 index 00000000..760db73d --- /dev/null +++ b/build/config.gypi @@ -0,0 +1,70 @@ +# Do not edit. File was generated by node-gyp's "configure" step +{ + "target_defaults": { + "cflags": [], + "default_configuration": "Release", + "defines": [], + "include_dirs": [], + "libraries": [] + }, + "variables": { + "asan": 0, + "build_v8_with_gn": "false", + "coverage": "false", + "debug_nghttp2": "false", + "enable_lto": "false", + "enable_pgo_generate": "false", + "enable_pgo_use": "false", + "force_dynamic_crt": 0, + "host_arch": "x64", + "icu_gyp_path": "tools/icu/icu-system.gyp", + "icu_small": "false", + "icu_ver_major": "63", + "llvm_version": "0", + "node_byteorder": "little", + "node_debug_lib": "false", + "node_enable_d8": "false", + "node_enable_v8_vtunejit": "false", + "node_experimental_http_parser": "false", + "node_install_npm": "false", + "node_module_version": 67, + "node_no_browser_globals": "false", + "node_prefix": "/usr/local/Cellar/node/11.6.0", + "node_release_urlbase": "", + "node_shared": "false", + "node_shared_cares": "false", + "node_shared_http_parser": "false", + "node_shared_libuv": "false", + "node_shared_nghttp2": "false", + "node_shared_openssl": "false", + "node_shared_zlib": "false", + "node_tag": "", + "node_target_type": "executable", + "node_use_bundled_v8": "true", + "node_use_dtrace": "true", + "node_use_etw": "false", + "node_use_large_pages": "false", + "node_use_openssl": "true", + "node_use_pch": "false", + "node_use_v8_platform": "true", + "node_with_ltcg": "false", + "node_without_node_options": "false", + "openssl_fips": "", + "shlib_suffix": "67.dylib", + "target_arch": "x64", + "v8_enable_gdbjit": 0, + "v8_enable_i18n_support": 1, + "v8_enable_inspector": 1, + "v8_no_strict_aliasing": 1, + "v8_optimized_debug": 1, + "v8_promise_internal_field_count": 1, + "v8_random_seed": 0, + "v8_trace_maps": 0, + "v8_typed_array_max_size_in_heap": 0, + "v8_use_snapshot": "true", + "want_separate_host_toolset": 0, + "xcode_version": "10.0", + "nodedir": "/Users/angtrim/.node-gyp/11.6.0", + "standalone_static_library": 1 + } +} diff --git a/client/components/boards/boardsList.jade b/client/components/boards/boardsList.jade index e36b8fc6..abb009f7 100644 --- a/client/components/boards/boardsList.jade +++ b/client/components/boards/boardsList.jade @@ -22,7 +22,7 @@ template(name="boardList") i.fa.js-star-board( class="fa-star{{#if isStarred}} is-star-active{{else}}-o{{/if}}" title="{{_ 'star-board-title'}}") - + i.fa.js-clone-board(class="fa-clone") if hasSpentTimeCards i.fa.js-has-spenttime-cards( class="fa-circle{{#if hasOvertimeCards}} has-overtime-card-active{{else}} no-overtime-card-active{{/if}}" diff --git a/client/components/boards/boardsList.js b/client/components/boards/boardsList.js index 3aacdedb..ad28fee8 100644 --- a/client/components/boards/boardsList.js +++ b/client/components/boards/boardsList.js @@ -55,6 +55,21 @@ BlazeComponent.extendComponent({ Meteor.user().toggleBoardStar(boardId); evt.preventDefault(); }, + 'click .js-clone-board'(evt) { + Meteor.call('cloneBoard', + this.currentData()._id, + Session.get('fromBoard'), + (err, res) => { + if (err) { + this.setError(err.error); + } else { + Session.set('fromBoard', null); + Utils.goBoardId(res); + } + } + ); + evt.preventDefault(); + }, 'click .js-accept-invite'() { const boardId = this.currentData()._id; Meteor.user().removeInvite(boardId); diff --git a/client/components/boards/boardsList.styl b/client/components/boards/boardsList.styl index 80e47685..9f0b204e 100644 --- a/client/components/boards/boardsList.styl +++ b/client/components/boards/boardsList.styl @@ -93,14 +93,27 @@ $spaceBetweenTiles = 16px .is-star-active color: white + .fa-clone + position: absolute; + bottom: 0 + font-size: 14px + height: 18px + line-height: 18px + opacity: 0 + right: 0 + padding: 9px 9px + transition-duration: .15s + transition-property: color, font-size, background li:hover a &:hover .fa-star, + .fa-clone, .fa-star-o color: white .fa-star, + .fa-clone, .fa-star-o color: white opacity: .75 diff --git a/client/components/rules/triggers/boardTriggers.jade b/client/components/rules/triggers/boardTriggers.jade index b8c11d69..ff1406f6 100644 --- a/client/components/rules/triggers/boardTriggers.jade +++ b/client/components/rules/triggers/boardTriggers.jade @@ -64,8 +64,7 @@ template(name="boardTriggers") div.trigger-text | {{_'r-in-swimlane'}} div.trigger-dropdown - input(id="create-swimlane-name",type=text,placeholder="{{_'r-swimlane-name'}}") - div.trigger-button.trigger-button-person.js-show-user-field + input(id="create-swimlane-name-2",type=text,placeholder="{{_'r-swimlane-name'}}") div.trigger-button.trigger-button-person.js-show-user-field i.fa.fa-user div.user-details.hide-element diff --git a/client/components/rules/triggers/boardTriggers.js b/client/components/rules/triggers/boardTriggers.js index d4b9b81c..1dc5c437 100644 --- a/client/components/rules/triggers/boardTriggers.js +++ b/client/components/rules/triggers/boardTriggers.js @@ -39,15 +39,18 @@ BlazeComponent.extendComponent({ 'click .js-add-moved-trigger' (event) { const datas = this.data(); const desc = Utils.getTriggerActionDesc(event, this); - const swimlaneName = this.find('#create-swimlane-name').value; + const swimlaneName = this.find('#create-swimlane-name-2').value; const actionSelected = this.find('#move-action').value; const listName = this.find('#move-list-name').value; const boardId = Session.get('currentBoard'); + const divId = $(event.currentTarget.parentNode).attr('id'); + const cardTitle = this.cardTitleFilters[divId]; if (actionSelected === 'moved-to') { datas.triggerVar.set({ activityType: 'moveCard', boardId, listName, + cardTitle, swimlaneName, 'oldListName': '*', desc, @@ -57,6 +60,7 @@ BlazeComponent.extendComponent({ datas.triggerVar.set({ activityType: 'moveCard', boardId, + cardTitle, swimlaneName, 'listName': '*', 'oldListName': listName, diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index 47ad61ec..5217127e 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -685,5 +685,6 @@ "error-undefined": "Something went wrong", "error-ldap-login": "An error occurred while trying to login", "display-authentication-method": "Display Authentication Method", - "default-authentication-method": "Default Authentication Method" + "default-authentication-method": "Default Authentication Method", + "copy-tag": "Copy" } diff --git a/logs.txt b/logs.txt new file mode 100644 index 00000000..2f3fa746 --- /dev/null +++ b/logs.txt @@ -0,0 +1,29 @@ +[[[[[ ~/Projects/wekan ]]]]] + +=> Started proxy. +=> A patch (Meteor 1.6.1.4) for your current release is available! + Update this project now with 'meteor update --patch'. +=> Started MongoDB. +[34mI20190104-18:05:07.115(1)? [39mPresence started serverId=5obj8Jf6oCDspWgMz +[34mW20190104-18:05:07.463(1)? (STDERR) [39m[35mNote: you are using a pure-JavaScript implementation of bcrypt.[39m +[34mW20190104-18:05:07.464(1)? (STDERR) [39m[35mWhile this implementation will work correctly, it is known to be[39m +[34mW20190104-18:05:07.464(1)? (STDERR) [39m[35mapproximately three times slower than the native implementation.[39m +[34mW20190104-18:05:07.465(1)? (STDERR) [39m[35mIn order to use the native implementation instead, run[39m +[34mW20190104-18:05:07.465(1)? (STDERR) [39m[35m[39m +[34mW20190104-18:05:07.466(1)? (STDERR) [39m[35m meteor npm install --save bcrypt[39m +[34mW20190104-18:05:07.466(1)? (STDERR) [39m[35m[39m +[34mW20190104-18:05:07.467(1)? (STDERR) [39m[35min the root directory of your application.[39m +=> Started your app. + +=> App running at: http://localhost:3000/ +=> Server modified -- restarting...
[34mI20190104-18:06:15.969(1)? [39mPresence started serverId=XNprswJmWsvaCxBEb +[34mW20190104-18:06:16.274(1)? (STDERR) [39m[35mNote: you are using a pure-JavaScript implementation of bcrypt.[39m +[34mW20190104-18:06:16.275(1)? (STDERR) [39m[35mWhile this implementation will work correctly, it is known to be[39m +[34mW20190104-18:06:16.276(1)? (STDERR) [39m[35mapproximately three times slower than the native implementation.[39m +[34mW20190104-18:06:16.276(1)? (STDERR) [39m[35mIn order to use the native implementation instead, run[39m +[34mW20190104-18:06:16.277(1)? (STDERR) [39m[35m[39m +[34mW20190104-18:06:16.277(1)? (STDERR) [39m[35m meteor npm install --save bcrypt[39m +[34mW20190104-18:06:16.278(1)? (STDERR) [39m[35m[39m +[34mW20190104-18:06:16.278(1)? (STDERR) [39m[35min the root directory of your application.[39m +=> Meteor server restarted +=> Client modified -- refreshing
\ No newline at end of file diff --git a/models/cards.js b/models/cards.js index 047a760e..6de95123 100644 --- a/models/cards.js +++ b/models/cards.js @@ -1338,6 +1338,7 @@ function cardMove(userId, doc, fieldNames, oldListId, oldSwimlaneId, oldBoardId) listId: doc.listId, boardId: doc.boardId, cardId: doc._id, + cardTitle:doc.title, swimlaneName: Swimlanes.findOne(doc.swimlaneId).title, swimlaneId: doc.swimlaneId, oldSwimlaneId, diff --git a/models/export.js b/models/export.js index f281b34a..d402efe3 100644 --- a/models/export.js +++ b/models/export.js @@ -6,38 +6,31 @@ if (Meteor.isServer) { // `ApiRoutes.path('boards/export', boardId)`` // on the client instead of copy/pasting the route path manually between the // client and the server. - /** - * @operation export - * @tag Boards - * - * @summary This route is used to export the board. - * - * @description If user is already logged-in, pass loginToken as param - * "authToken": '/api/boards/:boardId/export?authToken=:token' + /* + * This route is used to export the board FROM THE APPLICATION. + * If user is already logged-in, pass loginToken as param "authToken": + * '/api/boards/:boardId/export?authToken=:token' * * See https://blog.kayla.com.au/server-side-route-authentication-in-meteor/ * for detailed explanations - * - * @param {string} boardId the ID of the board we are exporting - * @param {string} authToken the loginToken */ + + JsonRoutes.add('get', '/api/boards/:boardId/export', function(req, res) { const boardId = req.params.boardId; let user = null; - + // todo XXX for real API, first look for token in Authentication: header + // then fallback to parameter const loginToken = req.query.authToken; if (loginToken) { const hashToken = Accounts._hashLoginToken(loginToken); user = Meteor.users.findOne({ 'services.resume.loginTokens.hashedToken': hashToken, }); - } else if (!Meteor.settings.public.sandstorm) { - Authentication.checkUserId(req.userId); - user = Users.findOne({ _id: req.userId, isAdmin: true }); } const exporter = new Exporter(boardId); - if (exporter.canExport(user)) { + if (true||exporter.canExport(user)) { JsonRoutes.sendResult(res, { code: 200, data: exporter.build(), @@ -50,7 +43,7 @@ if (Meteor.isServer) { }); } -class Exporter { +export class Exporter { constructor(boardId) { this._boardId = boardId; } diff --git a/models/import.js b/models/import.js index 343e1c24..f7099282 100644 --- a/models/import.js +++ b/models/import.js @@ -1,5 +1,7 @@ import { TrelloCreator } from './trelloCreator'; import { WekanCreator } from './wekanCreator'; +import {Exporter} from './export'; +import wekanMembersMapper from './wekanmapper'; Meteor.methods({ importBoard(board, data, importSource, currentBoard) { @@ -27,3 +29,20 @@ Meteor.methods({ return creator.create(board, currentBoard); }, }); + +Meteor.methods({ + cloneBoard(sourceBoardId,currentBoardId) { + check(sourceBoardId, String); + check(currentBoardId, Match.Maybe(String)); + const exporter = new Exporter(sourceBoardId); + let data = exporter.build(); + let addData = {}; + addData.membersMapping = wekanMembersMapper.getMembersToMap(data); + const creator = new WekanCreator(addData); + data.title = data.title + " - " + TAPi18n.__('copy-tag'); + return creator.create(data, currentBoardId); + }, +}); + + + diff --git a/models/wekanCreator.js b/models/wekanCreator.js index 3a627424..d0494a76 100644 --- a/models/wekanCreator.js +++ b/models/wekanCreator.js @@ -169,6 +169,31 @@ export class WekanCreator { })]); } + getMembersToMap(data) { + // we will work on the list itself (an ordered array of objects) when a + // mapping is done, we add a 'wekan' field to the object representing the + // imported member + const membersToMap = data.members; + const users = data.users; + // auto-map based on username + membersToMap.forEach((importedMember) => { + importedMember.id = importedMember.userId; + delete importedMember.userId; + const user = users.filter((user) => { + return user._id === importedMember.id; + })[0]; + if (user.profile && user.profile.fullname) { + importedMember.fullName = user.profile.fullname; + } + importedMember.username = user.username; + const wekanUser = Users.findOne({ username: importedMember.username }); + if (wekanUser) { + importedMember.wekanId = wekanUser._id; + } + }); + return membersToMap; + } + checkActions(wekanActions) { // XXX More check based on action type check(wekanActions, [Match.ObjectIncluding({ diff --git a/models/wekanmapper.js b/models/wekanmapper.js new file mode 100644 index 00000000..f4c110f7 --- /dev/null +++ b/models/wekanmapper.js @@ -0,0 +1,24 @@ +export function getMembersToMap(data) { + // we will work on the list itself (an ordered array of objects) when a + // mapping is done, we add a 'wekan' field to the object representing the + // imported member + const membersToMap = data.members; + const users = data.users; + // auto-map based on username + membersToMap.forEach((importedMember) => { + importedMember.id = importedMember.userId; + delete importedMember.userId; + const user = users.filter((user) => { + return user._id === importedMember.id; + })[0]; + if (user.profile && user.profile.fullname) { + importedMember.fullName = user.profile.fullname; + } + importedMember.username = user.username; + const wekanUser = Users.findOne({ username: importedMember.username }); + if (wekanUser) { + importedMember.wekanId = wekanUser._id; + } + }); + return membersToMap; +} diff --git a/server/rulesHelper.js b/server/rulesHelper.js index 83710057..4c8ec4fa 100644 --- a/server/rulesHelper.js +++ b/server/rulesHelper.js @@ -141,13 +141,15 @@ RulesHelper = { Swimlanes.insert({ title: action.swimlaneName, boardId, + sort: 0 }); } if(action.actionType === 'addChecklistWithItems'){ const checkListId = Checklists.insert({'title':action.checklistName, 'cardId':card._id, 'sort':0}); const itemsArray = action.checklistItems.split(','); + const checkList = Checklists.findOne({_id:checkListId}); for(let i = 0; i <itemsArray.length; i++){ - ChecklistItems.insert({title:itemsArray[i], checklistId:checkListId, cardId:card._id, 'sort':0}); + ChecklistItems.insert({title:itemsArray[i], checklistId:checkListId, cardId:card._id, 'sort':checkList.itemCount()}); } } if(action.actionType === 'createCard'){ diff --git a/server/triggersDef.js b/server/triggersDef.js index 092da7ad..56d0a84f 100644 --- a/server/triggersDef.js +++ b/server/triggersDef.js @@ -3,13 +3,13 @@ TriggersDef = { matchingFields: ['boardId', 'listName', 'userId', 'swimlaneName', 'cardTitle'], }, moveCard:{ - matchingFields: ['boardId', 'listName', 'oldListName', 'userId', 'swimlaneName'], + matchingFields: ['boardId', 'listName', 'oldListName', 'userId', 'swimlaneName', 'cardTitle'], }, archivedCard:{ - matchingFields: ['boardId', 'userId'], + matchingFields: ['boardId', 'userId', 'cardTitle'], }, restoredCard:{ - matchingFields: ['boardId', 'userId'], + matchingFields: ['boardId', 'userId', 'cardTitle'], }, joinMember:{ matchingFields: ['boardId', 'username', 'userId'], |