فهرست منبع

Implement board archive and restoration

Maxime Quandalle 9 سال پیش
والد
کامیت
48ac8b026f

+ 1 - 1
History.md

@@ -28,7 +28,7 @@ This release continues the implementation of basic features of a “kanban”
 software, especially:
 
 * Basic card attachments. If the attached file is an image we generate and
-  displayed a thumbnail that can be used as a card “cover” (visible in the board
+  display a thumbnail that can be used as a card “cover” (visible in the board
   general view);
 * User names mentions and auto-completion in card description and comments
   (though we don’t have any notification system for now, making this feature a

+ 12 - 0
client/components/boards/boardArchive.jade

@@ -0,0 +1,12 @@
+template(name="archivedBoards")
+  h2 Archived boards
+
+  ul.archived-lists
+    each archivedBoards
+      li.archived-lists-item
+        button.js-restore-board
+          i.fa.fa-undo
+          | Restore board
+        = title
+    else
+      li.no-items-message No archived board.

+ 35 - 0
client/components/boards/boardArchive.js

@@ -0,0 +1,35 @@
+Template.headerTitle.events({
+  'click .js-open-archived-board': function() {
+    Modal.open('archivedBoards')
+  }
+})
+
+BlazeComponent.extendComponent({
+  template() {
+    return 'archivedBoards';
+  },
+
+  onCreated() {
+    this.subscribe('archivedBoards')
+  },
+
+  archivedBoards() {
+    return Boards.find({ archived: true }, {
+      sort: ['title']
+    })
+  },
+
+  events() {
+    return [{
+      'click .js-restore-board': function() {
+        let boardId = this.currentData()._id
+        Boards.update(boardId, {
+          $set: {
+            archived: false
+          }
+        })
+        Utils.goBoardId(boardId)
+      }
+    }]
+  },
+}).register('archivedBoards')

+ 1 - 0
client/components/boards/boardBody.jade

@@ -3,6 +3,7 @@ template(name="board")
     if currentBoard
       +boardBody
     else
+      //- XXX We need a better error message in case the board has been archived
       +message(label="board-no-found")
   else
     +spinner

+ 2 - 2
client/components/boards/boardHeader.jade

@@ -60,7 +60,7 @@ template(name="boardMenuPopup")
     if currentUser.isBoardAdmin
       hr
       ul.pop-over-list
-        li: a Close Board…
+        li: a.js-archive-board Archive Board…
 
 template(name="boardVisibilityList")
   ul.pop-over-list
@@ -120,6 +120,6 @@ template(name="boardChangeTitlePopup")
       input.js-board-name(type="text" value="{{title}}" autofocus)
     input.primary.wide(type="submit" value="{{_ 'rename'}}")
 
-template(name="closeBoardPopup")
+template(name="archiveBoardPopup")
   p {{_ 'close-board-pop'}}
   button.js-confirm.negate.full(type="submit") {{_ 'close'}}

+ 8 - 1
client/components/boards/boardHeader.js

@@ -5,7 +5,14 @@ Template.boardMenuPopup.events({
     Popup.close();
   },
   'click .js-change-board-color': Popup.open('boardChangeColor'),
-  'click .js-change-language': Popup.open('setLanguage')
+  'click .js-change-language': Popup.open('setLanguage'),
+  'click .js-archive-board ': Popup.afterConfirm('archiveBoard', function() {
+    var boardId = Session.get('currentBoard');
+    Boards.update(boardId, { $set: { archived: true }});
+    // XXX We should have some kind of notification on top of the page to
+    // confirm that the board was successfully archived.
+    FlowRouter.go('home');
+  })
 });
 
 Template.boardChangeTitlePopup.events({

+ 15 - 14
client/components/boards/boardList.jade

@@ -1,15 +1,16 @@
 template(name="boardList")
-  if boards.count
-    ul.board-list.clearfix
-      each boards
-        li(class="{{#if isStarred}}starred{{/if}}" class=colorClass)
-          a.js-open-board(href="{{pathFor 'board' id=_id slug=slug}}")
-            span.details
-              span.board-list-item-name= title
-              i.fa.js-star-board(
-                class="fa-star{{#if isStarred}} is-star-active{{else}}-o{{/if}}"
-                title="{{_ 'star-board-title'}}")
-  else
-    ul.board-list.clearfix
-      li.js-add-board
-        a.label {{_ 'add-board'}}
+  .wrapper
+    if boards.count
+      ul.board-list.clearfix
+        each boards
+          li(class="{{#if isStarred}}starred{{/if}}" class=colorClass)
+            a.js-open-board(href="{{pathFor 'board' id=_id slug=slug}}")
+              span.details
+                span.board-list-item-name= title
+                i.fa.js-star-board(
+                  class="fa-star{{#if isStarred}} is-star-active{{else}}-o{{/if}}"
+                  title="{{_ 'star-board-title'}}")
+    else
+      ul.board-list.clearfix
+        li.js-add-board
+          a.label {{_ 'add-board'}}

+ 1 - 1
client/components/boards/boardList.js

@@ -4,7 +4,7 @@ BlazeComponent.extendComponent({
   },
 
   boards: function() {
-    return Boards.find({}, {
+    return Boards.find({ archived: false }, {
       sort: ['title']
     });
   },

+ 4 - 3
client/components/boards/boardList.styl

@@ -1,6 +1,7 @@
+$spaceBetweenTiles = 16px
+
 .board-list
-  margin: 25px auto
-  width: 1200px
+  margin: $spaceBetweenTiles ($spaceBetweenTiles/-2) 0
 
   li
     float: left
@@ -24,7 +25,7 @@
     font-weight: 700
     min-height: 18px
     padding: 8px 12px 8px 12px
-    margin: 0 16px 16px 0
+    margin: 0 ($spaceBetweenTiles/2) $spaceBetweenTiles
     position: relative
     text-decoration: none
 

+ 5 - 1
client/components/main/header.jade

@@ -31,7 +31,7 @@ template(name="header")
       The main bar is a colorful bar that provide all the meta-data for the
       current page. This bar is contextual based.
       If the user is not connected we display "sign in" and "log in" buttons.
-    #header-main-bar
+    #header-main-bar(class="{{#if wrappedHeader}}wrapper{{/if}}")
       if $.Session.get 'currentBoard'
         +headerBoard
       else
@@ -39,3 +39,7 @@ template(name="header")
 
 template(name="headerTitle")
   h1 LibreBoard
+  .board-header-btns.right
+    a.board-header-btn.js-open-archived-board
+      i.fa.fa-archive
+      span Archives

+ 6 - 0
client/components/main/header.js

@@ -2,6 +2,12 @@ Template.header.helpers({
   // Reactively set the color of the page from the color of the current board.
   headerTemplate: function() {
     return 'headerBoard';
+  },
+
+  wrappedHeader: function() {
+    var unwrapedRoutes = ['board', 'card'];
+    var currentRouteName = FlowRouter.getRouteName();
+    return unwrapedRoutes.indexOf(currentRouteName) === -1;
   }
 });
 

+ 2 - 0
client/components/main/header.styl

@@ -106,6 +106,8 @@
         margin: 0 10px
 
         + span
+          display: inline-block
+          margin-top: 1px
           margin-right: 10px
 
       .board-header-btn-close

+ 5 - 1
client/components/main/layouts.styl

@@ -42,6 +42,7 @@ body
     width: 660px
     min-height: 160px
     margin: 42px auto
+    padding: 12px
     border-radius: 4px
     background: darken(white, 13%)
     z-index: 110
@@ -49,7 +50,6 @@ body
     .modal-close-btn
       display: block
       float: right
-      margin: 12px
       font-size: 24px
 
 h1
@@ -206,6 +206,10 @@ dd
   margin-bottom: 0
   padding-bottom: 0
 
+.wrapper
+  max-width: 1200px
+  margin: 0 auto
+
 .relative
   position: relative
 

+ 1 - 1
client/lib/modal.js

@@ -1,6 +1,6 @@
 const closedValue = null
 
-Modal = new class {
+window.Modal = new class {
   constructor() {
     this._currentModal = new ReactiveVar(closedValue)
   }

+ 1 - 1
collections/users.js

@@ -14,7 +14,7 @@ Users.helpers({
   },
   starredBoards: function() {
     var starredBoardIds = this.profile.starredBoards || [];
-    return Boards.find({_id: {$in: starredBoardIds}});
+    return Boards.find({archived: false, _id: {$in: starredBoardIds}});
   },
   hasStarred: function(boardId) {
     var starredBoardIds = this.profile.starredBoards || [];

+ 1 - 1
i18n/ar.i18n.json

@@ -159,7 +159,7 @@
     "boardChangeTitlePopup-title": "Rename Board",
     "boardChangeVisibilityPopup-title": "Change Visibility",
     "addMemberPopup-title": "Members",
-    "closeBoardPopup-title": "Close Board?",
+    "archiveBoardPopup-title": "Close Board?",
     "removeMemberPopup-title": "Remove Member?",
     "createBoardPopup-title": "Create Board",
     "listActionPopup-title": "List Actions",

+ 1 - 1
i18n/br.i18n.json

@@ -159,7 +159,7 @@
     "boardChangeTitlePopup-title": "Renomear Quadro",
     "boardChangeVisibilityPopup-title": "Alterar Visibilidade",
     "addMemberPopup-title": "Membros",
-    "closeBoardPopup-title": "Fechar Quadro?",
+    "archiveBoardPopup-title": "Fechar Quadro?",
     "removeMemberPopup-title": "Remover Membro?",
     "createBoardPopup-title": "Criar Quadro",
     "listActionPopup-title": "Listar Ações",

+ 1 - 1
i18n/cm.i18n.json

@@ -159,7 +159,7 @@
     "boardChangeTitlePopup-title": "Rename Board",
     "boardChangeVisibilityPopup-title": "Change Visibility",
     "addMemberPopup-title": "Members",
-    "closeBoardPopup-title": "Close Board?",
+    "archiveBoardPopup-title": "Close Board?",
     "removeMemberPopup-title": "Remove Member?",
     "createBoardPopup-title": "Create Board",
     "listActionPopup-title": "List Actions",

+ 1 - 1
i18n/cn.i18n.json

@@ -159,7 +159,7 @@
     "boardChangeTitlePopup-title": "重命名看板",
     "boardChangeVisibilityPopup-title": "更改可视级别",
     "addMemberPopup-title": "成员",
-    "closeBoardPopup-title": "关闭看板?",
+    "archiveBoardPopup-title": "关闭看板?",
     "removeMemberPopup-title": "删除成员?",
     "createBoardPopup-title": "创建看板",
     "listActionPopup-title": "列出动作",

+ 1 - 1
i18n/cs.i18n.json

@@ -159,7 +159,7 @@
     "boardChangeTitlePopup-title": "Rename Board",
     "boardChangeVisibilityPopup-title": "Change Visibility",
     "addMemberPopup-title": "Members",
-    "closeBoardPopup-title": "Close Board?",
+    "archiveBoardPopup-title": "Close Board?",
     "removeMemberPopup-title": "Remove Member?",
     "createBoardPopup-title": "Create Board",
     "listActionPopup-title": "List Actions",

+ 1 - 1
i18n/de.i18n.json

@@ -159,7 +159,7 @@
     "boardChangeTitlePopup-title": "Bord umbenennen",
     "boardChangeVisibilityPopup-title": "Ändere Sichbarkeit",
     "addMemberPopup-title": "Nutzer",
-    "closeBoardPopup-title": "Schliese Bord?",
+    "archiveBoardPopup-title": "Schliese Bord?",
     "removeMemberPopup-title": "Entferne Nutzer?",
     "createBoardPopup-title": "Erstelle ein Bord",
     "listActionPopup-title": "Liste von Aktionen",

+ 1 - 1
i18n/en.i18n.json

@@ -171,7 +171,7 @@
     "boardChangeTitlePopup-title": "Rename Board",
     "boardChangeVisibilityPopup-title": "Change Visibility",
     "addMemberPopup-title": "Members",
-    "closeBoardPopup-title": "Close Board?",
+    "archiveBoardPopup-title": "Close Board?",
     "removeMemberPopup-title": "Remove Member?",
     "createBoardPopup-title": "Create Board",
     "listActionPopup-title": "List Actions",

+ 1 - 1
i18n/es.i18n.json

@@ -159,7 +159,7 @@
     "boardChangeTitlePopup-title": "Renombrar tablero",
     "boardChangeVisibilityPopup-title": "Cambiar visibilidad",
     "addMemberPopup-title": "Miembros",
-    "closeBoardPopup-title": "Cerrar el tablero",
+    "archiveBoardPopup-title": "Cerrar el tablero",
     "removeMemberPopup-title": "¿Eliminar miembro?",
     "createBoardPopup-title": "Crear tablero",
     "listActionPopup-title": "Acciones de la lista",

+ 1 - 1
i18n/fi.i18n.json

@@ -159,7 +159,7 @@
     "boardChangeTitlePopup-title": "Nimeä taulu uudelleen",
     "boardChangeVisibilityPopup-title": "Vaihda näkyvyyttä",
     "addMemberPopup-title": "Jäsenet",
-    "closeBoardPopup-title": "Sulje taulu?",
+    "archiveBoardPopup-title": "Sulje taulu?",
     "removeMemberPopup-title": "Poista jäsen?",
     "createBoardPopup-title": "Luo taulu",
     "listActionPopup-title": "Listaa toimet",

+ 1 - 1
i18n/fr.i18n.json

@@ -159,7 +159,7 @@
     "boardChangeTitlePopup-title": "Renommer le tableau",
     "boardChangeVisibilityPopup-title": "Changer la visibilité",
     "addMemberPopup-title": "Membres",
-    "closeBoardPopup-title": "Fermer le tableau ?",
+    "archiveBoardPopup-title": "Fermer le tableau ?",
     "removeMemberPopup-title": "Supprimer le membre ?",
     "createBoardPopup-title": "Créer un tableau",
     "listActionPopup-title": "Liste des actions",

+ 1 - 1
i18n/hk.i18n.json

@@ -159,7 +159,7 @@
     "boardChangeTitlePopup-title": "Rename Board",
     "boardChangeVisibilityPopup-title": "Change Visibility",
     "addMemberPopup-title": "Members",
-    "closeBoardPopup-title": "Close Board?",
+    "archiveBoardPopup-title": "Close Board?",
     "removeMemberPopup-title": "Remove Member?",
     "createBoardPopup-title": "Create Board",
     "listActionPopup-title": "List Actions",

+ 1 - 1
i18n/id.i18n.json

@@ -159,7 +159,7 @@
     "boardChangeTitlePopup-title": "Rename Board",
     "boardChangeVisibilityPopup-title": "Change Visibility",
     "addMemberPopup-title": "Members",
-    "closeBoardPopup-title": "Close Board?",
+    "archiveBoardPopup-title": "Close Board?",
     "removeMemberPopup-title": "Remove Member?",
     "createBoardPopup-title": "Create Board",
     "listActionPopup-title": "List Actions",

+ 1 - 1
i18n/ja.i18n.json

@@ -159,7 +159,7 @@
     "boardChangeTitlePopup-title": "ボード名の変更",
     "boardChangeVisibilityPopup-title": "公開範囲の変更",
     "addMemberPopup-title": "メンバー",
-    "closeBoardPopup-title": "ボードを閉じますか?",
+    "archiveBoardPopup-title": "ボードを閉じますか?",
     "removeMemberPopup-title": "メンバーを外しますか?",
     "createBoardPopup-title": "ボードの作成",
     "listActionPopup-title": "操作一覧",

+ 1 - 1
i18n/ko.i18n.json

@@ -159,7 +159,7 @@
     "boardChangeTitlePopup-title": "보드 이름 바꾸기",
     "boardChangeVisibilityPopup-title": "표시 여부 변경",
     "addMemberPopup-title": "멤버",
-    "closeBoardPopup-title": "보드를 닫습니까?",
+    "archiveBoardPopup-title": "보드를 닫습니까?",
     "removeMemberPopup-title": "멤버를 제거합니까?",
     "createBoardPopup-title": "보드 생성",
     "listActionPopup-title": "동작 목록",

+ 1 - 1
i18n/ru.i18n.json

@@ -159,7 +159,7 @@
     "boardChangeTitlePopup-title": "Переименовать доску",
     "boardChangeVisibilityPopup-title": "Изменить настройки видимости",
     "addMemberPopup-title": "Участники",
-    "closeBoardPopup-title": "Закрыть доску?",
+    "archiveBoardPopup-title": "Закрыть доску?",
     "removeMemberPopup-title": "Удалить участника?",
     "createBoardPopup-title": "Создать доску",
     "listActionPopup-title": "Список действий",

+ 1 - 1
i18n/tr.i18n.json

@@ -159,7 +159,7 @@
     "boardChangeTitlePopup-title": "Pano Adı Değiştirme",
     "boardChangeVisibilityPopup-title": "Görünebilirliği Değiştir",
     "addMemberPopup-title": "Üyeler",
-    "closeBoardPopup-title": "Pano Kapatılsın mı?",
+    "archiveBoardPopup-title": "Pano Kapatılsın mı?",
     "removeMemberPopup-title": "Üyeyi Çıkarmak mı?",
     "createBoardPopup-title": "Pano Oluşturma",
     "listActionPopup-title": "Liste İşlemleri",

+ 23 - 0
server/publications/boards.js

@@ -22,6 +22,7 @@ Meteor.publish('boards', function() {
   }, {
     fields: {
       _id: 1,
+      archived: 1,
       slug: 1,
       title: 1,
       color: 1,
@@ -30,6 +31,28 @@ Meteor.publish('boards', function() {
   });
 });
 
+Meteor.publish('archivedBoards', function() {
+  if (! Match.test(this.userId, String))
+    return [];
+
+  return Boards.find({
+    archived: true,
+    members: {
+      $elemMatch: {
+        userId: this.userId,
+        isAdmin: true
+      }
+    }
+  }, {
+    fields: {
+      _id: 1,
+      archived: 1,
+      slug: 1,
+      title: 1
+    }
+  })
+});
+
 Meteor.publishComposite('board', function(boardId) {
   check(boardId, String);
   return {