diff options
-rw-r--r-- | CHANGELOG.md | 17 | ||||
-rw-r--r-- | Dockerfile | 25 | ||||
-rw-r--r-- | client/components/cards/cardDetails.js | 33 | ||||
-rw-r--r-- | client/components/lists/listHeader.js | 7 | ||||
-rw-r--r-- | client/components/users/userHeader.jade | 2 | ||||
-rw-r--r-- | i18n/es-AR.i18n.json | 92 | ||||
-rw-r--r-- | i18n/es.i18n.json | 2 | ||||
-rw-r--r-- | models/checklistItems.js | 50 | ||||
-rw-r--r-- | models/checklists.js | 116 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | sandstorm-pkgdef.capnp | 4 | ||||
-rw-r--r-- | server/authentication.js | 7 | ||||
-rw-r--r-- | snapcraft.yaml | 20 |
13 files changed, 253 insertions, 124 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index e0d1e55a..d9fa04d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +# v0.84 2018-04-16 Wekan release + +This release adds the following new features: + +- [Add Checklist Items REST API](https://github.com/wekan/wekan/commit/9eef5112dc1c1c30590d19fbfd2f615714112a3f). + +and fixes the following bugs: + +- [Fix Node Fibers 100% CPU issue](https://github.com/wekan/wekan/commit/e26a4824cfb119a15767c4827190a6b9ab65b904); +- [Plus button on a Swimlane row, always add an element on the first row](https://github.com/wekan/wekan/issues/1577); +- [Fix Checklist REST API](https://github.com/wekan/wekan/commit/9eef5112dc1c1c30590d19fbfd2f615714112a3f); +- [Fix Disabling "show cards count" not possible, now zero means disable](https://github.com/wekan/wekan/issues/1570); +- [Fix Checklist not copied when copied a card and Copy Checklist Template to Many Cards](https://github.com/wekan/wekan/issues/1565); +- [Fix Filter cards hides checklist items](https://github.com/wekan/wekan/issues/1561). + +Thanks to GitHub users andresmanelli, kentonv and xet7 for their contributions. + # v0.83 2018-04-12 Wekan release - Updated translations: Czech and French. @@ -14,7 +14,7 @@ ARG SRC_PATH # Set the environment variables (defaults where required) # paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303 ENV BUILD_DEPS="apt-utils gnupg gosu wget curl bzip2 build-essential python git ca-certificates gcc-7 paxctl" -ENV NODE_VERSION ${NODE_VERSION:-v8.9.3} +ENV NODE_VERSION ${NODE_VERSION:-v8.11.1} ENV METEOR_RELEASE ${METEOR_RELEASE:-1.6.0.1} ENV USE_EDGE ${USE_EDGE:-false} ENV METEOR_EDGE ${METEOR_EDGE:-1.5-beta.17} @@ -68,6 +68,29 @@ RUN \ tar xvzf node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz && \ rm node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz && \ mv node-${NODE_VERSION}-${ARCHITECTURE} /opt/nodejs && \ + \ + # Remove original node, use Fibers 100% CPU usage issue patched node + rm /opt/nodejs/bin/node && \ + # Node Fibers 100% CPU usage issue: + # https://github.com/wekan/wekan-mongodb/issues/2#issuecomment-381453161 + # https://github.com/meteor/meteor/issues/9796#issuecomment-381676326 + # https://github.com/sandstorm-io/sandstorm/blob/0f1fec013fe7208ed0fd97eb88b31b77e3c61f42/shell/server/00-startup.js#L99-L129 + # Also see beginning of wekan/server/authentication.js + # import Fiber from "fibers"; + # Fiber.poolSize = 1e9; + # Download node version 8.11.1 that has fix included, node binary copied from Sandstorm + # Description at https://releases.wekan.team/node.txt + # SHA256SUM: 18c99d5e79e2fe91e75157a31be30e5420787213684d4048eb91e602e092725d + echo "18c99d5e79e2fe91e75157a31be30e5420787213684d4048eb91e602e092725d node" >> node-SHASUMS256.txt.asc && \ + wget https://releases.wekan.team/node && \ + # Verify Fibers patched node authenticity + echo "Fibers patched node authenticity:" && \ + grep node node-SHASUMS256.txt.asc | shasum -a 256 -c - && \ + rm -f node-SHASUMS256.txt.asc && \ + chmod +x node && \ + mv node /opt/nodejs/bin/ && \ + \ + # Create symlinks ln -s /opt/nodejs/bin/node /usr/bin/node && \ ln -s /opt/nodejs/bin/npm /usr/bin/npm && \ \ diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js index 421cef53..cdd027e6 100644 --- a/client/components/cards/cardDetails.js +++ b/client/components/cards/cardDetails.js @@ -343,9 +343,16 @@ Template.copyCardPopup.events({ cursor.forEach(function() { 'use strict'; const checklist = arguments[0]; + const checklistId = checklist._id; checklist.cardId = _id; checklist._id = null; - Checklists.insert(checklist); + const newChecklistId = Checklists.insert(checklist); + ChecklistItems.find({checklistId}).forEach(function(item) { + item._id = null; + item.checklistId = newChecklistId; + item.cardId = _id; + ChecklistItems.insert(item); + }); }); // copy card comments @@ -363,17 +370,20 @@ Template.copyCardPopup.events({ }); Template.copyChecklistToManyCardsPopup.events({ - 'click .js-select-list' (evt) { + 'click .js-done' () { const card = Cards.findOne(Session.get('currentCard')); const oldId = card._id; card._id = null; - card.listId = this._id; - const list = Lists.findOne(card.listId); - card.boardId = list.boardId; - const textarea = $(evt.currentTarget).parents('.content').find('textarea'); + const lSelect = $('.js-select-lists')[0]; + card.listId = lSelect.options[lSelect.selectedIndex].value; + const slSelect = $('.js-select-swimlanes')[0]; + card.swimlaneId = slSelect.options[slSelect.selectedIndex].value; + const bSelect = $('.js-select-boards')[0]; + card.boardId = bSelect.options[bSelect.selectedIndex].value; + const textarea = $('#copy-card-title'); const titleEntry = textarea.val().trim(); // insert new card to the bottom of new list - card.sort = Lists.findOne(this._id).cards().count(); + card.sort = Lists.findOne(card.listId).cards().count(); if (titleEntry) { const titleList = JSON.parse(titleEntry); @@ -394,9 +404,16 @@ Template.copyChecklistToManyCardsPopup.events({ cursor.forEach(function() { 'use strict'; const checklist = arguments[0]; + const checklistId = checklist._id; checklist.cardId = _id; checklist._id = null; - Checklists.insert(checklist); + const newChecklistId = Checklists.insert(checklist); + ChecklistItems.find({checklistId}).forEach(function(item) { + item._id = null; + item.checklistId = newChecklistId; + item.cardId = _id; + ChecklistItems.insert(item); + }); }); // copy card comments diff --git a/client/components/lists/listHeader.js b/client/components/lists/listHeader.js index 60dd6a34..4b6bf196 100644 --- a/client/components/lists/listHeader.js +++ b/client/components/lists/listHeader.js @@ -28,14 +28,15 @@ BlazeComponent.extendComponent({ }, showCardsCountForList(count) { - return count > this.limitToShowCardsCount(); + const limit = this.limitToShowCardsCount(); + return limit > 0 && count > limit; }, events() { return [{ 'click .js-open-list-menu': Popup.open('listAction'), - 'click .js-add-card' () { - const listDom = document.getElementById(`js-list-${this.currentData()._id}`); + 'click .js-add-card' (evt) { + const listDom = $(evt.target).parents(`#js-list-${this.currentData()._id}`)[0]; const listComponent = BlazeComponent.getComponentForElement(listDom); listComponent.openForm({ position: 'top', diff --git a/client/components/users/userHeader.jade b/client/components/users/userHeader.jade index 096745ad..4ac59f45 100644 --- a/client/components/users/userHeader.jade +++ b/client/components/users/userHeader.jade @@ -86,5 +86,5 @@ template(name="changeSettingsPopup") li label.bold | {{_ 'show-cards-minimum-count'}} - input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="1" max="99" onkeydown="return false") + input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="0" max="99" onkeydown="return false") input.js-apply-show-cards-at.left(type="submit" value="{{_ 'apply'}}") diff --git a/i18n/es-AR.i18n.json b/i18n/es-AR.i18n.json index 0d0e53e1..e9a7d38c 100644 --- a/i18n/es-AR.i18n.json +++ b/i18n/es-AR.i18n.json @@ -9,10 +9,10 @@ "act-createCard": "agregada __card__ a __list__", "act-createList": "agregada __list__ a __board__", "act-addBoardMember": "agregado __member__ a __board__", - "act-archivedBoard": "__board__ moved to Recycle Bin", - "act-archivedCard": "__card__ moved to Recycle Bin", - "act-archivedList": "__list__ moved to Recycle Bin", - "act-archivedSwimlane": "__swimlane__ moved to Recycle Bin", + "act-archivedBoard": "__board__ movido a Papelera de Reciclaje", + "act-archivedCard": "__card__ movido a Papelera de Reciclaje", + "act-archivedList": "__list__ movido a Papelera de Reciclaje", + "act-archivedSwimlane": "__swimlane__ movido a Papelera de Reciclaje", "act-importBoard": "__board__ importado", "act-importCard": "__card__ importada", "act-importList": "__list__ importada", @@ -27,7 +27,7 @@ "activities": "Actividades", "activity": "Actividad", "activity-added": "agregadas %s a %s", - "activity-archived": "%s moved to Recycle Bin", + "activity-archived": "%s movido a Papelera de Reciclaje", "activity-attached": "adjuntadas %s a %s", "activity-created": "creadas %s", "activity-excluded": "excluidas %s de %s", @@ -45,7 +45,7 @@ "add-attachment": "Agregar Adjunto", "add-board": "Agregar Tablero", "add-card": "Agregar Tarjeta", - "add-swimlane": "Add Swimlane", + "add-swimlane": "Agregar Calle", "add-checklist": "Agregar Lista de Tareas", "add-checklist-item": "Agregar ítem a lista de tareas", "add-cover": "Agregar Portadas", @@ -64,19 +64,19 @@ "and-n-other-card_plural": "Y __count__ otras tarjetas", "apply": "Aplicar", "app-is-offline": "Wekan está cargándose, por favor espere. Refrescar la página va a causar pérdida de datos. Si Wekan no se carga, por favor revise que el servidor de Wekan no se haya detenido.", - "archive": "Move to Recycle Bin", - "archive-all": "Move All to Recycle Bin", - "archive-board": "Move Board to Recycle Bin", - "archive-card": "Move Card to Recycle Bin", - "archive-list": "Move List to Recycle Bin", - "archive-swimlane": "Move Swimlane to Recycle Bin", - "archive-selection": "Move selection to Recycle Bin", - "archiveBoardPopup-title": "Move Board to Recycle Bin?", - "archived-items": "Recycle Bin", - "archived-boards": "Boards in Recycle Bin", + "archive": "Mover a Papelera de Reciclaje", + "archive-all": "Mover Todo a la Papelera de Reciclaje", + "archive-board": "Mover Tablero a la Papelera de Reciclaje", + "archive-card": "Mover Tarjeta a la Papelera de Reciclaje", + "archive-list": "Mover Lista a la Papelera de Reciclaje", + "archive-swimlane": "Mover Calle a la Papelera de Reciclaje", + "archive-selection": "Mover selección a la Papelera de Reciclaje", + "archiveBoardPopup-title": "¿Mover Tablero a la Papelera de Reciclaje?", + "archived-items": "Papelera de Reciclaje", + "archived-boards": "Tableros en la Papelera de Reciclaje", "restore-board": "Restaurar Tablero", - "no-archived-boards": "No Boards in Recycle Bin.", - "archives": "Recycle Bin", + "no-archived-boards": "No hay tableros en la Papelera de Reciclaje", + "archives": "Papelera de Reciclaje", "assign-member": "Asignar miembro", "attached": "adjunto(s)", "attachment": "Adjunto", @@ -97,16 +97,16 @@ "boardChangeWatchPopup-title": "Alternar Seguimiento", "boardMenuPopup-title": "Menú del Tablero", "boards": "Tableros", - "board-view": "Board View", - "board-view-swimlanes": "Swimlanes", + "board-view": "Vista de Tablero", + "board-view-swimlanes": "Calles", "board-view-lists": "Listas", "bucket-example": "Como \"Lista de Contenedores\" por ejemplo", "cancel": "Cancelar", - "card-archived": "This card is moved to Recycle Bin.", + "card-archived": "Esta tarjeta es movida a la Papelera de Reciclaje", "card-comments-title": "Esta tarjeta tiene %s comentario.", "card-delete-notice": "Borrar es permanente. Perderás todas las acciones asociadas con esta tarjeta.", "card-delete-pop": "Todas las acciones van a ser eliminadas del agregador de actividad y no podrás re-abrir la tarjeta. No hay deshacer.", - "card-delete-suggest-archive": "You can move a card Recycle Bin to remove it from the board and preserve the activity.", + "card-delete-suggest-archive": "Tu puedes mover una tarjeta a la Papelera de Reciclaje para removerla del tablero y preservar la actividad.", "card-due": "Vence", "card-due-on": "Vence en", "card-spent": "Tiempo Empleado", @@ -141,7 +141,7 @@ "clipboard": "Portapapeles o arrastrar y soltar", "close": "Cerrar", "close-board": "Cerrar Tablero", - "close-board-pop": "You will be able to restore the board by clicking the “Recycle Bin” button from the home header.", + "close-board-pop": "Podrás restaurar el tablero apretando el botón \"Papelera de Reciclaje\" del encabezado en inicio.", "color-black": "negro", "color-blue": "azul", "color-green": "verde", @@ -160,9 +160,9 @@ "confirm-checklist-delete-dialog": "¿Estás segur@ que querés borrar la lista de ítems?", "copy-card-link-to-clipboard": "Copiar enlace a tarjeta en el portapapeles", "copyCardPopup-title": "Copiar Tarjeta", - "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards", - "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format", - "copyChecklistToManyCardsPopup-format": "[ {\"title\": \"First card title\", \"description\":\"First card description\"}, {\"title\":\"Second card title\",\"description\":\"Second card description\"},{\"title\":\"Last card title\",\"description\":\"Last card description\"} ]", + "copyChecklistToManyCardsPopup-title": "Copiar Plantilla Checklist a Muchas Tarjetas", + "copyChecklistToManyCardsPopup-instructions": "Títulos y Descripciones de la Tarjeta Destino en este formato JSON", + "copyChecklistToManyCardsPopup-format": "[ {\"title\": \"Título de primera tarjeta\", \"description\":\"Descripción de primera tarjeta\"}, {\"title\":\"Título de segunda tarjeta\",\"description\":\"Descripción de segunda tarjeta\"},{\"title\":\"Título de última tarjeta\",\"description\":\"Descripción de última tarjeta\"} ]", "create": "Crear", "createBoardPopup-title": "Crear Tablero", "chooseBoardSourcePopup-title": "Importar tablero", @@ -188,7 +188,7 @@ "editCardDueDatePopup-title": "Cambiar fecha de vencimiento", "editCardSpentTimePopup-title": "Cambiar tiempo empleado", "editLabelPopup-title": "Cambiar Etiqueta", - "editNotificationPopup-title": "Editar Notificació", + "editNotificationPopup-title": "Editar Notificación", "editProfilePopup-title": "Editar Perfil", "email": "Email", "email-enrollAccount-subject": "Una cuenta creada para vos en __siteName__", @@ -264,19 +264,19 @@ "leave-board-pop": "¿Estás seguro que querés dejar __boardTitle__? Vas a salir de todas las tarjetas en este tablero.", "leaveBoardPopup-title": "¿Dejar Tablero?", "link-card": "Enlace a esta tarjeta", - "list-archive-cards": "Move all cards in this list to Recycle Bin", - "list-archive-cards-pop": "This will remove all the cards in this list from the board. To view cards in Recycle Bin and bring them back to the board, click “Menu” > “Recycle Bin”.", + "list-archive-cards": "Mover todas las tarjetas en esta lista a la Papelera de Reciclaje", + "list-archive-cards-pop": "Esto va a remover las tarjetas en esta lista del tablero. Para ver tarjetas en la Papelera de Reciclaje y traerlas de vuelta al tablero, clickeá \"Menú\" > \"Papelera de Reciclaje\".", "list-move-cards": "Mueve todas las tarjetas en esta lista", "list-select-cards": "Selecciona todas las tarjetas en esta lista", "listActionPopup-title": "Listar Acciones", - "swimlaneActionPopup-title": "Swimlane Actions", + "swimlaneActionPopup-title": "Acciones de la Calle", "listImportCardPopup-title": "Importar una tarjeta Trello", "listMorePopup-title": "Mas", "link-list": "Enlace a esta lista", "list-delete-pop": "Todas las acciones van a ser eliminadas del agregador de actividad y no podás recuperar la lista. No se puede deshacer.", - "list-delete-suggest-archive": "You can move a list to Recycle Bin to remove it from the board and preserve the activity.", + "list-delete-suggest-archive": "Podés mover la lista a la Papelera de Reciclaje para remvoerla del tablero y preservar la actividad.", "lists": "Listas", - "swimlanes": "Swimlanes", + "swimlanes": "Calles", "log-out": "Salir", "log-in": "Entrar", "loginPopup-title": "Entrar", @@ -294,9 +294,9 @@ "muted-info": "No serás notificado de ningún cambio en este tablero", "my-boards": "Mis Tableros", "name": "Nombre", - "no-archived-cards": "No cards in Recycle Bin.", - "no-archived-lists": "No lists in Recycle Bin.", - "no-archived-swimlanes": "No swimlanes in Recycle Bin.", + "no-archived-cards": "No hay tarjetas en la Papelera de Reciclaje", + "no-archived-lists": "No hay listas en la Papelera de Reciclaje", + "no-archived-swimlanes": "No hay calles en la Papelera de Reciclaje", "no-results": "No hay resultados", "normal": "Normal", "normal-desc": "Puede ver y editar tarjetas. No puede cambiar opciones.", @@ -332,8 +332,8 @@ "restore": "Restaurar", "save": "Grabar", "search": "Buscar", - "search-cards": "Search from card titles and descriptions on this board", - "search-example": "Text to search for?", + "search-cards": "Buscar en títulos y descripciones de tarjeta en este tablero", + "search-example": "¿Texto a buscar?", "select-color": "Seleccionar Color", "set-wip-limit-value": "Fijar un límite para el número máximo de tareas en esta lista", "setWipLimitPopup-title": "Establecer Límite TEP", @@ -361,7 +361,7 @@ "overtime-hours": "Sobretiempo (horas)", "overtime": "Sobretiempo", "has-overtime-cards": "Tiene tarjetas con sobretiempo", - "has-spenttime-cards": "Has spent time cards", + "has-spenttime-cards": "Ha gastado tarjetas de tiempo", "time": "Hora", "title": "Título", "tracking": "Seguimiento", @@ -374,12 +374,12 @@ "uploaded-avatar": "Cargado un avatar", "username": "Nombre de usuario", "view-it": "Verlo", - "warn-list-archived": "warning: this card is in an list at Recycle Bin", + "warn-list-archived": "cuidado; esta tarjeta está en la Papelera de Reciclaje", "watch": "Seguir", "watching": "Siguiendo", "watching-info": "Serás notificado de cualquier cambio en este tablero", "welcome-board": "Tablero de Bienvenida", - "welcome-swimlane": "Milestone 1", + "welcome-swimlane": "Hito 1", "welcome-list1": "Básicos", "welcome-list2": "Avanzado", "what-to-do": "¿Qué querés hacer?", @@ -437,10 +437,10 @@ "createdAt": "Creado en", "verified": "Verificado", "active": "Activo", - "card-received": "Received", - "card-received-on": "Received on", - "card-end": "End", - "card-end-on": "Ends on", - "editCardReceivedDatePopup-title": "Change received date", - "editCardEndDatePopup-title": "Change end date" + "card-received": "Recibido", + "card-received-on": "Recibido en", + "card-end": "Termino", + "card-end-on": "Termina en", + "editCardReceivedDatePopup-title": "Cambiar fecha de recepción", + "editCardEndDatePopup-title": "Cambiar fecha de término" }
\ No newline at end of file diff --git a/i18n/es.i18n.json b/i18n/es.i18n.json index 7dd6baa5..fcb2bbec 100644 --- a/i18n/es.i18n.json +++ b/i18n/es.i18n.json @@ -102,7 +102,7 @@ "board-view-lists": "Listas", "bucket-example": "Como “Cosas por hacer” por ejemplo", "cancel": "Cancelar", - "card-archived": "Esta tarjeta se envió a la papelera de reciclaje", + "card-archived": "Esta tarjeta se ha enviado a la papelera de reciclaje.", "card-comments-title": "Esta tarjeta tiene %s comentarios.", "card-delete-notice": "la eliminación es permanente. Perderás todas las acciones asociadas a esta tarjeta.", "card-delete-pop": "Se eliminarán todas las acciones del historial de actividades y no se podrá volver a abrir la tarjeta. Esta acción no puede deshacerse.", diff --git a/models/checklistItems.js b/models/checklistItems.js index 3c01d476..e075eda2 100644 --- a/models/checklistItems.js +++ b/models/checklistItems.js @@ -93,3 +93,53 @@ if (Meteor.isServer) { itemRemover(userId, doc); }); } + +if (Meteor.isServer) { + JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId', function (req, res) { + Authentication.checkUserId( req.userId); + const paramItemId = req.params.itemId; + const checklistItem = ChecklistItems.findOne({ _id: paramItemId }); + if (checklistItem) { + JsonRoutes.sendResult(res, { + code: 200, + data: checklistItem, + }); + } else { + JsonRoutes.sendResult(res, { + code: 500, + }); + } + }); + + JsonRoutes.add('PUT', '/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId', function (req, res) { + Authentication.checkUserId( req.userId); + + const paramItemId = req.params.itemId; + + if (req.body.hasOwnProperty('isFinished')) { + ChecklistItems.direct.update({_id: paramItemId}, {$set: {isFinished: req.body.isFinished}}); + } + if (req.body.hasOwnProperty('title')) { + ChecklistItems.direct.update({_id: paramItemId}, {$set: {title: req.body.title}}); + } + + JsonRoutes.sendResult(res, { + code: 200, + data: { + _id: paramItemId, + }, + }); + }); + + JsonRoutes.add('DELETE', '/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId', function (req, res) { + Authentication.checkUserId( req.userId); + const paramItemId = req.params.itemId; + ChecklistItems.direct.remove({ _id: paramItemId }); + JsonRoutes.sendResult(res, { + code: 200, + data: { + _id: paramItemId, + }, + }); + }); +} diff --git a/models/checklists.js b/models/checklists.js index 9946f98e..c58453ef 100644 --- a/models/checklists.js +++ b/models/checklists.js @@ -34,9 +34,9 @@ Checklists.helpers({ return ChecklistItems.find({ checklistId: this._id }).count(); }, items() { - return ChecklistItems.find(Filter.mongoSelector({ + return ChecklistItems.find({ checklistId: this._id, - }), { sort: ['sort'] }); + }, { sort: ['sort'] }); }, finishedCount() { return ChecklistItems.find({ @@ -106,94 +106,90 @@ if (Meteor.isServer) { if (Meteor.isServer) { JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/checklists', function (req, res) { - try { - Authentication.checkUserId( req.userId); - const paramCardId = req.params.cardId; + Authentication.checkUserId( req.userId); + const paramCardId = req.params.cardId; + const checklists = Checklists.find({ cardId: paramCardId }).map(function (doc) { + return { + _id: doc._id, + title: doc.title, + }; + }); + if (checklists) { JsonRoutes.sendResult(res, { code: 200, - data: Checklists.find({ cardId: paramCardId }).map(function (doc) { - return { - _id: doc._id, - title: doc.title, - }; - }), + data: checklists, }); - } - catch (error) { + } else { JsonRoutes.sendResult(res, { - code: 200, - data: error, + code: 500, }); } }); JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/checklists/:checklistId', function (req, res) { - try { - Authentication.checkUserId( req.userId); - const paramChecklistId = req.params.checklistId; - const paramCardId = req.params.cardId; + Authentication.checkUserId( req.userId); + const paramChecklistId = req.params.checklistId; + const paramCardId = req.params.cardId; + const checklist = Checklists.findOne({ _id: paramChecklistId, cardId: paramCardId }); + if (checklist) { + checklist.items = ChecklistItems.find({checklistId: checklist._id}).map(function (doc) { + return { + _id: doc._id, + title: doc.title, + isFinished: doc.isFinished, + }; + }); JsonRoutes.sendResult(res, { code: 200, - data: Checklists.findOne({ _id: paramChecklistId, cardId: paramCardId }), + data: checklist, }); - } - catch (error) { + } else { JsonRoutes.sendResult(res, { - code: 200, - data: error, + code: 500, }); } }); JsonRoutes.add('POST', '/api/boards/:boardId/cards/:cardId/checklists', function (req, res) { - try { - Authentication.checkUserId( req.userId); - const paramCardId = req.params.cardId; - - const checklistToSend = {}; - checklistToSend.cardId = paramCardId; - checklistToSend.title = req.body.title; - checklistToSend.items = []; - const id = Checklists.insert(checklistToSend); - const checklist = Checklists.findOne({_id: id}); - req.body.items.forEach(function (item) { - checklist.addItem(item); - }, this); - + Authentication.checkUserId( req.userId); + const paramCardId = req.params.cardId; + const id = Checklists.insert({ + title: req.body.title, + cardId: paramCardId, + sort: 0, + }); + if (id) { + req.body.items.forEach(function (item, idx) { + ChecklistItems.insert({ + cardId: paramCardId, + checklistId: id, + title: item.title, + sort: idx, + }); + }); JsonRoutes.sendResult(res, { code: 200, data: { _id: id, }, }); - } - catch (error) { + } else { JsonRoutes.sendResult(res, { - code: 200, - data: error, + code: 400, }); } }); JsonRoutes.add('DELETE', '/api/boards/:boardId/cards/:cardId/checklists/:checklistId', function (req, res) { - try { - Authentication.checkUserId( req.userId); - const paramCommentId = req.params.commentId; - const paramCardId = req.params.cardId; - Checklists.remove({ _id: paramCommentId, cardId: paramCardId }); - JsonRoutes.sendResult(res, { - code: 200, - data: { - _id: paramCardId, - }, - }); - } - catch (error) { - JsonRoutes.sendResult(res, { - code: 200, - data: error, - }); - } + Authentication.checkUserId( req.userId); + const paramChecklistId = req.params.checklistId; + Checklists.remove({ _id: paramChecklistId }); + JsonRoutes.sendResult(res, { + code: 200, + data: { + _id: paramChecklistId, + }, + }); }); } diff --git a/package.json b/package.json index 3e979ec4..a0053ca2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wekan", - "version": "0.83.0", + "version": "0.84.0", "description": "The open-source Trello-like kanban", "private": true, "scripts": { diff --git a/sandstorm-pkgdef.capnp b/sandstorm-pkgdef.capnp index 066bb3cb..554f5cc6 100644 --- a/sandstorm-pkgdef.capnp +++ b/sandstorm-pkgdef.capnp @@ -22,10 +22,10 @@ const pkgdef :Spk.PackageDefinition = ( appTitle = (defaultText = "Wekan"), # The name of the app as it is displayed to the user. - appVersion = 68, + appVersion = 69, # Increment this for every release. - appMarketingVersion = (defaultText = "0.83.0~2018-04-12"), + appMarketingVersion = (defaultText = "0.84.0~2018-04-16"), # Human-readable presentation of the app version. minUpgradableAppVersion = 0, diff --git a/server/authentication.js b/server/authentication.js index acc101cc..efe95015 100644 --- a/server/authentication.js +++ b/server/authentication.js @@ -1,5 +1,12 @@ Meteor.startup(() => { + // Node Fibers 100% CPU usage issue + // https://github.com/wekan/wekan-mongodb/issues/2#issuecomment-381453161 + // https://github.com/meteor/meteor/issues/9796#issuecomment-381676326 + // https://github.com/sandstorm-io/sandstorm/blob/0f1fec013fe7208ed0fd97eb88b31b77e3c61f42/shell/server/00-startup.js#L99-L129 + import Fiber from "fibers"; + Fiber.poolSize = 1e9; + Accounts.validateLoginAttempt(function (options) { const user = options.user || {}; return !user.loginDisabled; diff --git a/snapcraft.yaml b/snapcraft.yaml index fd123b21..abcd1cc6 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -81,7 +81,7 @@ parts: wekan: source: . plugin: nodejs - node-engine: 8.9.3 + node-engine: 8.11.1 node-packages: - npm@5.5.1 - node-gyp @@ -100,6 +100,24 @@ parts: prepare: | echo "Cleaning environment first" rm -rf ~/.meteor ~/.npm /usr/local/lib/node_modules + # Node Fibers 100% CPU usage issue: + # https://github.com/wekan/wekan-mongodb/issues/2#issuecomment-381453161 + # https://github.com/meteor/meteor/issues/9796#issuecomment-381676326 + # https://github.com/sandstorm-io/sandstorm/blob/0f1fec013fe7208ed0fd97eb88b31b77e3c61f42/shell/server/00-startup.js#L99-L129 + # Also see beginning of wekan/server/authentication.js + # import Fiber from "fibers"; + # Fiber.poolSize = 1e9; + # Download node version 8.11.1 that has fix included, node binary copied from Sandstorm + # Description at https://releases.wekan.team/node.txt + # SHA256SUM: 18c99d5e79e2fe91e75157a31be30e5420787213684d4048eb91e602e092725d + echo "18c99d5e79e2fe91e75157a31be30e5420787213684d4048eb91e602e092725d node" >> node-SHASUMS256.txt.asc + wget https://releases.wekan.team/node + # Verify Fibers patched node authenticity + echo "Fibers 100% CPU issue patched node authenticity:" + grep node node-SHASUMS256.txt.asc | shasum -a 256 -c - + rm -f node-SHASUMS256.txt.asc + chmod +x node + mv node `which node` echo "Applying paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303" paxctl -mC `which node` echo "Installing meteor" |