Bladeren bron

Merge branch 'andresmanelli-devel' into devel

Lauri Ojansivu 6 jaren geleden
bovenliggende
commit
9967859f42
70 gewijzigde bestanden met toevoegingen van 1292 en 319 verwijderingen
  1. 8 0
      CHANGELOG.md
  2. 9 2
      client/components/activities/comments.js
  3. 7 2
      client/components/cards/attachments.js
  4. 30 30
      client/components/cards/cardDate.js
  5. 30 23
      client/components/cards/cardDetails.jade
  6. 1 1
      client/components/cards/cardDetails.js
  7. 6 0
      client/components/cards/cardDetails.styl
  8. 4 4
      client/components/cards/cardTime.jade
  9. 8 11
      client/components/cards/cardTime.js
  10. 3 1
      client/components/cards/checklists.js
  11. 24 18
      client/components/cards/minicard.jade
  12. 11 0
      client/components/cards/minicard.js
  13. 12 0
      client/components/cards/minicard.styl
  14. 1 0
      client/components/cards/subtasks.js
  15. 3 0
      client/components/forms/forms.styl
  16. 11 0
      client/components/lists/list.styl
  17. 53 1
      client/components/lists/listBody.jade
  18. 201 0
      client/components/lists/listBody.js
  19. 3 2
      client/components/users/userAvatar.js
  20. 124 127
      client/lib/filter.js
  21. 7 0
      i18n/ar.i18n.json
  22. 7 0
      i18n/bg.i18n.json
  23. 7 0
      i18n/br.i18n.json
  24. 7 0
      i18n/ca.i18n.json
  25. 7 0
      i18n/cs.i18n.json
  26. 7 0
      i18n/de.i18n.json
  27. 7 0
      i18n/el.i18n.json
  28. 7 0
      i18n/en-GB.i18n.json
  29. 7 0
      i18n/en.i18n.json
  30. 7 0
      i18n/eo.i18n.json
  31. 7 0
      i18n/es-AR.i18n.json
  32. 7 0
      i18n/es.i18n.json
  33. 7 0
      i18n/eu.i18n.json
  34. 7 0
      i18n/fa.i18n.json
  35. 7 0
      i18n/fi.i18n.json
  36. 7 0
      i18n/fr.i18n.json
  37. 7 0
      i18n/gl.i18n.json
  38. 7 0
      i18n/he.i18n.json
  39. 7 0
      i18n/hu.i18n.json
  40. 7 0
      i18n/hy.i18n.json
  41. 7 0
      i18n/id.i18n.json
  42. 7 0
      i18n/ig.i18n.json
  43. 7 0
      i18n/it.i18n.json
  44. 7 0
      i18n/ja.i18n.json
  45. 7 0
      i18n/ka.i18n.json
  46. 7 0
      i18n/km.i18n.json
  47. 7 0
      i18n/ko.i18n.json
  48. 7 0
      i18n/lv.i18n.json
  49. 7 0
      i18n/mn.i18n.json
  50. 7 0
      i18n/nb.i18n.json
  51. 7 0
      i18n/nl.i18n.json
  52. 7 0
      i18n/pl.i18n.json
  53. 7 0
      i18n/pt-BR.i18n.json
  54. 7 0
      i18n/pt.i18n.json
  55. 7 0
      i18n/ro.i18n.json
  56. 7 0
      i18n/ru.i18n.json
  57. 7 0
      i18n/sr.i18n.json
  58. 7 0
      i18n/sv.i18n.json
  59. 7 0
      i18n/ta.i18n.json
  60. 7 0
      i18n/th.i18n.json
  61. 7 0
      i18n/tr.i18n.json
  62. 7 0
      i18n/uk.i18n.json
  63. 7 0
      i18n/vi.i18n.json
  64. 7 0
      i18n/zh-CN.i18n.json
  65. 7 0
      i18n/zh-TW.i18n.json
  66. 35 13
      models/boards.js
  67. 366 82
      models/cards.js
  68. 2 1
      models/export.js
  69. 12 0
      server/migrations.js
  70. 13 1
      server/publications/boards.js

+ 8 - 0
CHANGELOG.md

