Compare commits

...
19 Commits
Author SHA1 Message Date
Lauri Ojansivu dc4a0bb462 v4.84 2021-01-22 17:37:12 +02:00
Lauri Ojansivu a0418b460a Updated translations. 2021-01-22 17:28:22 +02:00
Lauri Ojansivu 0c093b539a Updated ChangeLog. 2021-01-22 16:58:54 +02:00
Lauri Ojansivu 1971037049 Merge pull request #3458 from jrsupplee/issue-2924
Issue 2924: Rules not copied during board copy
2021-01-22 16:57:18 +02:00
Lauri Ojansivu 57af4ae70d Updated ChangeLog. 2021-01-22 16:52:50 +02:00
Lauri Ojansivu 8f553497e4 Tried to fix possible prototype pollution reported by Deepcode.ai.
Thanks to Deepcode.ai and xet7 !
2021-01-22 16:37:42 +02:00
Lauri Ojansivu 0373da44b3 Disable some logs that are not needed anymore.
Thanks to xet7 !
2021-01-22 16:24:47 +02:00
Lauri Ojansivu 1df060b8f6 Merge pull request #3459 from jrsupplee/new-search
Search All Boards: Added list of board, list and color names.
2021-01-22 16:21:01 +02:00
John R. Supplee b249fcbb2e Use the copyBoard method to duplicate a board
* Use `copyBoard` instead of `cloneBoard` to duplicate a board
* Give duplicated boards a unique title by appending number
2021-01-22 14:24:39 +02:00
John R. Supplee ff8a36653a Use a Meteor call to copy a board
The current method was to copy a board on the client side.  But
not all data was available for copying rules.  Moving the copy
function to the server side solves this problem.
2021-01-22 12:49:48 +02:00
John R. Supplee 1c7a9e4de8 Copy rules, triggers and actions when copying a board 2021-01-22 01:27:28 +02:00
John R. Supplee 319783b008 Global search: add new operators
* add operators for due, created and modified
2021-01-21 18:11:09 +02:00
Lauri Ojansivu 0c3b92567e Updated translations. 2021-01-21 13:10:59 +02:00
Lauri Ojansivu 3513a3bd62 Updated ChangeLog. 2021-01-21 12:51:19 +02:00
Lauri Ojansivu a5f6dd6399 Fixed Upper/lowercase errors in some languages due to .toLowerCase.
Thanks to bronger and xet7 !

Fixes #3452
2021-01-21 12:48:15 +02:00
John R. Supplee 7ced6318a5 Global search
* Make some heading translatable
* set focus back to search phrase input after clicking a predicate
* Some spacing issues
2021-01-21 11:55:58 +02:00
John R. Supplee 61c691a267 Add support for clicking label names and board titles 2021-01-21 01:48:24 +02:00
John R. Supplee 52f920db12 Add clickable list titles 2021-01-20 21:52:27 +02:00
John R. Supplee 6d9928ea8f Add color palette and allow selection 2021-01-20 13:15:10 +02:00
77 changed files with 55021 additions and 57518 deletions

No files matched your search

+22
View File
@@ -1,3 +1,25 @@
# v4.84 2021-01-22 Wekan release
This release adds the following new features:
- [Search All Boards: Added list of board, list and color names. Added operators for due, created and modified.
Added support for clicking list titles, label names and board titles. Make some heading translatable.
Set focus back to search phrase input after clicking a predicate. Fixed some spacing issues](https://github.com/wekan/wekan/pull/3459).
Thanks to jrsupplee.
and fixes the following bugs:
- [Fixed Upper/lowercase errors in some languages due to .toLowerCase](https://github.com/wekan/wekan/commit/a5f6dd6399142b3b05e9b6a0d106d931106807d6).
Thanks to bronger and xet7.
- [Tried to fix possible prototype pollution reported by Deepcode.ai](https://github.com/wekan/wekan/commit/8f553497e4f21d44e78243d22d80b18f729a3d6a).
Thanks to Deepcode.ai and xet7.
- [Disable some logs that are not needed anymore](https://github.com/wekan/wekan/commit/0373da44b38f7300f69470fed3cabab9b63c8783).
Thanks to xet7.
- [Rules not copied during board copy](https://github.com/wekan/wekan/pull/3458).
Thanks to jrsupplee.
Thanks to above GitHub users for their contributions and translators for their translations.
# v4.83 2021-01-20 Wekan release
This release adds the following new features:
+1 -1
View File
@@ -1,5 +1,5 @@
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
appVersion: "v4.83.0"
appVersion: "v4.84.0"
files:
userUploads:
- README.md
+6 -2
View File
@@ -124,9 +124,13 @@ BlazeComponent.extendComponent({
},
'click .js-clone-board'(evt) {
Meteor.call(
'cloneBoard',
'copyBoard',
this.currentData()._id,
Session.get('fromBoard'),
{
sort: Boards.find({ archived: false }).count(),
type: 'board',
title: Boards.findOne(this.currentData()._id).copyTitle(),
},
(err, res) => {
if (err) {
this.setError(err.error);
+12 -6
View File
@@ -675,12 +675,18 @@ BlazeComponent.extendComponent({
element.type = 'swimlane';
_id = element.copy(this.boardId);
} else if (this.isBoardTemplateSearch) {
board = Boards.findOne(element.linkedId);
board.sort = Boards.find({ archived: false }).count();
board.type = 'board';
board.title = element.title;
delete board.slug;
_id = board.copy();
Meteor.call(
'copyBoard',
element.linkedId,
{
sort: Boards.find({ archived: false }).count(),
type: 'board',
title: element.title,
},
(err, data) => {
_id = data;
},
);
}
Popup.close();
},
+28 -1
View File
@@ -14,7 +14,14 @@ template(name="globalSearch")
if currentUser
.wrapper
form.global-search-instructions.js-search-query-form
input.global-search-query-input(type="text" name="searchQuery" placeholder="{{_ 'search-example'}}" value="{{ query.get }}" autofocus dir="auto")
input.global-search-query-input(
id="global-search-input"
type="text"
name="searchQuery"
placeholder="{{_ 'search-example'}}"
value="{{ query.get }}"
autofocus dir="auto"
)
if searching.get
+spinner
else if hasResults.get
@@ -32,6 +39,26 @@ template(name="globalSearch")
+resultCard(card)
else
.global-search-instructions
h2 {{_ 'boards' }}
.lists-wrapper
each title in myBoardNames.get
span.card-label.list-title.js-board-title
= title
h2 {{_ 'lists' }}
.lists-wrapper
each title in myLists.get
span.card-label.list-title.js-list-title
= title
h2 {{_ 'label-colors' }}
.palette-colors: each label in labelColors
span.card-label.palette-color.js-label-color(class="card-label-{{label.color}}")
= label.name
if myLabelNames.get.length
h2 {{_ 'label-names' }}
.lists-wrapper
each name in myLabelNames.get
span.card-label.list-title.js-label-name
= name
+viewer
= searchInstructions
+116 -15
View File
@@ -42,6 +42,9 @@ BlazeComponent.extendComponent({
this.query = new ReactiveVar('');
this.resultsHeading = new ReactiveVar('');
this.searchLink = new ReactiveVar(null);
this.myLists = new ReactiveVar([]);
this.myLabelNames = new ReactiveVar([]);
this.myBoardNames = new ReactiveVar([]);
this.queryParams = null;
this.parsingErrors = [];
this.resultsCount = 0;
@@ -55,6 +58,25 @@ BlazeComponent.extendComponent({
// }
// // eslint-disable-next-line no-console
// console.log('colorMap:', this.colorMap);
Meteor.call('myLists', (err, data) => {
if (!err) {
this.myLists.set(data);
}
});
Meteor.call('myLabelNames', (err, data) => {
if (!err) {
this.myLabelNames.set(data);
}
});
Meteor.call('myBoardNames', (err, data) => {
if (!err) {
this.myBoardNames.set(data);
}
});
Meteor.subscribe('setting');
if (Session.get('globalQuery')) {
this.searchAllBoards(Session.get('globalQuery'));
@@ -111,11 +133,13 @@ BlazeComponent.extendComponent({
messages.push({ tag: 'list-title-not-found', value: list });
});
this.queryErrors.notFound.labels.forEach(label => {
const color = TAPi18n.__(`color-${label}`);
if (color) {
const color = Object.entries(this.colorMap)
.filter(value => value[1] === label)
.map(value => value[0]);
if (color.length) {
messages.push({
tag: 'label-color-not-found',
value: color,
value: color[0],
});
} else {
messages.push({ tag: 'label-not-found', value: label });
@@ -185,9 +209,12 @@ BlazeComponent.extendComponent({
operatorMap[TAPi18n.__('operator-assignee')] = 'assignees';
operatorMap[TAPi18n.__('operator-assignee-abbrev')] = 'assignees';
operatorMap[TAPi18n.__('operator-is')] = 'is';
operatorMap[TAPi18n.__('operator-due')] = 'dueAt';
operatorMap[TAPi18n.__('operator-created')] = 'createdAt';
operatorMap[TAPi18n.__('operator-modified')] = 'modifiedAt';
// eslint-disable-next-line no-console
// console.log('operatorMap:', operatorMap);
console.log('operatorMap:', operatorMap);
const params = {
boards: [],
swimlanes: [],
@@ -197,6 +224,9 @@ BlazeComponent.extendComponent({
assignees: [],
labels: [],
is: [],
dueAt: null,
createdAt: null,
modifiedAt: null,
};
let text = '';
@@ -217,19 +247,46 @@ BlazeComponent.extendComponent({
} else {
op = m.groups.abbrev;
}
if (op in operatorMap) {
let value = m.groups.value;
if (operatorMap[op] === 'labels') {
if (value in this.colorMap) {
value = this.colorMap[value];
if (op !== "__proto__") {
if (op in operatorMap) {
let value = m.groups.value;
if (operatorMap[op] === 'labels') {
if (value in this.colorMap) {
value = this.colorMap[value];
}
} else if (
['dueAt', 'createdAt', 'modifiedAt'].includes(operatorMap[op])
) {
const days = parseInt(value, 10);
if (isNaN(days)) {
if (['day', 'week', 'month', 'quarter', 'year'].includes(value)) {
value = moment()
.subtract(1, value)
.format();
} else {
this.parsingErrors.push({
tag: 'operator-number-expected',
value: { operator: op, value },
});
value = null;
}
} else {
value = moment()
.subtract(days, 'days')
.format();
}
}
if (Array.isArray(params[operatorMap[op]])) {
params[operatorMap[op]].push(value);
} else {
params[operatorMap[op]] = value;
}
} else {
this.parsingErrors.push({
tag: 'operator-unknown-error',
value: op,
});
}
params[operatorMap[op]].push(value);
} else {
this.parsingErrors.push({
tag: 'operator-unknown-error',
value: op,
});
}
continue;
}
@@ -355,6 +412,14 @@ BlazeComponent.extendComponent({
return text;
},
labelColors() {
return Boards.simpleSchema()._schema['labels.$.color'].allowedValues.map(
color => {
return { color, name: TAPi18n.__(`color-${color}`) };
},
);
},
events() {
return [
{
@@ -362,6 +427,42 @@ BlazeComponent.extendComponent({
evt.preventDefault();
this.searchAllBoards(evt.target.searchQuery.value);
},
'click .js-label-color'(evt) {
evt.preventDefault();
this.query.set(
`${this.query.get()} ${TAPi18n.__('operator-label')}:"${
evt.currentTarget.textContent
}"`,
);
document.getElementById('global-search-input').focus();
},
'click .js-board-title'(evt) {
evt.preventDefault();
this.query.set(
`${this.query.get()} ${TAPi18n.__('operator-board')}:"${
evt.currentTarget.textContent
}"`,
);
document.getElementById('global-search-input').focus();
},
'click .js-list-title'(evt) {
evt.preventDefault();
this.query.set(
`${this.query.get()} ${TAPi18n.__('operator-list')}:"${
evt.currentTarget.textContent
}"`,
);
document.getElementById('global-search-input').focus();
},
'click .js-label-name'(evt) {
evt.preventDefault();
this.query.set(
`${this.query.get()} ${TAPi18n.__('operator-label')}:"${
evt.currentTarget.textContent
}"`,
);
document.getElementById('global-search-input').focus();
},
},
];
},
+9
View File
@@ -78,6 +78,12 @@
margin-left: auto
line-height: 150%
.global-search-instructions h1
margin-top: 2rem;
.global-search-instructions h2
margin-top: 1rem;
.global-search-query-input
width: 90% !important
margin-right: auto
@@ -95,3 +101,6 @@ code
background-color: lightgrey
padding: 0.1rem !important
font-size: 0.7rem !important
.list-title
background-color: darkgray
+5 -5
View File
@@ -166,16 +166,16 @@ Template.memberPopup.helpers({
const noComments = currentBoard.hasNoComments(this.userId);
const worker = currentBoard.hasWorker(this.userId);
if (commentOnly) {
return TAPi18n.__('comment-only').toLowerCase();
return TAPi18n.__('comment-only');
} else if (noComments) {
return TAPi18n.__('no-comments').toLowerCase();
return TAPi18n.__('no-comments');
} else if (worker) {
return TAPi18n.__('worker').toLowerCase();
return TAPi18n.__('worker');
} else {
return TAPi18n.__(type).toLowerCase();
return TAPi18n.__(type);
}
} else {
return TAPi18n.__(type).toLowerCase();
return TAPi18n.__(type);
}
},
isInvited() {
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+7 -1
View File
@@ -895,7 +895,11 @@
"operator-assignee": "assignee",
"operator-assignee-abbrev": "a",
"operator-is": "is",
"operator-due": "due",
"operator-created": "created",
"operator-modified": "modified",
"operator-unknown-error": "%s is not an operator",
"operator-number-expected": "operator __operator__ expected a number, got '__value__'",
"heading-notes": "Notes",
"globalSearch-instructions-heading": "Search Instructions",
"globalSearch-instructions-description": "Searches can include operators to refine the search. Operators are specified by writing the operator name and value separated by a colon. For example, an operator specification of `list:Blocked` would limit the search to cards that are contained in a list named *Blocked*. If the value contains spaces or special characters it must be enclosed in quotation marks (e.g. `__operator_list__:\"To Review\"`).",
@@ -916,5 +920,7 @@
"globalSearch-instructions-notes-5": "Currently archived cards are not searched.",
"link-to-search": "Link to this search",
"excel-font": "Arial",
"number": "Number"
"number": "Number",
"label-colors": "Label Colors",
"label-names": "Label Names"
}
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+924 -918
View File
File diff suppressed because it is too large. Load diff
+66
View File
@@ -508,6 +508,7 @@ Boards.helpers({
copy() {
const oldId = this._id;
delete this._id;
delete this.slug;
const _id = Boards.insert(this);
// Copy all swimlanes in board
@@ -537,7 +538,52 @@ Boards.helpers({
},
});
});
// copy rules, actions, and triggers
const actionsMap = {};
Actions.find({ boardId: oldId }).forEach(action => {
const id = action._id;
delete action._id;
action.boardId = _id;
actionsMap[id] = Actions.insert(action);
});
const triggersMap = {};
Triggers.find({ boardId: oldId }).forEach(trigger => {
const id = trigger._id;
delete trigger._id;
trigger.boardId = _id;
triggersMap[id] = Triggers.insert(trigger);
});
Rules.find({ boardId: oldId }).forEach(rule => {
delete rule._id;
rule.boardId = _id;
rule.actionId = actionsMap[rule.actionId];
rule.triggerId = triggersMap[rule.triggerId];
Rules.insert(rule);
});
},
/**
* Return a unique title based on the current title
*
* @returns {string|null}
*/
copyTitle() {
const m = this.title.match(/^(?<title>.*?)\s*(\[(?<num>\d+)]\s*$|\s*$)/);
const title = m.groups.title;
let num = 0;
Boards.find({ title: new RegExp(`^${title}\\s*\\[\\d+]\\s*$`) }).forEach(
board => {
const m = board.title.match(/^(?<title>.*?)\s*\[(?<num>\d+)]\s*$/);
if (m) {
const n = parseInt(m.groups.num, 10);
num = num < n ? n : num;
}
},
);
return `${title} [${num + 1}]`;
},
/**
* Is supplied user authorized to view this board?
*/
@@ -1343,6 +1389,26 @@ if (Meteor.isServer) {
},
});
},
myLabelNames() {
let names = [];
Boards.userBoards(Meteor.userId()).forEach(board => {
names = names.concat(
board.labels
.filter(label => !!label.name)
.map(label => {
return label.name;
}),
);
});
return _.uniq(names).sort();
},
myBoardNames() {
return _.uniq(
Boards.userBoards(Meteor.userId()).map(board => {
return board.title;
}),
).sort();
},
});
Meteor.methods({
+15 -1
View File
@@ -1954,6 +1954,18 @@ Cards.globalSearch = queryParams => {
selector.listId.$in = queryLists;
}
if (queryParams.dueAt !== null) {
selector.dueAt = { $gte: new Date(queryParams.dueAt) };
}
if (queryParams.createdAt !== null) {
selector.createdAt = { $gte: new Date(queryParams.createdAt) };
}
if (queryParams.modifiedAt !== null) {
selector.modifiedAt = { $gte: new Date(queryParams.modifiedAt) };
}
const queryMembers = [];
const queryAssignees = [];
if (queryParams.users.length) {
@@ -2094,13 +2106,15 @@ Cards.globalSearch = queryParams => {
assignees: 1,
colors: 1,
dueAt: 1,
createdAt: 1,
modifiedAt: 1,
labelIds: 1,
},
limit: 50,
});
// eslint-disable-next-line no-console
// console.log('count:', cards.count());
//console.log('count:', cards.count());
return { cards, errors };
};
+14
View File
@@ -362,6 +362,20 @@ Meteor.methods({
const list = Lists.findOne({ _id: listId });
list.toggleSoftLimit(!list.getWipLimit('soft'));
},
myLists() {
// my lists
return _.uniq(
Lists.find(
{ boardId: { $in: Boards.userBoardIds(this.userId) } },
{ fields: { title: 1 } },
)
.fetch()
.map(list => {
return list.title;
}),
).sort();
},
});
Lists.hookOptions.after.update = { fetchPrevious: false };
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "wekan",
"version": "v4.83.0",
"version": "v4.84.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "wekan",
"version": "v4.83.0",
"version": "v4.84.0",
"description": "Open-Source kanban",
"private": true,
"scripts": {
+183 -2696
View File
File diff suppressed because it is too large. Load diff
+1 -624
View File
@@ -1,7 +1,7 @@
swagger: '2.0'
info:
title: Wekan REST API
version: v4.83
version: v4.84
description: |
The REST API allows you to control and extend Wekan with ease.
@@ -134,198 +134,6 @@ paths:
description: |
Error in registration
/api/boards:
get:
operationId: get_public_boards
summary: Get all public boards
tags:
- Boards
produces:
- application/json
security:
- UserSecurity: []
responses:
'200':
description: |-
200 response
schema:
type: array
items:
type: object
properties:
_id:
type: string
title:
type: string
post:
operationId: new_board
summary: Create a board
description: |
This allows to create a board.
The color has to be chosen between `belize`, `nephritis`, `pomegranate`,
`pumpkin`, `wisteria`, `moderatepink`, `strongcyan`,
`limegreen`, `midnight`, `dark`, `relax`, `corteza`:
<img src="https://wekan.github.io/board-colors.png" width="40%" alt="Wekan logo" />
tags:
- Boards
consumes:
- multipart/form-data
- application/json
parameters:
- name: title
in: formData
description: |
the new title of the board
type: string
required: true
- name: owner
in: formData
description: |
"ABCDE12345" <= User ID in Wekan.
(Not username or email)
type: string
required: true
- name: isAdmin
in: formData
description: |
is the owner an admin of the board (default true)
type: boolean
required: false
- name: isActive
in: formData
description: |
is the board active (default true)
type: boolean
required: false
- name: isNoComments
in: formData
description: |
disable comments (default false)
type: boolean
required: false
- name: isCommentOnly
in: formData
description: |
only enable comments (default false)
type: boolean
required: false
- name: isWorker
in: formData
description: |
only move cards, assign himself to card and comment (default false)
type: boolean
required: false
- name: permission
in: formData
description: |
"private" board <== Set to "public" if you
want public Wekan board
type: string
required: false
- name: color
in: formData
description: |
the color of the board
type: string
required: false
produces:
- application/json
security:
- UserSecurity: []
responses:
'200':
description: |-
200 response
schema:
type: object
properties:
_id:
type: string
defaultSwimlaneId:
type: string
/api/boards/{board}:
get:
operationId: get_board
summary: Get the board with that particular ID
tags:
- Boards
parameters:
- name: board
in: path
description: |
the ID of the board to retrieve the data
type: string
required: true
produces:
- application/json
security:
- UserSecurity: []
responses:
'200':
description: |-
200 response
schema:
$ref: "#/definitions/Boards"
delete:
operationId: delete_board
summary: Delete a board
tags:
- Boards
parameters:
- name: board
in: path
description: |
the ID of the board
type: string
required: true
produces:
- application/json
security:
- UserSecurity: []
responses:
'200':
description: |-
200 response
/api/boards/{board}/attachments:
get:
operationId: get_board_attachments
summary: Get the list of attachments of a board
tags:
- Boards
parameters:
- name: board
in: path
description: |
the board ID
type: string
required: true
produces:
- application/json
security:
- UserSecurity: []
responses:
'200':
description: |-
200 response
schema:
type: array
items:
type: object
properties:
attachmentId:
type: string
attachmentName:
type: string
attachmentType:
type: string
cardId:
type: string
listId:
type: string
swimlaneId:
type: string
/api/boards/{board}/cards/{card}/checklists:
get:
operationId: get_all_checklists
@@ -1288,40 +1096,6 @@ paths:
200 response
schema:
$ref: "#/definitions/Integrations"
/api/boards/{board}/labels:
put:
operationId: add_board_label
summary: Add a label to a board
description: |
If the board doesn't have the name/color label, this function
adds the label to the board.
tags:
- Boards
consumes:
- multipart/form-data
- application/json
parameters:
- name: label
in: formData
description: the label value
type: string
required: true
- name: board
in: path
description: |
the board
type: string
required: true
produces:
- application/json
security:
- UserSecurity: []
responses:
'200':
description: |-
200 response
schema:
type: string
/api/boards/{board}/lists:
get:
operationId: get_all_lists
@@ -1803,61 +1577,6 @@ paths:
properties:
_id:
type: string
/api/boards/{board}/members/{member}:
post:
operationId: set_board_member_permission
summary: Change the permission of a member of a board
tags:
- Boards
- Users
consumes:
- multipart/form-data
- application/json
parameters:
- name: isAdmin
in: formData
description: |
admin capability
type: boolean
required: true
- name: isNoComments
in: formData
description: |
NoComments capability
type: boolean
required: true
- name: isCommentOnly
in: formData
description: |
CommentsOnly capability
type: boolean
required: true
- name: isWorker
in: formData
description: |
Worker capability
type: boolean
required: true
- name: board
in: path
description: |
the ID of the board that we are changing
type: string
required: true
- name: member
in: path
description: |
the ID of the user to change permissions
type: string
required: true
produces:
- application/json
security:
- UserSecurity: []
responses:
'200':
description: |-
200 response
/api/boards/{board}/members/{user}/add:
post:
operationId: add_board_member
@@ -2314,349 +2033,7 @@ paths:
properties:
_id:
type: string
/api/users/{user}/boards:
get:
operationId: get_boards_from_user
summary: Get all boards attached to a user
tags:
- Boards
parameters:
- name: user
in: path
description: |
the ID of the user to retrieve the data
type: string
required: true
produces:
- application/json
security:
- UserSecurity: []
responses:
'200':
description: |-
200 response
schema:
type: array
items:
type: object
properties:
_id:
type: string
title:
type: string
definitions:
Boards:
type: object
description: This is a Board.
properties:
title:
description: |
The title of the board
type: string
slug:
description: |
The title slugified.
type: string
archived:
description: |
Is the board archived?
type: boolean
createdAt:
description: |
Creation time of the board
type: string
modifiedAt:
description: |
Last modification time of the board
type: string
x-nullable: true
stars:
description: |
How many stars the board has
type: number
labels:
description: |
List of labels attached to a board
type: array
items:
$ref: "#/definitions/BoardsLabels"
members:
description: |
List of members of a board
type: array
items:
$ref: "#/definitions/BoardsMembers"
permission:
description: |
visibility of the board
type: string
enum:
- public
- private
color:
description: |
The color of the board.
type: string
enum:
- belize
- nephritis
- pomegranate
- pumpkin
- wisteria
- moderatepink
- strongcyan
- limegreen
- midnight
- dark
- relax
- corteza
- clearblue
- natural
- modern
- moderndark
description:
description: |
The description of the board
type: string
x-nullable: true
subtasksDefaultBoardId:
description: |
The default board ID assigned to subtasks.
type: string
x-nullable: true
subtasksDefaultListId:
description: |
The default List ID assigned to subtasks.
type: string
x-nullable: true
dateSettingsDefaultBoardId:
type: string
x-nullable: true
dateSettingsDefaultListId:
type: string
x-nullable: true
allowsSubtasks:
description: |
Does the board allows subtasks?
type: boolean
allowsAttachments:
description: |
Does the board allows attachments?
type: boolean
allowsChecklists:
description: |
Does the board allows checklists?
type: boolean
allowsComments:
description: |
Does the board allows comments?
type: boolean
allowsDescriptionTitle:
description: |
Does the board allows description title?
type: boolean
allowsDescriptionText:
description: |
Does the board allows description text?
type: boolean
allowsActivities:
description: |
Does the board allows comments?
type: boolean
allowsLabels:
description: |
Does the board allows labels?
type: boolean
allowsAssignee:
description: |
Does the board allows assignee?
type: boolean
allowsMembers:
description: |
Does the board allows members?
type: boolean
allowsRequestedBy:
description: |
Does the board allows requested by?
type: boolean
allowsAssignedBy:
description: |
Does the board allows requested by?
type: boolean
allowsReceivedDate:
description: |
Does the board allows received date?
type: boolean
allowsStartDate:
description: |
Does the board allows start date?
type: boolean
allowsEndDate:
description: |
Does the board allows end date?
type: boolean
allowsDueDate:
description: |
Does the board allows due date?
type: boolean
presentParentTask:
description: |
Controls how to present the parent task:
- `prefix-with-full-path`: add a prefix with the full path
- `prefix-with-parent`: add a prefisx with the parent name
- `subtext-with-full-path`: add a subtext with the full path
- `subtext-with-parent`: add a subtext with the parent name
- `no-parent`: does not show the parent at all
type: string
enum:
- prefix-with-full-path
- prefix-with-parent
- subtext-with-full-path
- subtext-with-parent
- no-parent
x-nullable: true
startAt:
description: |
Starting date of the board.
type: string
x-nullable: true
dueAt:
description: |
Due date of the board.
type: string
x-nullable: true
endAt:
description: |
End date of the board.
type: string
x-nullable: true
spentTime:
description: |
Time spent in the board.
type: number
x-nullable: true
isOvertime:
description: |
Is the board overtimed?
type: boolean
x-nullable: true
type:
description: |
The type of board
type: string
sort:
description: |
Sort value
type: number
required:
- title
- slug
- archived
- createdAt
- stars
- labels
- members
- permission
- color
- allowsSubtasks
- allowsAttachments
- allowsChecklists
- allowsComments
- allowsDescriptionTitle
- allowsDescriptionText
- allowsActivities
- allowsLabels
- allowsAssignee
- allowsMembers
- allowsRequestedBy
- allowsAssignedBy
- allowsReceivedDate
- allowsStartDate
- allowsEndDate
- allowsDueDate
- type
- sort
BoardsLabels:
type: object
properties:
_id:
description: |
Unique id of a label
type: string
name:
description: |
Name of a label
type: string
color:
description: |
color of a label.
Can be amongst `green`, `yellow`, `orange`, `red`, `purple`,
`blue`, `sky`, `lime`, `pink`, `black`,
`silver`, `peachpuff`, `crimson`, `plum`, `darkgreen`,
`slateblue`, `magenta`, `gold`, `navy`, `gray`,
`saddlebrown`, `paleturquoise`, `mistyrose`, `indigo`
type: string
enum:
- green
- yellow
- orange
- red
- purple
- blue
- sky
- lime
- pink
- black
- silver
- peachpuff
- crimson
- plum
- darkgreen
- slateblue
- magenta
- gold
- navy
- gray
- saddlebrown
- paleturquoise
- mistyrose
- indigo
required:
- _id
- color
BoardsMembers:
type: object
properties:
userId:
description: |
The uniq ID of the member
type: string
isAdmin:
description: |
Is the member an admin of the board?
type: boolean
isActive:
description: |
Is the member active?
type: boolean
isNoComments:
description: |
Is the member not allowed to make comments
type: boolean
isCommentOnly:
description: |
Is the member only allowed to comment on the board
type: boolean
isWorker:
description: |
Is the member only allowed to move card, assign himself to card and comment
type: boolean
required:
- userId
- isAdmin
- isActive
CardComments:
type: object
description: A comment on a card
+2 -2
View File
@@ -22,10 +22,10 @@ const pkgdef :Spk.PackageDefinition = (
appTitle = (defaultText = "Wekan"),
# The name of the app as it is displayed to the user.
appVersion = 483,
appVersion = 484,
# Increment this for every release.
appMarketingVersion = (defaultText = "4.83.0~2021-01-20"),
appMarketingVersion = (defaultText = "4.84.0~2021-01-22"),
# Human-readable presentation of the app version.
minUpgradableAppVersion = 0,
+16
View File
@@ -209,3 +209,19 @@ Meteor.publishRelations('board', function(boardId, isArchived) {
return this.ready();
});
Meteor.methods({
copyBoard(boardId, properties) {
check(boardId, String);
check(properties, Object);
const board = Boards.findOne(boardId);
if (board) {
for (const key in properties) {
board[key] = properties[key];
}
return board.copy();
}
return null;
},
});