@@ -1,3 +1,11 @@
+# Upcoming Wekan release
+
+This release add the following new features:
+
+- [Linked Cards and Linked Boards](https://github.com/wekan/wekan/pull/1592).
+
+Thanks to GitHub user andresmanelli for contributions.
+
 # v1.26 2018-08-09 Wekan release
 
 This release fixes the following bugs:

+ 9 - 2
client/components/activities/comments.js

@@ -21,11 +21,18 @@ BlazeComponent.extendComponent({
       'submit .js-new-comment-form'(evt) {
         const input = this.getInput();
         const text = input.val().trim();
+        const card = this.currentData();
+        let boardId = card.boardId;
+        let cardId = card._id;
+        if (card.isLinkedCard()) {
+          boardId = Cards.findOne(card.linkedId).boardId;
+          cardId = card.linkedId;
+        }
         if (text) {
           CardComments.insert({
             text,
-            boardId: this.currentData().boardId,
-            cardId: this.currentData()._id,
+            boardId,
+            cardId,
           });
           resetCommentInput(input);
           Tracker.flush();

+ 7 - 2
client/components/cards/attachments.js

@@ -57,8 +57,13 @@ Template.cardAttachmentsPopup.events({
     const card = this;
     FS.Utility.eachFile(evt, (f) => {
       const file = new FS.File(f);
-      file.boardId = card.boardId;
-      file.cardId = card._id;
+      if (card.isLinkedCard()) {
+        file.boardId = Cards.findOne(card.linkedId).boardId;
+        file.cardId = card.linkedId;
+      } else {
+        file.boardId = card.boardId;
+        file.cardId = card._id;
+      }
       file.userId = Meteor.userId();
 
       const attachment = Attachments.insert(file);

+ 30 - 30
client/components/cards/cardDate.js

@@ -96,7 +96,7 @@ Template.dateBadge.helpers({
 (class extends DatePicker {
   onCreated() {
     super.onCreated();
-    this.data().receivedAt && this.date.set(moment(this.data().receivedAt));
+    this.data().getReceived() && this.date.set(moment(this.data().getReceived()));
   }
 
   _storeDate(date) {
@@ -104,7 +104,7 @@ Template.dateBadge.helpers({
   }
 
   _deleteDate() {
-    this.card.unsetReceived();
+    this.card.setReceived(null);
   }
 }).register('editCardReceivedDatePopup');
 
@@ -113,13 +113,13 @@ Template.dateBadge.helpers({
 (class extends DatePicker {
   onCreated() {
     super.onCreated();
-    this.data().startAt && this.date.set(moment(this.data().startAt));
+    this.data().getStart() && this.date.set(moment(this.data().getStart()));
   }
 
   onRendered() {
     super.onRendered();
-    if (moment.isDate(this.card.receivedAt)) {
-      this.$('.js-datepicker').datepicker('setStartDate', this.card.receivedAt);
+    if (moment.isDate(this.card.getReceived())) {
+      this.$('.js-datepicker').datepicker('setStartDate', this.card.getReceived());
     }
   }
 
@@ -128,7 +128,7 @@ Template.dateBadge.helpers({
   }
 
   _deleteDate() {
-    this.card.unsetStart();
+    this.card.setStart(null);
   }
 }).register('editCardStartDatePopup');
 
@@ -136,13 +136,13 @@ Template.dateBadge.helpers({
 (class extends DatePicker {
   onCreated() {
     super.onCreated();
-    this.data().dueAt && this.date.set(moment(this.data().dueAt));
+    this.data().getDue() && this.date.set(moment(this.data().getDue()));
   }
 
   onRendered() {
     super.onRendered();
-    if (moment.isDate(this.card.startAt)) {
-      this.$('.js-datepicker').datepicker('setStartDate', this.card.startAt);
+    if (moment.isDate(this.card.getStart())) {
+      this.$('.js-datepicker').datepicker('setStartDate', this.card.getStart());
     }
   }
 
@@ -151,7 +151,7 @@ Template.dateBadge.helpers({
   }
 
   _deleteDate() {
-    this.card.unsetDue();
+    this.card.setDue(null);
   }
 }).register('editCardDueDatePopup');
 
@@ -159,13 +159,13 @@ Template.dateBadge.helpers({
 (class extends DatePicker {
   onCreated() {
     super.onCreated();
-    this.data().endAt && this.date.set(moment(this.data().endAt));
+    this.data().getEnd() && this.date.set(moment(this.data().getEnd()));
   }
 
   onRendered() {
     super.onRendered();
-    if (moment.isDate(this.card.startAt)) {
-      this.$('.js-datepicker').datepicker('setStartDate', this.card.startAt);
+    if (moment.isDate(this.card.getStart())) {
+      this.$('.js-datepicker').datepicker('setStartDate', this.card.getStart());
     }
   }
 
@@ -174,7 +174,7 @@ Template.dateBadge.helpers({
   }
 
   _deleteDate() {
-    this.card.unsetEnd();
+    this.card.setEnd(null);
   }
 }).register('editCardEndDatePopup');
 
@@ -213,15 +213,15 @@ class CardReceivedDate extends CardDate {
     super.onCreated();
     const self = this;
     self.autorun(() => {
-      self.date.set(moment(self.data().receivedAt));
+      self.date.set(moment(self.data().getReceived()));
     });
   }
 
   classes() {
     let classes = 'received-date ';
-    const dueAt = this.data().dueAt;
-    const endAt = this.data().endAt;
-    const startAt = this.data().startAt;
+    const dueAt = this.data().getDue();
+    const endAt = this.data().getEnd();
+    const startAt = this.data().getStart();
     const theDate = this.date.get();
     // if dueAt, endAt and startAt exist & are > receivedAt, receivedAt doesn't need to be flagged
     if (((startAt) && (theDate.isAfter(dueAt))) ||
@@ -250,14 +250,14 @@ class CardStartDate extends CardDate {
     super.onCreated();
     const self = this;
     self.autorun(() => {
-      self.date.set(moment(self.data().startAt));
+      self.date.set(moment(self.data().getStart()));
     });
   }
 
   classes() {
     let classes = 'start-date' + ' ';
-    const dueAt = this.data().dueAt;
-    const endAt = this.data().endAt;
+    const dueAt = this.data().getDue();
+    const endAt = this.data().getEnd();
     const theDate = this.date.get();
     const now = this.now.get();
     // if dueAt or endAt exist & are > startAt, startAt doesn't need to be flagged
@@ -288,14 +288,13 @@ class CardDueDate extends CardDate {
     super.onCreated();
     const self = this;
     self.autorun(() => {
-      self.date.set(moment(self.data().dueAt));
+      self.date.set(moment(self.data().getDue()));
     });
   }
 
   classes() {
     let classes = 'due-date' + ' ';
-
-    const endAt = this.data().endAt;
+    const endAt = this.data().getEnd();
     const theDate = this.date.get();
     const now = this.now.get();
     // if the due date is after the end date, green - done early
@@ -330,19 +329,20 @@ class CardEndDate extends CardDate {
     super.onCreated();
     const self = this;
     self.autorun(() => {
-      self.date.set(moment(self.data().endAt));
+      self.date.set(moment(self.data().getEnd()));
     });
   }
 
   classes() {
     let classes = 'end-date' + ' ';
-    const dueAt = this.data.dueAt;
+    const dueAt = this.data().getDue();
     const theDate = this.date.get();
-    // if dueAt exists & is after endAt, endAt doesn't need to be flagged
-    if ((dueAt) && (theDate.isAfter(dueAt, 'minute')))
+    if (theDate.diff(dueAt, 'days') >= 2)
       classes += 'long-overdue';
-    else
-      classes += 'current';
+    else if (theDate.diff(dueAt, 'days') >= 0)
+      classes += 'due';
+    else if (theDate.diff(dueAt, 'days') >= -2)
+      classes += 'almost-due';
     return classes;
   }
 

+ 30 - 23
client/components/cards/cardDetails.jade

@@ -10,7 +10,7 @@ template(name="cardDetails")
         h2.card-details-title.js-card-title(
           class="{{#if canModifyCard}}js-open-inlined-form is-editable{{/if}}")
             +viewer
-              = title
+              = getTitle
               if isWatching
                 i.fa.fa-eye.card-details-watch
         .card-details-path
@@ -19,43 +19,50 @@ template(name="cardDetails")
             a.js-parent-card(href=linkForCard) {{title}}
           // else
             {{_ 'top-level-card'}}
+        if isLinkedCard
+          h3.linked-card-location
+            +viewer
+              | {{getBoardTitle}} > {{getTitle}}
 
-    if archived
-      p.warning {{_ 'card-archived'}}
+    if getArchived
+      if isLinkedBoard
+        p.warning {{_ 'board-archived'}}
+      else
+        p.warning {{_ 'card-archived'}}
 
     .card-details-items
       .card-details-item.card-details-item-received
         h3.card-details-item-title {{_ 'card-received'}}
-        if receivedAt
+        if getReceived
           +cardReceivedDate
         else
           a.js-received-date {{_ 'add'}}
 
       .card-details-item.card-details-item-start
         h3.card-details-item-title {{_ 'card-start'}}
-        if startAt
+        if getStart
           +cardStartDate
         else
           a.js-start-date {{_ 'add'}}
 
-      .card-details-item.card-details-item-end
-        h3.card-details-item-title {{_ 'card-end'}}
-        if endAt
-          +cardEndDate
-        else
-          a.js-end-date {{_ 'add'}}
-
       .card-details-item.card-details-item-due
         h3.card-details-item-title {{_ 'card-due'}}
-        if dueAt
+        if getDue
           +cardDueDate
         else
           a.js-due-date {{_ 'add'}}
 
+      .card-details-item.card-details-item-end
+        h3.card-details-item-title {{_ 'card-end'}}
+        if getEnd
+          +cardEndDate
+        else
+          a.js-end-date {{_ 'add'}}
+
     .card-details-items
       .card-details-item.card-details-item-members
         h3.card-details-item-title {{_ 'members'}}
-        each members
+        each getMembers
           +userAvatar(userId=this cardId=../_id)
           | {{! XXX Hack to hide syntaxic coloration /// }}
         if canModifyCard
@@ -79,9 +86,9 @@ template(name="cardDetails")
           +cardCustomField
 
     .card-details-items
-      if spentTime
+      if getSpentTime
         .card-details-item.card-details-item-spent
-          if isOvertime
+          if getIsOvertime
             h3.card-details-item-title {{_ 'overtime-hours'}}
           else
             h3.card-details-item-title {{_ 'spent-time-hours'}}
@@ -92,15 +99,15 @@ template(name="cardDetails")
       h3.card-details-item-title {{_ 'description'}}
       +inlinedCardDescription(classNames="card-description js-card-description")
         +editor(autofocus=true)
-          | {{getUnsavedValue 'cardDescription' _id description}}
+          | {{getUnsavedValue 'cardDescription' _id getDescription}}
         .edit-controls.clearfix
           button.primary(type="submit") {{_ 'save'}}
           a.fa.fa-times-thin.js-close-inlined-form
       else
         a.js-open-inlined-form
-          if description
+          if getDescription
             +viewer
-              = description
+              = getDescription
           else
             | {{_ 'edit'}}
         if (hasUnsavedValue 'cardDescription' _id)
@@ -109,10 +116,10 @@ template(name="cardDetails")
             a.js-open-inlined-form {{_ 'view-it'}}
             = ' - '
             a.js-close-inlined-form {{_ 'discard'}}
-    else if description
+    else if getDescription
       h3.card-details-item-title {{_ 'description'}}
       +viewer
-        = description
+        = getDescription
 
     .card-details-items
       .card-details-item.card-details-item-name
@@ -179,7 +186,7 @@ template(name="cardDetails")
 
 template(name="editCardTitleForm")
   textarea.js-edit-card-title(rows='1' autofocus)
-    = title
+    = getTitle
   .edit-controls.clearfix
     button.primary.confirm.js-submit-edit-card-title-form(type="submit") {{_ 'save'}}
     a.fa.fa-times-thin.js-close-inlined-form
@@ -230,7 +237,7 @@ template(name="moveCardPopup")
 template(name="copyCardPopup")
   label(for='copy-card-title') {{_ 'title'}}:
   textarea#copy-card-title.minicard-composer-textarea.js-card-title(autofocus)
-    = title
+    = getTitle
   +boardsAndLists
 
 template(name="copyChecklistToManyCardsPopup")

+ 1 - 1
client/components/cards/cardDetails.js

@@ -274,7 +274,7 @@ BlazeComponent.extendComponent({
   close(isReset = false) {
     if (this.isOpen.get() && !isReset) {
       const draft = this.getValue().trim();
-      if (draft !== Cards.findOne(Session.get('currentCard')).description) {
+      if (draft !== Cards.findOne(Session.get('currentCard')).getDescription()) {
         UnsavedEdits.set(this._getUnsavedEditKey(), this.getValue());
       }
     }

+ 6 - 0
client/components/cards/cardDetails.styl

@@ -47,6 +47,12 @@
       margin: 7px 0 0
       padding: 0
 
+    .linked-card-location
+      font-style: italic
+      font-size: 1em
+      margin-bottom: 0
+      & p
+        margin-bottom: 0
 
     form.inlined-form
       margin-top: 5px

+ 4 - 4
client/components/cards/cardTime.jade

@@ -3,10 +3,10 @@ template(name="editCardSpentTime")
     form.edit-time
       .fields
         label(for="time") {{_ 'time'}}
-        input.js-time-field#time(type="number" step="0.01" name="time" value="{{card.spentTime}}" placeholder=timeFormat autofocus)
+        input.js-time-field#time(type="number" step="0.01" name="time" value="{{card.getSpentTime}}" placeholder=timeFormat autofocus)
         label(for="overtime") {{_ 'overtime'}}
         a.js-toggle-overtime
-          .materialCheckBox#overtime(class="{{#if card.isOvertime}}is-checked{{/if}}" name="overtime")
+          .materialCheckBox#overtime(class="{{#if getIsOvertime}}is-checked{{/if}}" name="overtime")
 
       if error.get
         .warning {{_ error.get}}
@@ -15,8 +15,8 @@ template(name="editCardSpentTime")
 
 template(name="timeBadge")
   if canModifyCard
-    a.js-edit-time.card-time(title="{{showTitle}}" class="{{#if isOvertime}}card-label-red{{else}}card-label-green{{/if}}")
+    a.js-edit-time.card-time(title="{{showTitle}}" class="{{#if getIsOvertime}}card-label-red{{else}}card-label-green{{/if}}")
       | {{showTime}}
   else
-    a.card-time(title="{{showTitle}}" class="{{#if isOvertime}}card-label-red{{else}}card-label-green{{/if}}")
+    a.card-time(title="{{showTitle}}" class="{{#if getIsOvertime}}card-label-red{{else}}card-label-green{{/if}}")
       | {{showTime}}

+ 8 - 11
client/components/cards/cardTime.js

@@ -7,17 +7,17 @@ BlazeComponent.extendComponent({
     this.card = this.data();
   },
   toggleOvertime() {
-    this.card.isOvertime = !this.card.isOvertime;
+    this.card.setIsOvertime(!this.card.getIsOvertime());
     $('#overtime .materialCheckBox').toggleClass('is-checked');
 
     $('#overtime').toggleClass('is-checked');
   },
   storeTime(spentTime, isOvertime) {
     this.card.setSpentTime(spentTime);
-    this.card.setOvertime(isOvertime);
+    this.card.setIsOvertime(isOvertime);
   },
   deleteTime() {
-    this.card.unsetSpentTime();
+    this.card.setSpentTime(null);
   },
   events() {
     return [{
@@ -26,7 +26,7 @@ BlazeComponent.extendComponent({
         evt.preventDefault();
 
         const spentTime = parseFloat(evt.target.time.value);
-        const isOvertime = this.card.isOvertime;
+        const isOvertime = this.card.getIsOvertime();
 
         if (spentTime >= 0) {
           this.storeTime(spentTime, isOvertime);
@@ -55,17 +55,14 @@ BlazeComponent.extendComponent({
     self.time = ReactiveVar();
   },
   showTitle() {
-    if (this.data().isOvertime) {
-      return `${TAPi18n.__('overtime')} ${this.data().spentTime} ${TAPi18n.__('hours')}`;
+    if (this.data().getIsOvertime()) {
+      return `${TAPi18n.__('overtime')} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`;
     } else {
-      return `${TAPi18n.__('card-spent')} ${this.data().spentTime} ${TAPi18n.__('hours')}`;
+      return `${TAPi18n.__('card-spent')} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`;
     }
   },
   showTime() {
-    return this.data().spentTime;
-  },
-  isOvertime() {
-    return this.data().isOvertime;
+    return this.data().getSpentTime();
   },
   events() {
     return [{

+ 3 - 1
client/components/cards/checklists.js

@@ -74,8 +74,10 @@ BlazeComponent.extendComponent({
     event.preventDefault();
     const textarea = this.find('textarea.js-add-checklist-item');
     const title = textarea.value.trim();
-    const cardId = this.currentData().cardId;
+    let cardId = this.currentData().cardId;
     const card = Cards.findOne(cardId);
+    if (card.isLinked())
+      cardId = card.linkedId;
 
     if (title) {
       Checklists.insert({

+ 24 - 18
client/components/cards/minicard.jade

@@ -1,5 +1,7 @@
 template(name="minicard")
-  .minicard
+  .minicard(
+    class="{{#if isLinkedCard}}linked-card{{/if}}"
+    class="{{#if isLinkedBoard}}linked-board{{/if}}")
     if cover
       .minicard-cover(style="background-image: url('{{cover.url}}');")
     if labels
@@ -13,8 +15,16 @@ template(name="minicard")
       if $eq 'prefix-with-parent' currentBoard.presentParentTask
         .parent-prefix
           | {{ parentCardName }}
+      if isLinkedBoard
+        a.js-linked-link
+          span.linked-icon.fa.fa-folder
+      else if isLinkedCard
+        a.js-linked-link
+          span.linked-icon.fa.fa-id-card
+      if getArchived
+        span.linked-icon.linked-archived.fa.fa-archive
       +viewer
-        | {{ title }}
+        = getTitle
       if $eq 'subtext-with-full-path' currentBoard.presentParentTask
         .parent-subtext
           | {{ parentString ' > ' }}
@@ -23,23 +33,19 @@ template(name="minicard")
           | {{ parentCardName }}
 
     .dates
-      if receivedAt
-        unless startAt
-          unless dueAt
-            unless endAt
+      if getReceived
+        unless getStart
+          unless getDue
+            unless getEnd
               .date
                 +minicardReceivedDate
-      if startAt
+      if getStart
         .date
           +minicardStartDate
-      if dueAt
-        unless endAt
-          .date
-            +minicardDueDate
-      if endAt
+      if getDue
         .date
-          +minicardEndDate
-      if spentTime
+          +minicardDueDate
+      if getSpentTime
         .date
           +cardSpentTime
 
@@ -53,9 +59,9 @@ template(name="minicard")
               +viewer
                 = trueValue
 
-    if members
+    if getMembers
       .minicard-members.js-minicard-members
-        each members
+        each getMembers
           +userAvatar(userId=this)
 
     .badges
@@ -63,8 +69,8 @@ template(name="minicard")
         .badge(title="{{_ 'card-comments-title' comments.count }}")
           span.badge-icon.fa.fa-comment-o.badge-comment
           span.badge-text= comments.count
-      if description
-        .badge.badge-state-image-only(title=description)
+      if getDescription
+        .badge.badge-state-image-only(title=getDescription)
           span.badge-icon.fa.fa-align-left
       if attachments.count
         .badge

+ 11 - 0
client/components/cards/minicard.js

@@ -6,4 +6,15 @@ BlazeComponent.extendComponent({
   template() {
     return 'minicard';
   },
+
+  events() {
+    return [{
+      'click .js-linked-link' () {
+        if (this.data().isLinkedCard())
+          Utils.goCardId(this.data().linkedId);
+        else if (this.data().isLinkedBoard())
+          Utils.goBoardId(this.data().linkedId);
+      },
+    }];
+  },
 }).register('minicard');

+ 12 - 0
client/components/cards/minicard.styl

@@ -44,6 +44,16 @@
   transition: transform 0.2s,
               border-radius 0.2s
 
+  &.linked-board
+  &.linked-card
+    .linked-icon
+      display: inline-block
+      margin-right: 11px
+      vertical-align: baseline
+      font-size: 0.9em
+    .linked-archived
+      color: #937760
+
   .is-selected &
     transform: translateX(11px)
     border-bottom-right-radius: 0
@@ -87,6 +97,8 @@
   .minicard-title
     p:last-child
       margin-bottom: 0
+    .viewer
+      display: inline-block
   .dates
     display: flex;
     flex-direction: row;

+ 1 - 0
client/components/cards/subtasks.js

@@ -29,6 +29,7 @@ BlazeComponent.extendComponent({
         boardId: targetBoard._id,
         sort: sortIndex,
         swimlaneId,
+        type: 'cardType-card',
       });
 
       // In case the filter is active we need to add the newly inserted card in

+ 3 - 0
client/components/forms/forms.styl

@@ -225,9 +225,12 @@ textarea
 
 .edit-controls,
 .add-controls
+  display: flex
+  align-items: baseline
   margin-top: 0
 
   button[type=submit]
+  input[type=button]
     float: left
     height: 32px
     margin-top: -2px

+ 11 - 0
client/components/lists/list.styl

@@ -187,3 +187,14 @@
       padding: 7px
       top: -@padding
       right: 17px
+
+.link-board-wrapper
+  display: flex
+  align-items: baseline
+
+  .js-link-board
+    margin-left: 15px
+
+.search-card-results
+  max-height: 250px
+  overflow: hidden

+ 53 - 1
client/components/lists/listBody.jade

@@ -34,8 +34,60 @@ template(name="addCardForm")
 
   .add-controls.clearfix
     button.primary.confirm(type="submit") {{_ 'add'}}
-    a.fa.fa-times-thin.js-close-inlined-form
+    span.quiet
+      | {{_ 'or'}}
+      a.js-link {{_ 'link'}}
+    span.quiet
+      |  
+      | /
+      a.js-search {{_ 'search'}}
 
 template(name="autocompleteLabelLine")
   .minicard-label(class="card-label-{{colorName}}" title=labelName)
   span(class="{{#if hasNoName}}quiet{{/if}}")= labelName
+
+template(name="linkCardPopup")
+  label {{_ 'boards'}}:
+  .link-board-wrapper
+    select.js-select-boards
+      each boards
+        if $eq _id currentBoard._id
+          option(value="{{_id}}" selected) {{_ 'current'}}
+        else
+          option(value="{{_id}}") {{title}}
+    input.primary.confirm.js-link-board(type="button" value="{{_ 'link'}}")
+
+  label {{_ 'swimlanes'}}:
+  select.js-select-swimlanes
+    each swimlanes
+      option(value="{{_id}}") {{title}}
+
+  label {{_ 'lists'}}:
+  select.js-select-lists
+    each lists
+      option(value="{{_id}}") {{title}}
+
+  label {{_ 'cards'}}:
+  select.js-select-cards
+    each cards
+      option(value="{{_id}}") {{title}}
+
+  .edit-controls.clearfix
+    input.primary.confirm.js-done(type="button" value="{{_ 'link'}}")
+
+template(name="searchCardPopup")
+  label {{_ 'boards'}}:
+  .link-board-wrapper
+    select.js-select-boards
+      each boards
+        if $eq _id currentBoard._id
+          option(value="{{_id}}" selected) {{_ 'current'}}
+        else
+          option(value="{{_id}}") {{title}}
+  form.js-search-term-form
+    input(type="text" name="searchTerm" placeholder="{{_ 'search-example'}}" autofocus)
+  .list-body.js-perfect-scrollbar.search-card-results
+    .minicards.clearfix.js-minicards
+      each results
+        a.minicard-wrapper.js-minicard
+          +minicard(this)

+ 201 - 0
client/components/lists/listBody.js

@@ -1,3 +1,5 @@
+const subManager = new SubsManager();
+
 BlazeComponent.extendComponent({
   mixins() {
     return [Mixins.PerfectScrollbar];
@@ -55,6 +57,7 @@ BlazeComponent.extendComponent({
         boardId: boardId._id,
         sort: sortIndex,
         swimlaneId,
+        type: 'cardType-card',
       });
       // In case the filter is active we need to add the newly inserted card in
       // the list of exceptions -- cards that are not filtered. Otherwise the
@@ -197,6 +200,8 @@ BlazeComponent.extendComponent({
   events() {
     return [{
       keydown: this.pressKey,
+      'click .js-link': Popup.open('linkCard'),
+      'click .js-search': Popup.open('searchCard'),
     }];
   },
 
@@ -268,3 +273,199 @@ BlazeComponent.extendComponent({
     });
   },
 }).register('addCardForm');
+
+BlazeComponent.extendComponent({
+  onCreated() {
+    // Prefetch first non-current board id
+    const boardId = Boards.findOne({
+      archived: false,
+      'members.userId': Meteor.userId(),
+      _id: {$ne: Session.get('currentBoard')},
+    }, {
+      sort: ['title'],
+    })._id;
+    // Subscribe to this board
+    subManager.subscribe('board', boardId);
+    this.selectedBoardId = new ReactiveVar(boardId);
+    this.selectedSwimlaneId = new ReactiveVar('');
+    this.selectedListId = new ReactiveVar('');
+
+    this.boardId = Session.get('currentBoard');
+    // In order to get current board info
+    subManager.subscribe('board', this.boardId);
+    this.board = Boards.findOne(this.boardId);
+    // List where to insert card
+    const list = $(Popup._getTopStack().openerElement).closest('.js-list');
+    this.listId = Blaze.getData(list[0])._id;
+    // Swimlane where to insert card
+    const swimlane = $(Popup._getTopStack().openerElement).closest('.js-swimlane');
+    this.swimlaneId = '';
+    const boardView = Meteor.user().profile.boardView;
+    if (boardView === 'board-view-swimlanes')
+      this.swimlaneId = Blaze.getData(swimlane[0])._id;
+    else if (boardView === 'board-view-lists')
+      this.swimlaneId = Swimlanes.findOne({boardId: this.boardId})._id;
+  },
+
+  boards() {
+    const boards = Boards.find({
+      archived: false,
+      'members.userId': Meteor.userId(),
+      _id: {$ne: Session.get('currentBoard')},
+    }, {
+      sort: ['title'],
+    });
+    return boards;
+  },
+
+  swimlanes() {
+    const swimlanes = Swimlanes.find({boardId: this.selectedBoardId.get()});
+    if (swimlanes.count())
+      this.selectedSwimlaneId.set(swimlanes.fetch()[0]._id);
+    return swimlanes;
+  },
+
+  lists() {
+    const lists = Lists.find({boardId: this.selectedBoardId.get()});
+    if (lists.count())
+      this.selectedListId.set(lists.fetch()[0]._id);
+    return lists;
+  },
+
+  cards() {
+    return Cards.find({
+      boardId: this.selectedBoardId.get(),
+      swimlaneId: this.selectedSwimlaneId.get(),
+      listId: this.selectedListId.get(),
+      archived: false,
+      linkedId: null,
+      _id: {$nin: this.board.cards().map((card) => { return card.linkedId || card._id; })},
+    });
+  },
+
+  events() {
+    return [{
+      'change .js-select-boards'(evt) {
+        this.selectedBoardId.set($(evt.currentTarget).val());
+        subManager.subscribe('board', this.selectedBoardId.get());
+      },
+      'change .js-select-swimlanes'(evt) {
+        this.selectedSwimlaneId.set($(evt.currentTarget).val());
+      },
+      'change .js-select-lists'(evt) {
+        this.selectedListId.set($(evt.currentTarget).val());
+      },
+      'click .js-done' (evt) {
+        // LINK CARD
+        evt.stopPropagation();
+        evt.preventDefault();
+        const _id = Cards.insert({
+          title: $('.js-select-cards option:selected').text(), //dummy
+          listId: this.listId,
+          swimlaneId: this.swimlaneId,
+          boardId: this.boardId,
+          sort: Lists.findOne(this.listId).cards().count(),
+          type: 'cardType-linkedCard',
+          linkedId: $('.js-select-cards option:selected').val(),
+        });
+        Filter.addException(_id);
+        Popup.close();
+      },
+      'click .js-link-board' (evt) {
+        //LINK BOARD
+        evt.stopPropagation();
+        evt.preventDefault();
+        const impBoardId = $('.js-select-boards option:selected').val();
+        const _id = Cards.insert({
+          title: $('.js-select-boards option:selected').text(), //dummy
+          listId: this.listId,
+          swimlaneId: this.swimlaneId,
+          boardId: this.boardId,
+          sort: Lists.findOne(this.listId).cards().count(),
+          type: 'cardType-linkedBoard',
+          linkedId: impBoardId,
+        });
+        Filter.addException(_id);
+        Popup.close();
+      },
+    }];
+  },
+}).register('linkCardPopup');
+
+BlazeComponent.extendComponent({
+  mixins() {
+    return [Mixins.PerfectScrollbar];
+  },
+
+  onCreated() {
+    // Prefetch first non-current board id
+    const boardId = Boards.findOne({
+      archived: false,
+      'members.userId': Meteor.userId(),
+      _id: {$ne: Session.get('currentBoard')},
+    })._id;
+    // Subscribe to this board
+    subManager.subscribe('board', boardId);
+    this.selectedBoardId = new ReactiveVar(boardId);
+
+    this.boardId = Session.get('currentBoard');
+    // In order to get current board info
+    subManager.subscribe('board', this.boardId);
+    const board = Boards.findOne(this.boardId);
+    // List where to insert card
+    const list = $(Popup._getTopStack().openerElement).closest('.js-list');
+    this.listId = Blaze.getData(list[0])._id;
+    // Swimlane where to insert card
+    const swimlane = $(Popup._getTopStack().openerElement).closest('.js-swimlane');
+    this.swimlaneId = '';
+    if (board.view === 'board-view-swimlanes')
+      this.swimlaneId = Blaze.getData(swimlane[0])._id;
+    else
+      this.swimlaneId = Swimlanes.findOne({boardId: this.boardId})._id;
+    this.term = new ReactiveVar('');
+  },
+
+  boards() {
+    const boards = Boards.find({
+      archived: false,
+      'members.userId': Meteor.userId(),
+      _id: {$ne: Session.get('currentBoard')},
+    }, {
+      sort: ['title'],
+    });
+    return boards;
+  },
+
+  results() {
+    const board = Boards.findOne(this.selectedBoardId.get());
+    return board.searchCards(this.term.get(), true);
+  },
+
+  events() {
+    return [{
+      'change .js-select-boards'(evt) {
+        this.selectedBoardId.set($(evt.currentTarget).val());
+        subManager.subscribe('board', this.selectedBoardId.get());
+      },
+      'submit .js-search-term-form'(evt) {
+        evt.preventDefault();
+        this.term.set(evt.target.searchTerm.value);
+      },
+      'click .js-minicard'(evt) {
+        // LINK CARD
+        const card = Blaze.getData(evt.currentTarget);
+        const _id = Cards.insert({
+          title: card.title, //dummy
+          listId: this.listId,
+          swimlaneId: this.swimlaneId,
+          boardId: this.boardId,
+          sort: Lists.findOne(this.listId).cards().count(),
+          type: 'cardType-linkedCard',
+          linkedId: card._id,
+        });
+        Filter.addException(_id);
+        Popup.close();
+      },
+    }];
+  },
+}).register('searchCardPopup');

+ 3 - 2
client/components/users/userAvatar.js

@@ -134,8 +134,9 @@ BlazeComponent.extendComponent({
 
 Template.cardMembersPopup.helpers({
   isCardMember() {
-    const cardId = Template.parentData()._id;
-    const cardMembers = Cards.findOne(cardId).members || [];
+    const card = Template.parentData();
+    const cardMembers = card.getMembers();
+
     return _.contains(cardMembers, this.userId);
   },
 

+ 124 - 127
client/lib/filter.js

@@ -187,27 +187,27 @@ class AdvancedFilter {
       if (commands[i].cmd) {
         switch (commands[i].cmd) {
         case '(':
-        {
-          level++;
-          if (start === -1) start = i;
-          continue;
-        }
+          {
+            level++;
+            if (start === -1) start = i;
+            continue;
+          }
         case ')':
-        {
-          level--;
-          commands.splice(i, 1);
-          i--;
-          continue;
-        }
-        default:
-        {
-          if (level > 0) {
-            subcommands.push(commands[i]);
+          {
+            level--;
             commands.splice(i, 1);
             i--;
             continue;
           }
-        }
+        default:
+          {
+            if (level > 0) {
+              subcommands.push(commands[i]);
+              commands.splice(i, 1);
+              i--;
+              continue;
+            }
+          }
         }
       }
     }
@@ -229,113 +229,112 @@ class AdvancedFilter {
         case '=':
         case '==':
         case '===':
-        {
-          const field = commands[i - 1].cmd;
-          const str = commands[i + 1].cmd;
-          if (commands[i + 1].regex)
           {
-            const match = str.match(new RegExp('^/(.*?)/([gimy]*)$'));
-            let regex = null;
-            if (match.length > 2)
-              regex = new RegExp(match[1], match[2]);
+            const field = commands[i - 1].cmd;
+            const str = commands[i + 1].cmd;
+            if (commands[i + 1].regex)
+            {
+              const match = str.match(new RegExp('^/(.*?)/([gimy]*)$'));
+              let regex = null;
+              if (match.length > 2)
+                regex = new RegExp(match[1], match[2]);
+              else
+                regex = new RegExp(match[1]);
+              commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': regex };
+            }
             else
-              regex = new RegExp(match[1]);
-            commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': regex };
-          }
-          else
-          {
-            commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': {$in: [this._fieldValueToId(field, str), parseInt(str, 10)]} };
+            {
+              commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': {$in: [this._fieldValueToId(field, str), parseInt(str, 10)]} };
+            }
+            commands.splice(i - 1, 1);
+            commands.splice(i, 1);
+            //changed = true;
+            i--;
+            break;
           }
-          commands.splice(i - 1, 1);
-          commands.splice(i, 1);
-          //changed = true;
-          i--;
-          break;
-        }
         case '!=':
         case '!==':
-        {
-          const field = commands[i - 1].cmd;
-          const str = commands[i + 1].cmd;
-          if (commands[i + 1].regex)
           {
-            const match = str.match(new RegExp('^/(.*?)/([gimy]*)$'));
-            let regex = null;
-            if (match.length > 2)
-              regex = new RegExp(match[1], match[2]);
+            const field = commands[i - 1].cmd;
+            const str = commands[i + 1].cmd;
+            if (commands[i + 1].regex)
+            {
+              const match = str.match(new RegExp('^/(.*?)/([gimy]*)$'));
+              let regex = null;
+              if (match.length > 2)
+                regex = new RegExp(match[1], match[2]);
+              else
+                regex = new RegExp(match[1]);
+              commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': { $not: regex } };
+            }
             else
-              regex = new RegExp(match[1]);
-            commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': { $not: regex } };
-          }
-          else
-          {
-            commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': { $not: {$in: [this._fieldValueToId(field, str), parseInt(str, 10)]} } };
+            {
+              commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': { $not: {$in: [this._fieldValueToId(field, str), parseInt(str, 10)]} } };
+            }
+            commands.splice(i - 1, 1);
+            commands.splice(i, 1);
+            //changed = true;
+            i--;
+            break;
           }
-          commands.splice(i - 1, 1);
-          commands.splice(i, 1);
-          //changed = true;
-          i--;
-          break;
-        }
         case '>':
         case 'gt':
         case 'Gt':
         case 'GT':
-        {
-          const field = commands[i - 1].cmd;
-          const str = commands[i + 1].cmd;
-          commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': { $gt: parseInt(str, 10) } };
-          commands.splice(i - 1, 1);
-          commands.splice(i, 1);
-          //changed = true;
-          i--;
-          break;
-        }
+          {
+            const field = commands[i - 1].cmd;
+            const str = commands[i + 1].cmd;
+            commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': { $gt: parseInt(str, 10) } };
+            commands.splice(i - 1, 1);
+            commands.splice(i, 1);
+            //changed = true;
+            i--;
+            break;
+          }
         case '>=':
         case '>==':
         case 'gte':
         case 'Gte':
         case 'GTE':
-        {
-          const field = commands[i - 1].cmd;
-          const str = commands[i + 1].cmd;
-          commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': { $gte:  parseInt(str, 10) } };
-          commands.splice(i - 1, 1);
-          commands.splice(i, 1);
-          //changed = true;
-          i--;
-          break;
-        }
+          {
+            const field = commands[i - 1].cmd;
+            const str = commands[i + 1].cmd;
+            commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': { $gte:  parseInt(str, 10) } };
+            commands.splice(i - 1, 1);
+            commands.splice(i, 1);
+            //changed = true;
+            i--;
+            break;
+          }
         case '<':
         case 'lt':
         case 'Lt':
         case 'LT':
-        {
-          const field = commands[i - 1].cmd;
-          const str = commands[i + 1].cmd;
-          commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': { $lt:  parseInt(str, 10) } };
-          commands.splice(i - 1, 1);
-          commands.splice(i, 1);
-          //changed = true;
-          i--;
-          break;
-        }
+          {
+            const field = commands[i - 1].cmd;
+            const str = commands[i + 1].cmd;
+            commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': { $lt:  parseInt(str, 10) } };
+            commands.splice(i - 1, 1);
+            commands.splice(i, 1);
+            //changed = true;
+            i--;
+            break;
+          }
         case '<=':
         case '<==':
         case 'lte':
         case 'Lte':
         case 'LTE':
-        {
-          const field = commands[i - 1].cmd;
-          const str = commands[i + 1].cmd;
-          commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': { $lte:  parseInt(str, 10) } };
-          commands.splice(i - 1, 1);
-          commands.splice(i, 1);
-          //changed = true;
-          i--;
-          break;
-        }
-
+          {
+            const field = commands[i - 1].cmd;
+            const str = commands[i + 1].cmd;
+            commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': { $lte:  parseInt(str, 10) } };
+            commands.splice(i - 1, 1);
+            commands.splice(i, 1);
+            //changed = true;
+            i--;
+            break;
+          }
         }
       }
     }
@@ -350,45 +349,43 @@ class AdvancedFilter {
         case 'OR':
         case '|':
         case '||':
-        {
-          const op1 = commands[i - 1];
-          const op2 = commands[i + 1];
-          commands[i] = { $or: [op1, op2] };
-          commands.splice(i - 1, 1);
-          commands.splice(i, 1);
-          //changed = true;
-          i--;
-          break;
-        }
+          {
+            const op1 = commands[i - 1];
+            const op2 = commands[i + 1];
+            commands[i] = { $or: [op1, op2] };
+            commands.splice(i - 1, 1);
+            commands.splice(i, 1);
+            //changed = true;
+            i--;
+            break;
+          }
         case 'and':
         case 'And':
         case 'AND':
         case '&':
         case '&&':
-        {
-          const op1 = commands[i - 1];
-          const op2 = commands[i + 1];
-          commands[i] = { $and: [op1, op2] };
-          commands.splice(i - 1, 1);
-          commands.splice(i, 1);
-          //changed = true;
-          i--;
-          break;
-        }
-
+          {
+            const op1 = commands[i - 1];
+            const op2 = commands[i + 1];
+            commands[i] = { $and: [op1, op2] };
+            commands.splice(i - 1, 1);
+            commands.splice(i, 1);
+            //changed = true;
+            i--;
+            break;
+          }
         case 'not':
         case 'Not':
         case 'NOT':
         case '!':
-        {
-          const op1 = commands[i + 1];
-          commands[i] = { $not: op1 };
-          commands.splice(i + 1, 1);
-          //changed = true;
-          i--;
-          break;
-        }
-
+          {
+            const op1 = commands[i + 1];
+            commands[i] = { $not: op1 };
+            commands.splice(i + 1, 1);
+            //changed = true;
+            i--;
+            break;
+          }
         }
       }
     }

+ 7 - 0
i18n/ar.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "مثل « todo list » على سبيل المثال",
     "cancel": "إلغاء",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "%s تعليقات لهذه البطاقة",
     "card-delete-notice": "هذا حذف أبديّ . سوف تفقد كل الإجراءات المنوطة بهذه البطاقة",
     "card-delete-pop": "سيتم إزالة جميع الإجراءات من تبعات النشاط، وأنك لن تكون قادرا على إعادة فتح البطاقة. لا يوجد التراجع.",
@@ -135,6 +136,9 @@
     "cards": "بطاقات",
     "cards-count": "بطاقات",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Change",
     "change-avatar": "تعديل الصورة الشخصية",
     "change-password": "تغيير كلمة المرور",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "نسخ رابط البطاقة إلى الحافظة",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "نسخ البطاقة",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "إنشاء لوحة",
     "home": "الرئيسية",
     "import": "Import",
+    "link": "Link",
     "import-board": "استيراد لوحة",
     "import-board-c": "استيراد لوحة",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/bg.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Like “Bucket List” for example",
     "cancel": "Cancel",
     "card-archived": "Картата е преместена в Кошчето.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "Тази карта има %s коментар.",
     "card-delete-notice": "Deleting is permanent. You will lose all actions associated with this card.",
     "card-delete-pop": "All actions will be removed from the activity feed and you won't be able to re-open the card. There is no undo.",
@@ -135,6 +136,9 @@
     "cards": "Карти",
     "cards-count": "Карти",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Промени",
     "change-avatar": "Промени аватара",
     "change-password": "Промени паролата",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Копирай връзката на картата в клипборда",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Копирай картата",
     "copyChecklistToManyCardsPopup-title": "Копирай чеклисти в други карти",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Create Board",
     "home": "Начало",
     "import": "Import",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/br.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Like “Bucket List” for example",
     "cancel": "Cancel",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "This card has %s comment.",
     "card-delete-notice": "Deleting is permanent. You will lose all actions associated with this card.",
     "card-delete-pop": "All actions will be removed from the activity feed and you won't be able to re-open the card. There is no undo.",
@@ -135,6 +136,9 @@
     "cards": "Kartennoù",
     "cards-count": "Kartennoù",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Change",
     "change-avatar": "Change Avatar",
     "change-password": "Kemmañ ger-tremen",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Create Board",
     "home": "Home",
     "import": "Import",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/ca.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Igual que “Bucket List”,  per exemple",
     "cancel": "Cancel·la",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "Aquesta fitxa té %s comentaris.",
     "card-delete-notice": "L'esborrat és permanent. Perdreu totes les accions associades a aquesta fitxa.",
     "card-delete-pop": "Totes les accions s'eliminaran de l'activitat i no podreu tornar a obrir la fitxa. No es pot desfer.",
@@ -135,6 +136,9 @@
     "cards": "Fitxes",
     "cards-count": "Fitxes",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Canvia",
     "change-avatar": "Canvia Avatar",
     "change-password": "Canvia la clau",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copia l'enllaç de la ftixa al porta-retalls",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copia la fitxa",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Títols de fitxa i Descripcions de destí en aquest format JSON",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Crea tauler",
     "home": "Inici",
     "import": "importa",
+    "link": "Link",
     "import-board": "Importa tauler",
     "import-board-c": "Importa tauler",
     "import-board-title-trello": "Importa tauler des de Trello",

+ 7 - 0
i18n/cs.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Například \"Než mě odvedou\"",
     "cancel": "Zrušit",
     "card-archived": "Karta byla přesunuta do koše.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "Tato karta má %s komentářů.",
     "card-delete-notice": "Smazání je trvalé. Přijdete o všechny akce asociované s touto kartou.",
     "card-delete-pop": "Všechny akce budou odstraněny z kanálu aktivity a nebude možné kartu znovu otevřít. Toto nelze vrátit zpět.",
@@ -135,6 +136,9 @@
     "cards": "Karty",
     "cards-count": "Karty",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Změnit",
     "change-avatar": "Změnit avatar",
     "change-password": "Změnit heslo",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Kopírovat adresu karty do mezipaměti",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Kopírovat kartu",
     "copyChecklistToManyCardsPopup-title": "Kopírovat checklist do více karet",
     "copyChecklistToManyCardsPopup-instructions": "Názvy a popisy cílové karty v tomto formátu JSON",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Vytvořit tablo",
     "home": "Domů",
     "import": "Import",
+    "link": "Link",
     "import-board": "Importovat tablo",
     "import-board-c": "Importovat tablo",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/de.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "z.B. \"Löffelliste\"",
     "cancel": "Abbrechen",
     "card-archived": "Diese Karte wurde in den Papierkorb verschoben",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "Diese Karte hat %s Kommentar(e).",
     "card-delete-notice": "Löschen kann nicht rückgängig gemacht werden. Alle Aktionen, die dieser Karte zugeordnet sind, werden ebenfalls gelöscht.",
     "card-delete-pop": "Alle Aktionen werden aus dem Aktivitätsfeed entfernt und die Karte kann nicht wiedereröffnet werden. Die Aktion kann nicht rückgängig gemacht werden.",
@@ -135,6 +136,9 @@
     "cards": "Karten",
     "cards-count": "Karten",
     "casSignIn": "Mit CAS anmelden",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Ändern",
     "change-avatar": "Profilbild ändern",
     "change-password": "Passwort ändern",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Wollen Sie die Teilaufgabe wirklich löschen?",
     "confirm-checklist-delete-dialog": "Wollen Sie die Checkliste wirklich löschen?",
     "copy-card-link-to-clipboard": "Kopiere Link zur Karte in die Zwischenablage",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Karte kopieren",
     "copyChecklistToManyCardsPopup-title": "Checklistenvorlage in mehrere Karten kopieren",
     "copyChecklistToManyCardsPopup-instructions": "Titel und Beschreibungen der Zielkarten im folgenden JSON-Format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Board erstellen",
     "home": "Home",
     "import": "Importieren",
+    "link": "Link",
     "import-board": "Board importieren",
     "import-board-c": "Board importieren",
     "import-board-title-trello": "Board von Trello importieren",

+ 7 - 0
i18n/el.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Like “Bucket List” for example",
     "cancel": "Ακύρωση",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "This card has %s comment.",
     "card-delete-notice": "Deleting is permanent. You will lose all actions associated with this card.",
     "card-delete-pop": "All actions will be removed from the activity feed and you won't be able to re-open the card. There is no undo.",
@@ -135,6 +136,9 @@
     "cards": "Κάρτες",
     "cards-count": "Κάρτες",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Αλλαγή",
     "change-avatar": "Change Avatar",
     "change-password": "Αλλαγή Κωδικού",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Create Board",
     "home": "Home",
     "import": "Εισαγωγή",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/en-GB.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Like “Bucket List” for example",
     "cancel": "Cancel",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "This card has %s comment.",
     "card-delete-notice": "Deleting is permanent. You will lose all actions associated with this card.",
     "card-delete-pop": "All actions will be removed from the activity feed and you won't be able to re-open the card. There is no undo.",
@@ -135,6 +136,9 @@
     "cards": "Cards",
     "cards-count": "Cards",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Change",
     "change-avatar": "Change Avatar",
     "change-password": "Change Password",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Create Board",
     "home": "Home",
     "import": "Import",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/en.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Like “Bucket List” for example",
     "cancel": "Cancel",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "This card has %s comment.",
     "card-delete-notice": "Deleting is permanent. You will lose all actions associated with this card.",
     "card-delete-pop": "All actions will be removed from the activity feed and you won't be able to re-open the card. There is no undo.",
@@ -135,6 +136,9 @@
     "cards": "Cards",
     "cards-count": "Cards",
     "casSignIn" : "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Change",
     "change-avatar": "Change Avatar",
     "change-password": "Change Password",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Create Board",
     "home": "Home",
     "import": "Import",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/eo.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Like “Bucket List” for example",
     "cancel": "Cancel",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "This card has %s comment.",
     "card-delete-notice": "Deleting is permanent. You will lose all actions associated with this card.",
     "card-delete-pop": "All actions will be removed from the activity feed and you won't be able to re-open the card. There is no undo.",
@@ -135,6 +136,9 @@
     "cards": "Kartoj",
     "cards-count": "Kartoj",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Ŝanĝi",
     "change-avatar": "Change Avatar",
     "change-password": "Ŝangi pasvorton",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Krei tavolon",
     "home": "Hejmo",
     "import": "Importi",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/es-AR.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Como \"Lista de Contenedores\" por ejemplo",
     "cancel": "Cancelar",
     "card-archived": "Esta tarjeta es movida a la Papelera de Reciclaje",
+    "board-archived": "This board is moved to Recycle Bin.",
     "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.",
@@ -135,6 +136,9 @@
     "cards": "Tarjetas",
     "cards-count": "Tarjetas",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Cambiar",
     "change-avatar": "Cambiar Avatar",
     "change-password": "Cambiar Contraseña",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copiar enlace a tarjeta en el portapapeles",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copiar Tarjeta",
     "copyChecklistToManyCardsPopup-title": "Copiar Plantilla Checklist a Muchas Tarjetas",
     "copyChecklistToManyCardsPopup-instructions": "Títulos y Descripciones de la Tarjeta Destino en este formato JSON",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Crear Tablero",
     "home": "Inicio",
     "import": "Importar",
+    "link": "Link",
     "import-board": "importar tablero",
     "import-board-c": "Importar tablero",
     "import-board-title-trello": "Importar tablero de Trello",

+ 7 - 0
i18n/es.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Como “Cosas por hacer” por ejemplo",
     "cancel": "Cancelar",
     "card-archived": "Esta tarjeta se ha enviado a la papelera de reciclaje.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "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.",
@@ -135,6 +136,9 @@
     "cards": "Tarjetas",
     "cards-count": "Tarjetas",
     "casSignIn": "Iniciar sesión con CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Cambiar",
     "change-avatar": "Cambiar el avatar",
     "change-password": "Cambiar la contraseña",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "¿Seguro que quieres eliminar la subtarea?",
     "confirm-checklist-delete-dialog": "¿Seguro que quieres eliminar la lista de verificación?",
     "copy-card-link-to-clipboard": "Copiar el enlace de la tarjeta al portapapeles",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copiar la tarjeta",
     "copyChecklistToManyCardsPopup-title": "Copiar la plantilla de la lista de verificación en varias tarjetas",
     "copyChecklistToManyCardsPopup-instructions": "Títulos y descripciones de las tarjetas de destino en formato JSON",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Crear tablero",
     "home": "Inicio",
     "import": "Importar",
+    "link": "Link",
     "import-board": "importar un tablero",
     "import-board-c": "Importar un tablero",
     "import-board-title-trello": "Importar un tablero desde Trello",

+ 7 - 0
i18n/eu.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Esaterako \"Pertz zerrenda\"",
     "cancel": "Utzi",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "Txartel honek iruzkin %s  dauka.",
     "card-delete-notice": "Ezabaketa behin betiko da. Txartel honi lotutako ekintza guztiak galduko dituzu.",
     "card-delete-pop": "Ekintza guztiak ekintza jariotik kenduko dira eta ezin izango duzu txartela berrireki. Ez dago desegiterik.",
@@ -135,6 +136,9 @@
     "cards": "Txartelak",
     "cards-count": "Txartelak",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Aldatu",
     "change-avatar": "Aldatu avatarra",
     "change-password": "Aldatu pasahitza",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Kopiatu txartela arbelera",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Kopiatu txartela",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Sortu arbela",
     "home": "Hasiera",
     "import": "Inportatu",
+    "link": "Link",
     "import-board": "inportatu arbela",
     "import-board-c": "Inportatu arbela",
     "import-board-title-trello": "Inportatu arbela Trellotik",

+ 7 - 0
i18n/fa.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "برای مثال چیزی شبیه \"لیست سبدها\"",
     "cancel": "انصراف",
     "card-archived": "این کارت به سطل زباله ریخته شده است",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "این کارت دارای %s نظر است.",
     "card-delete-notice": "حذف دائمی. تمامی موارد مرتبط با این کارت از بین خواهند رفت.",
     "card-delete-pop": "همه اقدامات از این پردازه (خوراک) حذف خواهد شد و امکان بازگرداندن کارت وجود نخواهد داشت.",
@@ -135,6 +136,9 @@
     "cards": "کارت‌ها",
     "cards-count": "کارت‌ها",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "تغییر",
     "change-avatar": "تغییر تصویر",
     "change-password": "تغییر کلمه عبور",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "مطمئنا چک لیست پاک شود؟",
     "copy-card-link-to-clipboard": "درج پیوند کارت در حافظه",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "کپی کارت",
     "copyChecklistToManyCardsPopup-title": "کپی قالب کارت به کارت‌های متعدد",
     "copyChecklistToManyCardsPopup-instructions": "عنوان و توضیحات کارت مقصد در این قالب JSON",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "ایجاد تخته",
     "home": "خانه",
     "import": "وارد کردن",
+    "link": "Link",
     "import-board": "وارد کردن تخته",
     "import-board-c": "وارد کردن تخته",
     "import-board-title-trello": "وارد کردن تخته از Trello",

+ 7 - 0
i18n/fi.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Kuten “Laatikko lista” esimerkiksi",
     "cancel": "Peruuta",
     "card-archived": "Tämä kortti on siirretty roskakoriin.",
+    "board-archived": "Tämä taulu on siirretty roskakoriin.",
     "card-comments-title": "Tässä kortissa on %s kommenttia.",
     "card-delete-notice": "Poistaminen on lopullista. Menetät kaikki toimet jotka on liitetty tähän korttiin.",
     "card-delete-pop": "Kaikki toimet poistetaan toimintasyötteestä ja et tule pystymään uudelleenavaamaan korttia. Tätä ei voi peruuttaa.",
@@ -135,6 +136,9 @@
     "cards": "Kortit",
     "cards-count": "korttia",
     "casSignIn": "CAS kirjautuminen",
+    "cardType-card": "Kortti",
+    "cardType-linkedCard": "Linkitetty kortti",
+    "cardType-linkedBoard": "Linkitetty taulu",
     "change": "Muokkaa",
     "change-avatar": "Muokkaa profiilikuvaa",
     "change-password": "Vaihda salasana",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Oletko varma että haluat poistaa alitehtävän?",
     "confirm-checklist-delete-dialog": "Oletko varma että haluat poistaa tarkistuslistan?",
     "copy-card-link-to-clipboard": "Kopioi kortin linkki leikepöydälle",
+    "linkCardPopup-title": "Linkitä kortti",
+    "searchCardPopup-title": "Etsi kortti",
     "copyCardPopup-title": "Kopioi kortti",
     "copyChecklistToManyCardsPopup-title": "Kopioi tarkistuslistan malli monille korteille",
     "copyChecklistToManyCardsPopup-instructions": "Kohde korttien otsikot ja kuvaukset JSON muodossa",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Luo taulu",
     "home": "Koti",
     "import": "Tuo",
+    "link": "Linkitä",
     "import-board": "tuo taulu",
     "import-board-c": "Tuo taulu",
     "import-board-title-trello": "Tuo taulu Trellosta",

+ 7 - 0
i18n/fr.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Comme « todo list » par exemple",
     "cancel": "Annuler",
     "card-archived": "Cette carte est déplacée vers la corbeille.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "Cette carte a %s commentaires.",
     "card-delete-notice": "La suppression est permanente. Vous perdrez toutes les actions associées à cette carte.",
     "card-delete-pop": "Toutes les actions vont être supprimées du suivi d'activités et vous ne pourrez plus utiliser cette carte. Cette action est irréversible.",
@@ -135,6 +136,9 @@
     "cards": "Cartes",
     "cards-count": "Cartes",
     "casSignIn": "Se connecter avec CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Modifier",
     "change-avatar": "Modifier l'avatar",
     "change-password": "Modifier le mot de passe",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Êtes-vous sûr de vouloir supprimer la sous-tâche ?",
     "confirm-checklist-delete-dialog": "Êtes-vous sûr de vouloir supprimer la checklist ?",
     "copy-card-link-to-clipboard": "Copier le lien vers la carte dans le presse-papier",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copier la carte",
     "copyChecklistToManyCardsPopup-title": "Copier le modèle de checklist vers plusieurs cartes",
     "copyChecklistToManyCardsPopup-instructions": "Titres et descriptions des cartes de destination dans ce format JSON",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Créer un tableau",
     "home": "Accueil",
     "import": "Importer",
+    "link": "Link",
     "import-board": "importer un tableau",
     "import-board-c": "Importer un tableau",
     "import-board-title-trello": "Importer le tableau depuis Trello",

+ 7 - 0
i18n/gl.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Like “Bucket List” for example",
     "cancel": "Cancelar",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "This card has %s comment.",
     "card-delete-notice": "Deleting is permanent. You will lose all actions associated with this card.",
     "card-delete-pop": "All actions will be removed from the activity feed and you won't be able to re-open the card. There is no undo.",
@@ -135,6 +136,9 @@
     "cards": "Tarxetas",
     "cards-count": "Tarxetas",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Cambiar",
     "change-avatar": "Cambiar o avatar",
     "change-password": "Cambiar o contrasinal",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Crear taboleiro",
     "home": "Inicio",
     "import": "Importar",
+    "link": "Link",
     "import-board": "importar taboleiro",
     "import-board-c": "Importar taboleiro",
     "import-board-title-trello": "Importar taboleiro de Trello",

+ 7 - 0
i18n/he.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "כמו למשל „רשימת המשימות“",
     "cancel": "ביטול",
     "card-archived": "כרטיס זה הועבר לסל המחזור",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "לכרטיס זה %s תגובות.",
     "card-delete-notice": "מחיקה היא סופית. כל הפעולות המשויכות לכרטיס זה תלכנה לאיוד.",
     "card-delete-pop": "כל הפעולות יוסרו מלוח הפעילות ולא תהיה אפשרות לפתוח מחדש את הכרטיס. אין דרך חזרה.",
@@ -135,6 +136,9 @@
     "cards": "כרטיסים",
     "cards-count": "כרטיסים",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "שינוי",
     "change-avatar": "החלפת תמונת משתמש",
     "change-password": "החלפת ססמה",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "האם למחוק את תת המשימה?",
     "confirm-checklist-delete-dialog": "האם אתה בטוח שברצונך למחוק את רשימת המשימות?",
     "copy-card-link-to-clipboard": "העתקת קישור הכרטיס ללוח הגזירים",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "העתק כרטיס",
     "copyChecklistToManyCardsPopup-title": "העתקת תבנית רשימת מטלות למגוון כרטיסים",
     "copyChecklistToManyCardsPopup-instructions": "כותרות ותיאורים של כרטיסי יעד בתצורת JSON זו",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "יצירת לוח",
     "home": "בית",
     "import": "יבוא",
+    "link": "Link",
     "import-board": "ייבוא לוח",
     "import-board-c": "יבוא לוח",
     "import-board-title-trello": "ייבוא לוח מטרלו",

+ 7 - 0
i18n/hu.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Mint például „Bakancslista”",
     "cancel": "Mégse",
     "card-archived": "Ez a kártya a lomtárba került.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "Ez a kártya %s hozzászólást tartalmaz.",
     "card-delete-notice": "A törlés végleges. Az összes műveletet elveszíti, amely ehhez a kártyához tartozik.",
     "card-delete-pop": "Az összes művelet el lesz távolítva a tevékenységlistából, és nem lesz képes többé újra megnyitni a kártyát. Nincs visszaállítási lehetőség.",
@@ -135,6 +136,9 @@
     "cards": "Kártyák",
     "cards-count": "Kártyák",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Változtatás",
     "change-avatar": "Avatár megváltoztatása",
     "change-password": "Jelszó megváltoztatása",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Kártya hivatkozásának másolása a vágólapra",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Kártya másolása",
     "copyChecklistToManyCardsPopup-title": "Ellenőrzőlista sablon másolása több kártyára",
     "copyChecklistToManyCardsPopup-instructions": "A célkártyák címe és a leírások ebben a JSON formátumban",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Tábla létrehozása",
     "home": "Kezdőlap",
     "import": "Importálás",
+    "link": "Link",
     "import-board": "tábla importálása",
     "import-board-c": "Tábla importálása",
     "import-board-title-trello": "Tábla importálása a Trello oldalról",

+ 7 - 0
i18n/hy.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Like “Bucket List” for example",
     "cancel": "Cancel",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "This card has %s comment.",
     "card-delete-notice": "Deleting is permanent. You will lose all actions associated with this card.",
     "card-delete-pop": "All actions will be removed from the activity feed and you won't be able to re-open the card. There is no undo.",
@@ -135,6 +136,9 @@
     "cards": "Cards",
     "cards-count": "Cards",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Change",
     "change-avatar": "Change Avatar",
     "change-password": "Change Password",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Create Board",
     "home": "Home",
     "import": "Import",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/id.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Contohnya seperti “Bucket List” ",
     "cancel": "Batal",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "Kartu ini punya %s komentar",
     "card-delete-notice": "Menghapus sama dengan permanen. Anda akan kehilangan semua aksi yang terhubung ke kartu ini",
     "card-delete-pop": "Semua aksi akan dihapus dari aktivitas dan anda tidak bisa lagi buka kartu ini",
@@ -135,6 +136,9 @@
     "cards": "Daftar Kartu",
     "cards-count": "Daftar Kartu",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Ubah",
     "change-avatar": "Ubah Avatar",
     "change-password": "Ubah Kata Sandi",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Buat Panel",
     "home": "Beranda",
     "import": "Impor",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "Impor panel dari Trello",

+ 7 - 0
i18n/ig.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Like “Bucket List” for example",
     "cancel": "Cancel",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "This card has %s comment.",
     "card-delete-notice": "Deleting is permanent. You will lose all actions associated with this card.",
     "card-delete-pop": "All actions will be removed from the activity feed and you won't be able to re-open the card. There is no undo.",
@@ -135,6 +136,9 @@
     "cards": "Cards",
     "cards-count": "Cards",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Gbanwe",
     "change-avatar": "Change Avatar",
     "change-password": "Change Password",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Create Board",
     "home": "Home",
     "import": "Import",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/it.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Per esempio come \"una lista di cose da fare\"",
     "cancel": "Cancella",
     "card-archived": "Questa scheda è stata spostata nel cestino.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "Questa scheda ha %s commenti.",
     "card-delete-notice": "L'eliminazione è permanente. Tutte le azioni associate a questa scheda andranno perse.",
     "card-delete-pop": "Tutte le azioni saranno rimosse dal flusso attività e non sarai in grado di riaprire la scheda. Non potrai tornare indietro.",
@@ -135,6 +136,9 @@
     "cards": "Schede",
     "cards-count": "Schede",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Cambia",
     "change-avatar": "Cambia avatar",
     "change-password": "Cambia password",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copia link della scheda sulla clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copia Scheda",
     "copyChecklistToManyCardsPopup-title": "Copia template checklist su più schede",
     "copyChecklistToManyCardsPopup-instructions": "Titolo e la descrizione della scheda di destinazione in questo formato JSON",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Crea bacheca",
     "home": "Home",
     "import": "Importa",
+    "link": "Link",
     "import-board": "Importa bacheca",
     "import-board-c": "Importa bacheca",
     "import-board-title-trello": "Importa una bacheca da Trello",

+ 7 - 0
i18n/ja.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "例:バケットリスト",
     "cancel": "キャンセル",
     "card-archived": "このカードはゴミ箱に移動されます",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "%s 件のコメントがあります。",
     "card-delete-notice": "削除は取り消しできません。このカードに関係するすべてのアクションがなくなります。",
     "card-delete-pop": "すべての内容がアクティビティから削除されます。この削除は元に戻すことができません。",
@@ -135,6 +136,9 @@
     "cards": "カード",
     "cards-count": "カード",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "変更",
     "change-avatar": "アバターの変更",
     "change-password": "パスワードの変更",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "カードへのリンクをクリップボードにコピー",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "カードをコピー",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "ボードの作成",
     "home": "ホーム",
     "import": "インポート",
+    "link": "Link",
     "import-board": "ボードをインポート",
     "import-board-c": "ボードをインポート",
     "import-board-title-trello": "Trelloからボードをインポート",

+ 7 - 0
i18n/ka.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "მაგალითად “Bucket List” ",
     "cancel": "გაუქმება",
     "card-archived": "ბარათი გადატანილია სანაგვე ურნაში ",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "ამ ბარათს ჰქონდა%s კომენტარი.",
     "card-delete-notice": "წაშლის შემთხვევაში ამ ბარათთან ასცირებული ყველა მოქმედება დაიკარგება.",
     "card-delete-pop": "ყველა მოქმედება წაიშლება აქტივობების ველიდან და თქვენ აღარ შეგეძლებათ ბარათის ხელახლა გახსნა. დაბრუნება შეუძლებელია.",
@@ -135,6 +136,9 @@
     "cards": "ბარათები",
     "cards-count": "ბარათები",
     "casSignIn": "შესვლა CAS-ით",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "ცვლილება",
     "change-avatar": "სურათის შეცვლა",
     "change-password": "პაროლის შეცვლა",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "დარწმუნებული ხართ, რომ გსურთ ქვესაქმიანობის წაშლა? ",
     "confirm-checklist-delete-dialog": "დარწმუნებული ხართ, რომ გსურთ კატალოგის წაშლა ? ",
     "copy-card-link-to-clipboard": "დააკოპირეთ ბარათის ბმული clipboard-ზე",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "ბარათის ასლი",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "დაფის შექმნა",
     "home": "სახლი",
     "import": "იმპორტირება",
+    "link": "Link",
     "import-board": " დაფის იმპორტი",
     "import-board-c": "დაფის იმპორტი",
     "import-board-title-trello": "დაფის იმპორტი  Trello-დან",

+ 7 - 0
i18n/km.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Like “Bucket List” for example",
     "cancel": "Cancel",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "This card has %s comment.",
     "card-delete-notice": "Deleting is permanent. You will lose all actions associated with this card.",
     "card-delete-pop": "All actions will be removed from the activity feed and you won't be able to re-open the card. There is no undo.",
@@ -135,6 +136,9 @@
     "cards": "Cards",
     "cards-count": "Cards",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Change",
     "change-avatar": "Change Avatar",
     "change-password": "Change Password",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Create Board",
     "home": "Home",
     "import": "Import",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/ko.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "예: “프로젝트 이름“ 입력",
     "cancel": "취소",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "이 카드에 %s 코멘트가 있습니다.",
     "card-delete-notice": "영구 삭제입니다. 이 카드와 관련된 모든 작업들을 잃게됩니다.",
     "card-delete-pop": "모든 작업이 활동 내역에서 제거되며 카드를 다시 열 수 없습니다. 복구가 안되니 주의하시기 바랍니다.",
@@ -135,6 +136,9 @@
     "cards": "카드",
     "cards-count": "카드",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "변경",
     "change-avatar": "아바타 변경",
     "change-password": "암호 변경",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "클립보드에 카드의 링크가 복사되었습니다.",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "카드 복사",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "보드 생성",
     "home": "홈",
     "import": "가져오기",
+    "link": "Link",
     "import-board": "보드 가져오기",
     "import-board-c": "보드 가져오기",
     "import-board-title-trello": "Trello에서 보드 가져오기",

+ 7 - 0
i18n/lv.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Like “Bucket List” for example",
     "cancel": "Cancel",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "This card has %s comment.",
     "card-delete-notice": "Deleting is permanent. You will lose all actions associated with this card.",
     "card-delete-pop": "All actions will be removed from the activity feed and you won't be able to re-open the card. There is no undo.",
@@ -135,6 +136,9 @@
     "cards": "Cards",
     "cards-count": "Cards",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Change",
     "change-avatar": "Change Avatar",
     "change-password": "Change Password",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Create Board",
     "home": "Home",
     "import": "Import",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/mn.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Like “Bucket List” for example",
     "cancel": "Cancel",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "This card has %s comment.",
     "card-delete-notice": "Deleting is permanent. You will lose all actions associated with this card.",
     "card-delete-pop": "All actions will be removed from the activity feed and you won't be able to re-open the card. There is no undo.",
@@ -135,6 +136,9 @@
     "cards": "Cards",
     "cards-count": "Cards",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Change",
     "change-avatar": "Аватар өөрчлөх",
     "change-password": "Нууц үг солих",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Самбар үүсгэх",
     "home": "Home",
     "import": "Import",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/nb.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Som \"Bucket List\" for eksempel",
     "cancel": "Avbryt",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "Dette kortet har %s kommentar.",
     "card-delete-notice": "Sletting er permanent. Du vil miste alle hendelser knyttet til dette kortet.",
     "card-delete-pop": "Alle handlinger vil fjernes fra feeden for aktiviteter og du vil ikke kunne åpne kortet på nytt. Det er ingen mulighet å angre.",
@@ -135,6 +136,9 @@
     "cards": "Kort",
     "cards-count": "Kort",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Endre",
     "change-avatar": "Endre avatar",
     "change-password": "Endre passord",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Create Board",
     "home": "Home",
     "import": "Import",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/nl.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Zoals \"Bucket List\" bijvoorbeeld",
     "cancel": "Annuleren",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "Deze kaart heeft %s reactie.",
     "card-delete-notice": "Verwijdering is permanent. Als je dit doet, verlies je alle informatie die op deze kaart is opgeslagen.",
     "card-delete-pop": "Alle acties worden verwijderd van de activiteiten feed, en er zal geen mogelijkheid zijn om de kaart opnieuw te openen. Deze actie kan je niet ongedaan maken.",
@@ -135,6 +136,9 @@
     "cards": "Kaarten",
     "cards-count": "Kaarten",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Wijzig",
     "change-avatar": "Wijzig avatar",
     "change-password": "Wijzig wachtwoord",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Kopieer kaart link naar klembord",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Kopieer kaart",
     "copyChecklistToManyCardsPopup-title": "Checklist sjabloon kopiëren naar meerdere kaarten",
     "copyChecklistToManyCardsPopup-instructions": "Doel kaart titels en omschrijvingen in dit JSON formaat",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Bord aanmaken",
     "home": "Voorpagina",
     "import": "Importeer",
+    "link": "Link",
     "import-board": "Importeer bord",
     "import-board-c": "Importeer bord",
     "import-board-title-trello": "Importeer bord van Trello",

+ 7 - 0
i18n/pl.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Like “Bucket List” for example",
     "cancel": "Anuluj",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "Ta karta ma %s komentarzy.",
     "card-delete-notice": "Usunięcie jest trwałe. Stracisz wszystkie akcje powiązane z tą kartą.",
     "card-delete-pop": "Wszystkie akcje będą usunięte z widoku aktywności, nie można będzie ponownie otworzyć karty.  Usunięcie jest nieodwracalne.",
@@ -135,6 +136,9 @@
     "cards": "Karty",
     "cards-count": "Karty",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Zmień",
     "change-avatar": "Zmień Avatar",
     "change-password": "Zmień hasło",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Skopiuj kartę",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Utwórz tablicę",
     "home": "Strona główna",
     "import": "Importu",
+    "link": "Link",
     "import-board": "importuj tablice",
     "import-board-c": "Import tablicy",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/pt-BR.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "\"Bucket List\", por exemplo",
     "cancel": "Cancelar",
     "card-archived": "Este cartão foi movido para a lixeira",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "Este cartão possui %s comentários.",
     "card-delete-notice": "A exclusão será permanente. Você perderá todas as ações associadas a este cartão.",
     "card-delete-pop": "Todas as ações serão removidas da lista de Atividades e vocês não poderá re-abrir o cartão. Não há como desfazer.",
@@ -135,6 +136,9 @@
     "cards": "Cartões",
     "cards-count": "Cartões",
     "casSignIn": "Entrar com CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Alterar",
     "change-avatar": "Alterar Avatar",
     "change-password": "Alterar Senha",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Tem certeza que deseja deletar a subtarefa?",
     "confirm-checklist-delete-dialog": "Tem certeza que quer deletar o checklist?",
     "copy-card-link-to-clipboard": "Copiar link do cartão para a área de transferência",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copiar o cartão",
     "copyChecklistToManyCardsPopup-title": "Copiar modelo de checklist para vários cartões",
     "copyChecklistToManyCardsPopup-instructions": "Títulos e descrições do cartão de destino neste formato JSON",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Criar Quadro",
     "home": "Início",
     "import": "Importar",
+    "link": "Link",
     "import-board": "importar quadro",
     "import-board-c": "Importar quadro",
     "import-board-title-trello": "Importar board do Trello",

+ 7 - 0
i18n/pt.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Like “Bucket List” for example",
     "cancel": "Cancel",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "This card has %s comment.",
     "card-delete-notice": "Deleting is permanent. You will lose all actions associated with this card.",
     "card-delete-pop": "All actions will be removed from the activity feed and you won't be able to re-open the card. There is no undo.",
@@ -135,6 +136,9 @@
     "cards": "Cartões",
     "cards-count": "Cartões",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Alterar",
     "change-avatar": "Change Avatar",
     "change-password": "Change Password",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Create Board",
     "home": "Home",
     "import": "Import",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/ro.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Like “Bucket List” for example",
     "cancel": "Cancel",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "This card has %s comment.",
     "card-delete-notice": "Deleting is permanent. You will lose all actions associated with this card.",
     "card-delete-pop": "All actions will be removed from the activity feed and you won't be able to re-open the card. There is no undo.",
@@ -135,6 +136,9 @@
     "cards": "Cards",
     "cards-count": "Cards",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Change",
     "change-avatar": "Change Avatar",
     "change-password": "Change Password",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Create Board",
     "home": "Home",
     "import": "Import",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/ru.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Например “Список дел”",
     "cancel": "Отмена",
     "card-archived": "Эта карточка перемещена в Корзину",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "Комментарии (%s)",
     "card-delete-notice": "Это действие невозможно будет отменить. Все изменения, которые вы вносили в карточку будут потеряны.",
     "card-delete-pop": "Все действия будут удалены из ленты активности участников и вы не сможете заново открыть карточку. Действие необратимо",
@@ -135,6 +136,9 @@
     "cards": "Карточки",
     "cards-count": "Карточки",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Изменить",
     "change-avatar": "Изменить аватар",
     "change-password": "Изменить пароль",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Вы уверены, что хотите удалить подзадачу?",
     "confirm-checklist-delete-dialog": "Вы уверены, что хотите удалить чеклист?",
     "copy-card-link-to-clipboard": "Копировать ссылку на карточку в буфер обмена",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Копировать карточку",
     "copyChecklistToManyCardsPopup-title": "Копировать шаблон контрольного списка в несколько карточек",
     "copyChecklistToManyCardsPopup-instructions": "Названия и описания целевых карт в формате JSON",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Создать доску",
     "home": "Главная",
     "import": "Импорт",
+    "link": "Link",
     "import-board": "импортировать доску",
     "import-board-c": "Импортировать доску",
     "import-board-title-trello": "Импортировать доску из Trello",

+ 7 - 0
i18n/sr.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Na primer \"Lista zadataka\"",
     "cancel": "Otkaži",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "Ova kartica ima %s komentar.",
     "card-delete-notice": "Brisanje je trajno. Izgubićeš sve akcije povezane sa ovom karticom.",
     "card-delete-pop": "Sve akcije će biti uklonjene sa liste aktivnosti i kartica neće moći biti ponovo otvorena. Nema vraćanja unazad.",
@@ -135,6 +136,9 @@
     "cards": "Cards",
     "cards-count": "Cards",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Change",
     "change-avatar": "Change Avatar",
     "change-password": "Change Password",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Create Board",
     "home": "Home",
     "import": "Import",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "Uvezi tablu iz Trella",

+ 7 - 0
i18n/sv.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Gilla \"att-göra-innan-jag-dör-lista\" till exempel",
     "cancel": "Avbryt",
     "card-archived": "Detta kort flyttas till papperskorgen.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "Detta kort har %s kommentar.",
     "card-delete-notice": "Ta bort är permanent. Du kommer att förlora alla åtgärder i samband med detta kort.",
     "card-delete-pop": "Alla åtgärder kommer att tas bort från aktivitetsflöde och du kommer inte att kunna öppna kortet igen. Det går inte att ångra.",
@@ -135,6 +136,9 @@
     "cards": "Kort",
     "cards-count": "Kort",
     "casSignIn": "Logga in med CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Ändra",
     "change-avatar": "Ändra avatar",
     "change-password": "Ändra lösenord",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Är du säker på att du vill radera deluppgift?",
     "confirm-checklist-delete-dialog": "Är du säker på att du vill radera checklista?",
     "copy-card-link-to-clipboard": "Kopiera kortlänk till urklipp",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Kopiera kort",
     "copyChecklistToManyCardsPopup-title": "Kopiera checklist-mallen till flera kort",
     "copyChecklistToManyCardsPopup-instructions": "Destinationskorttitlar och beskrivningar i detta JSON-format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Skapa anslagstavla",
     "home": "Hem",
     "import": "Importera",
+    "link": "Link",
     "import-board": "importera anslagstavla",
     "import-board-c": "Importera anslagstavla",
     "import-board-title-trello": "Importera anslagstavla från Trello",

+ 7 - 0
i18n/ta.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Like “Bucket List” for example",
     "cancel": "Cancel",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "This card has %s comment.",
     "card-delete-notice": "Deleting is permanent. You will lose all actions associated with this card.",
     "card-delete-pop": "All actions will be removed from the activity feed and you won't be able to re-open the card. There is no undo.",
@@ -135,6 +136,9 @@
     "cards": "Cards",
     "cards-count": "Cards",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Change",
     "change-avatar": "Change Avatar",
     "change-password": "Change Password",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Create Board",
     "home": "Home",
     "import": "Import",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/th.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "ตัวอย่างเช่น “ระบบที่ต้องทำ”",
     "cancel": "ยกเลิก",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "การ์ดนี้มี %s ความเห็น.",
     "card-delete-notice": "เป็นการลบถาวร คุณจะสูญเสียข้อมูลที่เกี่ยวข้องกับการ์ดนี้ทั้งหมด",
     "card-delete-pop": "การดำเนินการทั้งหมดจะถูกลบจาก feed กิจกรรมและคุณไม่สามารถเปิดได้อีกครั้งหรือยกเลิกการทำ",
@@ -135,6 +136,9 @@
     "cards": "การ์ด",
     "cards-count": "การ์ด",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "เปลี่ยน",
     "change-avatar": "เปลี่ยนภาพ",
     "change-password": "เปลี่ยนรหัสผ่าน",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "สร้างบอร์ด",
     "home": "หน้าหลัก",
     "import": "นำเข้า",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "นำเข้าบอร์ดจาก Trello",

+ 7 - 0
i18n/tr.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Örn: \"Marketten Alacaklarım\"",
     "cancel": "İptal",
     "card-archived": "Bu kart Geri Dönüşüm Kutusu'na taşındı",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "Bu kartta %s yorum var.",
     "card-delete-notice": "Silme işlemi kalıcıdır. Bu kartla ilişkili tüm eylemleri kaybedersiniz.",
     "card-delete-pop": "Son hareketler alanındaki tüm veriler silinecek, ayrıca bu kartı yeniden açamayacaksın. Bu işlemin geri dönüşü yok.",
@@ -135,6 +136,9 @@
     "cards": "Kartlar",
     "cards-count": "Kartlar",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Değiştir",
     "change-avatar": "Avatar Değiştir",
     "change-password": "Parola Değiştir",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Kartın linkini kopyala",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Kartı Kopyala",
     "copyChecklistToManyCardsPopup-title": "Yapılacaklar Listesi şemasını birden çok karta kopyala",
     "copyChecklistToManyCardsPopup-instructions": "Hedef Kart Başlıkları ve Açıklamaları bu JSON formatında",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Pano Oluşturma",
     "home": "Ana Sayfa",
     "import": "İçeri aktar",
+    "link": "Link",
     "import-board": "panoyu içe aktar",
     "import-board-c": "Panoyu içe aktar",
     "import-board-title-trello": "Trello'dan panoyu içeri aktar",

+ 7 - 0
i18n/uk.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Like “Bucket List” for example",
     "cancel": "Відміна",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "This card has %s comment.",
     "card-delete-notice": "Deleting is permanent. You will lose all actions associated with this card.",
     "card-delete-pop": "All actions will be removed from the activity feed and you won't be able to re-open the card. There is no undo.",
@@ -135,6 +136,9 @@
     "cards": "Cards",
     "cards-count": "Cards",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Change",
     "change-avatar": "Change Avatar",
     "change-password": "Change Password",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Create Board",
     "home": "Home",
     "import": "Import",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/vi.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "Like “Bucket List” for example",
     "cancel": "Hủy",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "Thẻ này có %s bình luận.",
     "card-delete-notice": "Hành động xóa là không thể khôi phục. Bạn sẽ mất hết các hoạt động liên quan đến thẻ này.",
     "card-delete-pop": "All actions will be removed from the activity feed and you won't be able to re-open the card. There is no undo.",
@@ -135,6 +136,9 @@
     "cards": "Cards",
     "cards-count": "Cards",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "Change",
     "change-avatar": "Change Avatar",
     "change-password": "Change Password",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "Copy card link to clipboard",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "Create Board",
     "home": "Home",
     "import": "Import",
+    "link": "Link",
     "import-board": "import board",
     "import-board-c": "Import board",
     "import-board-title-trello": "Import board from Trello",

+ 7 - 0
i18n/zh-CN.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "例如 “目标清单”",
     "cancel": "取消",
     "card-archived": "此卡片已经被移入回收站。",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "该卡片有 %s 条评论",
     "card-delete-notice": "彻底删除的操作不可恢复,你将会丢失该卡片相关的所有操作记录。",
     "card-delete-pop": "所有的活动将从活动摘要中被移除且您将无法重新打开该卡片。此操作无法撤销。",
@@ -135,6 +136,9 @@
     "cards": "卡片",
     "cards-count": "卡片",
     "casSignIn": "用CAS登录",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "变更",
     "change-avatar": "更改头像",
     "change-password": "更改密码",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "确定要删除子任务吗?",
     "confirm-checklist-delete-dialog": "确定要删除清单吗?",
     "copy-card-link-to-clipboard": "复制卡片链接到剪贴板",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "复制卡片",
     "copyChecklistToManyCardsPopup-title": "复制清单模板至多个卡片",
     "copyChecklistToManyCardsPopup-instructions": "以JSON格式表示目标卡片的标题和描述",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "创建看板",
     "home": "首页",
     "import": "导入",
+    "link": "Link",
     "import-board": "导入看板",
     "import-board-c": "导入看板",
     "import-board-title-trello": "从Trello导入看板",

+ 7 - 0
i18n/zh-TW.i18n.json

@@ -109,6 +109,7 @@
     "bucket-example": "例如 “目標清單”",
     "cancel": "取消",
     "card-archived": "This card is moved to Recycle Bin.",
+    "board-archived": "This board is moved to Recycle Bin.",
     "card-comments-title": "該卡片有 %s 則評論",
     "card-delete-notice": "徹底刪除的操作不可復原,你將會遺失該卡片相關的所有操作記錄。",
     "card-delete-pop": "所有的動作將從活動動態中被移除且您將無法重新打開該卡片。此操作無法復原。",
@@ -135,6 +136,9 @@
     "cards": "卡片",
     "cards-count": "卡片",
     "casSignIn": "Sign In with CAS",
+    "cardType-card": "Card",
+    "cardType-linkedCard": "Linked Card",
+    "cardType-linkedBoard": "Linked Board",
     "change": "變更",
     "change-avatar": "更改大頭貼",
     "change-password": "更改密碼",
@@ -171,6 +175,8 @@
     "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
     "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
     "copy-card-link-to-clipboard": "將卡片連結複製到剪貼板",
+    "linkCardPopup-title": "Link Card",
+    "searchCardPopup-title": "Search Card",
     "copyCardPopup-title": "Copy Card",
     "copyChecklistToManyCardsPopup-title": "Copy Checklist Template to Many Cards",
     "copyChecklistToManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format",
@@ -261,6 +267,7 @@
     "headerBarCreateBoardPopup-title": "建立看板",
     "home": "首頁",
     "import": "匯入",
+    "link": "Link",
     "import-board": "匯入看板",
     "import-board-c": "匯入看板",
     "import-board-title-trello": "匯入在 Trello 的看板",

+ 35 - 13
models/boards.js

@@ -177,6 +177,28 @@ Boards.attachSchema(new SimpleSchema({
     optional: true,
     defaultValue: 'no-parent',
   },
+  startAt: {
+    type: Date,
+    optional: true,
+  },
+  dueAt: {
+    type: Date,
+    optional: true,
+  },
+  endAt: {
+    type: Date,
+    optional: true,
+  },
+  spentTime: {
+    type: Number,
+    decimal: true,
+    optional: true,
+  },
+  isOvertime: {
+    type: Boolean,
+    defaultValue: false,
+    optional: true,
+  },
 }));
 
 
@@ -212,6 +234,10 @@ Boards.helpers({
     return this.permission === 'public';
   },
 
+  cards() {
+    return Cards.find({ boardId: this._id, archived: false }, { sort: { title: 1 } });
+  },
+
   lists() {
     return Lists.find({ boardId: this._id, archived: false }, { sort: { sort: 1 } });
   },
@@ -220,10 +246,6 @@ Boards.helpers({
     return Swimlanes.find({ boardId: this._id, archived: false }, { sort: { sort: 1 } });
   },
 
-  cards() {
-    return Cards.find({ boardId: this._id, archived: false }, { sort: { sort: 1 } });
-  },
-
   hasOvertimeCards(){
     const card = Cards.findOne({isOvertime: true, boardId: this._id, archived: false} );
     return card !== undefined;
@@ -294,22 +316,22 @@ Boards.helpers({
     return _id;
   },
 
-  searchCards(term) {
+  searchCards(term, excludeLinked) {
     check(term, Match.OneOf(String, null, undefined));
 
-    let query = { boardId: this._id };
+    const query = { boardId: this._id };
+    if (excludeLinked) {
+      query.linkedId = null;
+    }
     const projection = { limit: 10, sort: { createdAt: -1 } };
 
     if (term) {
       const regex = new RegExp(term, 'i');
 
-      query = {
-        boardId: this._id,
-        $or: [
-          { title: regex },
-          { description: regex },
-        ],
-      };
+      query.$or = [
+        { title: regex },
+        { description: regex },
+      ];
     }
 
     return Cards.find(query, projection);

+ 366 - 82
models/cards.js

@@ -133,6 +133,13 @@ Cards.attachSchema(new SimpleSchema({
     defaultValue: -1,
     optional: true,
   },
+  type: {
+    type: String,
+  },
+  linkedId: {
+    type: String,
+    optional: true,
+  },
 }));
 
 Cards.allow({
@@ -174,19 +181,33 @@ Cards.helpers({
   },
 
   isAssigned(memberId) {
-    return _.contains(this.members, memberId);
+    return _.contains(this.getMembers(), memberId);
   },
 
   activities() {
-    return Activities.find({cardId: this._id}, {sort: {createdAt: -1}});
+    if (this.isLinkedCard()) {
+      return Activities.find({cardId: this.linkedId}, {sort: {createdAt: -1}});
+    } else if (this.isLinkedBoard()) {
+      return Activities.find({boardId: this.linkedId}, {sort: {createdAt: -1}});
+    } else {
+      return Activities.find({cardId: this._id}, {sort: {createdAt: -1}});
+    }
   },
 
   comments() {
-    return CardComments.find({cardId: this._id}, {sort: {createdAt: -1}});
+    if (this.isLinkedCard()) {
+      return CardComments.find({cardId: this.linkedId}, {sort: {createdAt: -1}});
+    } else {
+      return CardComments.find({cardId: this._id}, {sort: {createdAt: -1}});
+    }
   },
 
   attachments() {
-    return Attachments.find({cardId: this._id}, {sort: {uploadedAt: -1}});
+    if (this.isLinkedCard()) {
+      return Attachments.find({cardId: this.linkedId}, {sort: {uploadedAt: -1}});
+    } else {
+      return Attachments.find({cardId: this._id}, {sort: {uploadedAt: -1}});
+    }
   },
 
   cover() {
@@ -197,7 +218,11 @@ Cards.helpers({
   },
 
   checklists() {
-    return Checklists.find({cardId: this._id}, {sort: { sort: 1 } });
+    if (this.isLinkedCard()) {
+      return Checklists.find({cardId: this.linkedId}, {sort: { sort: 1 } });
+    } else {
+      return Checklists.find({cardId: this._id}, {sort: { sort: 1 } });
+    }
   },
 
   checklistItemCount() {
@@ -386,6 +411,342 @@ Cards.helpers({
   isTopLevel() {
     return this.parentId === '';
   },
+
+  isLinkedCard() {
+    return this.type === 'cardType-linkedCard';
+  },
+
+  isLinkedBoard() {
+    return this.type === 'cardType-linkedBoard';
+  },
+
+  isLinked() {
+    return this.isLinkedCard() || this.isLinkedBoard();
+  },
+
+  setDescription(description) {
+    if (this.isLinkedCard()) {
+      return Cards.update({_id: this.linkedId}, {$set: {description}});
+    } else if (this.isLinkedBoard()) {
+      return Boards.update({_id: this.linkedId}, {$set: {description}});
+    } else {
+      return Cards.update(
+        {_id: this._id},
+        {$set: {description}}
+      );
+    }
+  },
+
+  getDescription() {
+    if (this.isLinkedCard()) {
+      const card = Cards.findOne({_id: this.linkedId});
+      if (card && card.description)
+        return card.description;
+      else
+        return null;
+    } else if (this.isLinkedBoard()) {
+      const board = Boards.findOne({_id: this.linkedId});
+      if (board && board.description)
+        return board.description;
+      else
+        return null;
+    } else if (this.description) {
+      return this.description;
+    } else {
+      return null;
+    }
+  },
+
+  getMembers() {
+    if (this.isLinkedCard()) {
+      const card = Cards.findOne({_id: this.linkedId});
+      return card.members;
+    } else if (this.isLinkedBoard()) {
+      const board = Boards.findOne({_id: this.linkedId});
+      return board.activeMembers().map((member) => {
+        return member.userId;
+      });
+    } else {
+      return this.members;
+    }
+  },
+
+  assignMember(memberId) {
+    if (this.isLinkedCard()) {
+      return Cards.update(
+        { _id: this.linkedId },
+        { $addToSet: { members: memberId }}
+      );
+    } else if (this.isLinkedBoard()) {
+      const board = Boards.findOne({_id: this.linkedId});
+      return board.addMember(memberId);
+    } else {
+      return Cards.update(
+        { _id: this._id },
+        { $addToSet: { members: memberId}}
+      );
+    }
+  },
+
+  unassignMember(memberId) {
+    if (this.isLinkedCard()) {
+      return Cards.update(
+        { _id: this.linkedId },
+        { $pull: { members: memberId }}
+      );
+    } else if (this.isLinkedBoard()) {
+      const board = Boards.findOne({_id: this.linkedId});
+      return board.removeMember(memberId);
+    } else {
+      return Cards.update(
+        { _id: this._id },
+        { $pull: { members: memberId}}
+      );
+    }
+  },
+
+  toggleMember(memberId) {
+    if (this.getMembers() && this.getMembers().indexOf(memberId) > -1) {
+      return this.unassignMember(memberId);
+    } else {
+      return this.assignMember(memberId);
+    }
+  },
+
+  getReceived() {
+    if (this.isLinkedCard()) {
+      const card = Cards.findOne({_id: this.linkedId});
+      return card.receivedAt;
+    } else {
+      return this.receivedAt;
+    }
+  },
+
+  setReceived(receivedAt) {
+    if (this.isLinkedCard()) {
+      return Cards.update(
+        {_id: this.linkedId},
+        {$set: {receivedAt}}
+      );
+    } else {
+      return Cards.update(
+        {_id: this._id},
+        {$set: {receivedAt}}
+      );
+    }
+  },
+
+  getStart() {
+    if (this.isLinkedCard()) {
+      const card = Cards.findOne({_id: this.linkedId});
+      return card.startAt;
+    } else if (this.isLinkedBoard()) {
+      const board = Boards.findOne({_id: this.linkedId});
+      return board.startAt;
+    } else {
+      return this.startAt;
+    }
+  },
+
+  setStart(startAt) {
+    if (this.isLinkedCard()) {
+      return Cards.update(
+        { _id: this.linkedId },
+        {$set: {startAt}}
+      );
+    } else if (this.isLinkedBoard()) {
+      return Boards.update(
+        {_id: this.linkedId},
+        {$set: {startAt}}
+      );
+    } else {
+      return Cards.update(
+        {_id: this._id},
+        {$set: {startAt}}
+      );
+    }
+  },
+
+  getDue() {
+    if (this.isLinkedCard()) {
+      const card = Cards.findOne({_id: this.linkedId});
+      return card.dueAt;
+    } else if (this.isLinkedBoard()) {
+      const board = Boards.findOne({_id: this.linkedId});
+      return board.dueAt;
+    } else {
+      return this.dueAt;
+    }
+  },
+
+  setDue(dueAt) {
+    if (this.isLinkedCard()) {
+      return Cards.update(
+        { _id: this.linkedId },
+        {$set: {dueAt}}
+      );
+    } else if (this.isLinkedBoard()) {
+      return Boards.update(
+        {_id: this.linkedId},
+        {$set: {dueAt}}
+      );
+    } else {
+      return Cards.update(
+        {_id: this._id},
+        {$set: {dueAt}}
+      );
+    }
+  },
+
+  getEnd() {
+    if (this.isLinkedCard()) {
+      const card = Cards.findOne({_id: this.linkedId});
+      return card.endAt;
+    } else if (this.isLinkedBoard()) {
+      const board = Boards.findOne({_id: this.linkedId});
+      return board.endAt;
+    } else {
+      return this.endAt;
+    }
+  },
+
+  setEnd(endAt) {
+    if (this.isLinkedCard()) {
+      return Cards.update(
+        { _id: this.linkedId },
+        {$set: {endAt}}
+      );
+    } else if (this.isLinkedBoard()) {
+      return Boards.update(
+        {_id: this.linkedId},
+        {$set: {endAt}}
+      );
+    } else {
+      return Cards.update(
+        {_id: this._id},
+        {$set: {endAt}}
+      );
+    }
+  },
+
+  getIsOvertime() {
+    if (this.isLinkedCard()) {
+      const card = Cards.findOne({ _id: this.linkedId });
+      return card.isOvertime;
+    } else if (this.isLinkedBoard()) {
+      const board = Boards.findOne({ _id: this.linkedId});
+      return board.isOvertime;
+    } else {
+      return this.isOvertime;
+    }
+  },
+
+  setIsOvertime(isOvertime) {
+    if (this.isLinkedCard()) {
+      return Cards.update(
+        { _id: this.linkedId },
+        {$set: {isOvertime}}
+      );
+    } else if (this.isLinkedBoard()) {
+      return Boards.update(
+        {_id: this.linkedId},
+        {$set: {isOvertime}}
+      );
+    } else {
+      return Cards.update(
+        {_id: this._id},
+        {$set: {isOvertime}}
+      );
+    }
+  },
+
+  getSpentTime() {
+    if (this.isLinkedCard()) {
+      const card = Cards.findOne({ _id: this.linkedId });
+      return card.spentTime;
+    } else if (this.isLinkedBoard()) {
+      const board = Boards.findOne({ _id: this.linkedId});
+      return board.spentTime;
+    } else {
+      return this.spentTime;
+    }
+  },
+
+  setSpentTime(spentTime) {
+    if (this.isLinkedCard()) {
+      return Cards.update(
+        { _id: this.linkedId },
+        {$set: {spentTime}}
+      );
+    } else if (this.isLinkedBoard()) {
+      return Boards.update(
+        {_id: this.linkedId},
+        {$set: {spentTime}}
+      );
+    } else {
+      return Cards.update(
+        {_id: this._id},
+        {$set: {spentTime}}
+      );
+    }
+  },
+
+  getTitle() {
+    if (this.isLinkedCard()) {
+      const card = Cards.findOne({ _id: this.linkedId });
+      return card.title;
+    } else if (this.isLinkedBoard()) {
+      const board = Boards.findOne({ _id: this.linkedId});
+      return board.title;
+    } else {
+      return this.title;
+    }
+  },
+
+  getBoardTitle() {
+    if (this.isLinkedCard()) {
+      const card = Cards.findOne({ _id: this.linkedId });
+      const board = Boards.findOne({ _id: card.boardId });
+      return board.title;
+    } else if (this.isLinkedBoard()) {
+      const board = Boards.findOne({ _id: this.linkedId});
+      return board.title;
+    } else {
+      const board = Boards.findOne({ _id: this.boardId });
+      return board.title;
+    }
+  },
+
+  setTitle(title) {
+    if (this.isLinkedCard()) {
+      return Cards.update(
+        { _id: this.linkedId },
+        {$set: {title}}
+      );
+    } else if (this.isLinkedBoard()) {
+      return Boards.update(
+        {_id: this.linkedId},
+        {$set: {title}}
+      );
+    } else {
+      return Cards.update(
+        {_id: this._id},
+        {$set: {title}}
+      );
+    }
+  },
+
+  getArchived() {
+    if (this.isLinkedCard()) {
+      const card = Cards.findOne({ _id: this.linkedId });
+      return card.archived;
+    } else if (this.isLinkedBoard()) {
+      const board = Boards.findOne({ _id: this.linkedId});
+      return board.archived;
+    } else {
+      return this.archived;
+    }
+  },
 });
 
 Cards.mutations({
@@ -405,22 +766,6 @@ Cards.mutations({
     return {$set: {archived: false}};
   },
 
-  setTitle(title) {
-    return {$set: {title}};
-  },
-
-  setDescription(description) {
-    return {$set: {description}};
-  },
-
-  setRequestedBy(requestedBy) {
-    return {$set: {requestedBy}};
-  },
-
-  setAssignedBy(assignedBy) {
-    return {$set: {assignedBy}};
-  },
-
   move(swimlaneId, listId, sortIndex) {
     const list = Lists.findOne(listId);
     const mutatedFields = {
@@ -449,22 +794,6 @@ Cards.mutations({
     }
   },
 
-  assignMember(memberId) {
-    return {$addToSet: {members: memberId}};
-  },
-
-  unassignMember(memberId) {
-    return {$pull: {members: memberId}};
-  },
-
-  toggleMember(memberId) {
-    if (this.members && this.members.indexOf(memberId) > -1) {
-      return this.unassignMember(memberId);
-    } else {
-      return this.assignMember(memberId);
-    }
-  },
-
   assignCustomField(customFieldId) {
     return {$addToSet: {customFields: {_id: customFieldId, value: null}}};
   },
@@ -502,54 +831,9 @@ Cards.mutations({
     return {$unset: {coverId: ''}};
   },
 
-  setReceived(receivedAt) {
-    return {$set: {receivedAt}};
-  },
-
-  unsetReceived() {
-    return {$unset: {receivedAt: ''}};
-  },
-
-  setStart(startAt) {
-    return {$set: {startAt}};
-  },
-
-  unsetStart() {
-    return {$unset: {startAt: ''}};
-  },
-
-  setDue(dueAt) {
-    return {$set: {dueAt}};
-  },
-
-  unsetDue() {
-    return {$unset: {dueAt: ''}};
-  },
-
-  setEnd(endAt) {
-    return {$set: {endAt}};
-  },
-
-  unsetEnd() {
-    return {$unset: {endAt: ''}};
-  },
-
-  setOvertime(isOvertime) {
-    return {$set: {isOvertime}};
-  },
-
-  setSpentTime(spentTime) {
-    return {$set: {spentTime}};
-  },
-
-  unsetSpentTime() {
-    return {$unset: {spentTime: '', isOvertime: false}};
-  },
-
   setParentId(parentId) {
     return {$set: {parentId}};
   },
-
 });
 
 

+ 2 - 1
models/export.js

@@ -45,6 +45,7 @@ class Exporter {
 
   build() {
     const byBoard = { boardId: this._boardId };
+    const byBoardNoLinked = { boardId: this._boardId, linkedId: null };
     // we do not want to retrieve boardId in related elements
     const noBoardId = { fields: { boardId: 0 } };
     const result = {
@@ -52,7 +53,7 @@ class Exporter {
     };
     _.extend(result, Boards.findOne(this._boardId, { fields: { stars: 0 } }));
     result.lists = Lists.find(byBoard, noBoardId).fetch();
-    result.cards = Cards.find(byBoard, noBoardId).fetch();
+    result.cards = Cards.find(byBoardNoLinked, noBoardId).fetch();
     result.swimlanes = Swimlanes.find(byBoard, noBoardId).fetch();
     result.comments = CardComments.find(byBoard, noBoardId).fetch();
     result.activities = Activities.find(byBoard, noBoardId).fetch();

+ 12 - 0
server/migrations.js

@@ -213,6 +213,18 @@ Migrations.add('add-profile-view', () => {
   });
 });
 
+Migrations.add('add-card-types', () => {
+  Cards.find().forEach((card) => {
+    Cards.direct.update(
+      { _id: card._id },
+      { $set: {
+        type: 'cardType-card',
+        linkedId: null } },
+      noValidate
+    );
+  });
+});
+
 Migrations.add('add-custom-fields-to-cards', () => {
   Cards.update({
     customFields: {

+ 13 - 1
server/publications/boards.js

@@ -98,7 +98,19 @@ Meteor.publishRelations('board', function(boardId) {
     //
     // And in the meantime our code below works pretty well -- it's not even a
     // hack!
-    this.cursor(Cards.find({ boardId }), function(cardId) {
+    this.cursor(Cards.find({ boardId }), function(cardId, card) {
+      if (card.type === 'cardType-linkedCard') {
+        const impCardId = card.linkedId;
+        this.cursor(Cards.find({ _id: impCardId }));
+        this.cursor(CardComments.find({ cardId: impCardId }));
+        this.cursor(Activities.find({ cardId: impCardId }));
+        this.cursor(Attachments.find({ cardId: impCardId }));
+        this.cursor(Checklists.find({ cardId: impCardId }));
+        this.cursor(ChecklistItems.find({ cardId: impCardId }));
+      } else if (card.type === 'cardType-linkedBoard') {
+        this.cursor(Boards.find({ _id: card.linkedId}));
+      }
+      this.cursor(Activities.find({ cardId }));
       this.cursor(CardComments.find({ cardId }));
       this.cursor(Attachments.find({ cardId }));
       this.cursor(Checklists.find({ cardId }));