Ver Fonte

[Combine hamburger menus at right](https://github.com/wekan/wekan/issues/2219).

Thanks to xet7 !

Related #2219
Lauri Ojansivu há 6 anos atrás
pai
commit
847ed8570b

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

@@ -111,43 +111,8 @@ template(name="boardHeaderBar")
               i.fa.fa-times-thin
 
       .separator
-      a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}")
-        i.board-header-btn-icon.fa.fa-cog
-
-template(name="boardMenuPopup")
-  ul.pop-over-list
-    li: a.js-custom-fields {{_ 'custom-fields'}}
-    li: a.js-open-archives {{_ 'archived-items'}}
-    if currentUser.isBoardAdmin
-      li: a.js-change-board-color {{_ 'board-change-color'}}
-    //-
-      XXX Language should be handled by sandstorm, but for now display a
-      language selection link in the board menu. This link is normally present
-      in the header bar that is not displayed on sandstorm.
-    if isSandstorm
-      li: a.js-change-language {{_ 'language'}}
-  unless isSandstorm
-    if currentUser.isBoardAdmin
-      hr
-      ul.pop-over-list
-        li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}}
-        unless currentBoard.isTemplatesBoard
-          li: a.js-archive-board {{_ 'archive-board'}}
-        li: a.js-outgoing-webhooks {{_ 'outgoing-webhooks'}}
-      hr
-      ul.pop-over-list
-        li: a.js-subtask-settings {{_ 'subtask-settings'}}
-
-  if isSandstorm
-    hr
-    ul.pop-over-list
-      li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}}
-      li: a.js-import-board {{_ 'import-board-c'}}
-      li: a.js-archive-board {{_ 'archive-board'}}
-      li: a.js-outgoing-webhooks {{_ 'outgoing-webhooks'}}
-    hr
-    ul.pop-over-list
-      li: a.js-subtask-settings {{_ 'subtask-settings'}}
+      a.board-header-btn.js-toggle-sidebar
+        i.fa.fa-navicon
 
 template(name="boardVisibilityList")
   ul.pop-over-list
@@ -168,94 +133,6 @@ template(name="boardVisibilityList")
             i.fa.fa-check
           span.sub-name {{_ 'public-desc'}}
 
-template(name="boardChangeVisibilityPopup")
-  +boardVisibilityList
-
-template(name="boardChangeWatchPopup")
-  ul.pop-over-list
-    li
-      with "watching"
-        a.js-select-watch
-          i.fa.fa-eye.colorful
-          | {{_ 'watching'}}
-          if watchCheck
-            i.fa.fa-check
-          span.sub-name {{_ 'watching-info'}}
-    li
-      with "tracking"
-        a.js-select-watch
-          i.fa.fa-bell.colorful
-          | {{_ 'tracking'}}
-          if watchCheck
-            i.fa.fa-check
-          span.sub-name {{_ 'tracking-info'}}
-    li
-      with "muted"
-        a.js-select-watch
-          i.fa.fa-bell-slash.colorful
-          | {{_ 'muted'}}
-          if watchCheck
-            i.fa.fa-check
-          span.sub-name {{_ 'muted-info'}}
-
-template(name="boardChangeColorPopup")
-  .board-backgrounds-list.clearfix
-    each backgroundColors
-      .board-background-select.js-select-background
-        span.background-box(class="board-color-{{this}}")
-          if isSelected
-            i.fa.fa-check
-
-template(name="boardSubtaskSettingsPopup")
-  form.board-subtask-settings
-    h3 {{_ 'show-parent-in-minicard'}}
-      a#prefix-with-full-path.flex.js-field-show-parent-in-minicard(class="{{#if $eq presentParentTask 'prefix-with-full-path'}}is-checked{{/if}}")
-        .materialCheckBox(class="{{#if $eq presentParentTask 'prefix-with-full-path'}}is-checked{{/if}}")
-        span {{_ 'prefix-with-full-path'}}
-      a#prefix-with-parent.flex.js-field-show-parent-in-minicard(class="{{#if $eq presentParentTask 'prefix-with-parent'}}is-checked{{/if}}")
-        .materialCheckBox(class="{{#if $eq presentParentTask 'prefix-with-parent'}}is-checked{{/if}}")
-        span {{_ 'prefix-with-parent'}}
-      a#subtext-with-full-path.flex.js-field-show-parent-in-minicard(class="{{#if $eq presentParentTask 'subtext-with-full-path'}}is-checked{{/if}}")
-        .materialCheckBox(class="{{#if $eq presentParentTask 'subtext-with-full-path'}}is-checked{{/if}}")
-        span {{_ 'subtext-with-full-path'}}
-      a#subtext-with-parent.flex.js-field-show-parent-in-minicard(class="{{#if $eq presentParentTask 'subtext-with-parent'}}is-checked{{/if}}")
-        .materialCheckBox(class="{{#if $eq presentParentTask 'subtext-with-parent'}}is-checked{{/if}}")
-        span {{_ 'subtext-with-parent'}}
-      a#no-parent.flex.js-field-show-parent-in-minicard(class="{{#if $eq presentParentTask 'no-parent'}}is-checked{{/if}}")
-        .materialCheckBox(class="{{#if $eq presentParentTask 'no-parent'}}is-checked{{/if}}")
-        span {{_ 'no-parent'}}
-    div
-      hr
-
-    div.check-div
-      a.flex.js-field-has-subtasks(class="{{#if allowsSubtasks}}is-checked{{/if}}")
-        .materialCheckBox(class="{{#if allowsSubtasks}}is-checked{{/if}}")
-        span {{_ 'show-subtasks-field'}}
-
-    label
-      | {{_ 'deposit-subtasks-board'}}
-      select.js-field-deposit-board(disabled="{{#unless allowsSubtasks}}disabled{{/unless}}")
-          each boards
-            if isBoardSelected
-              option(value=_id selected="selected") {{title}}
-            else
-              option(value=_id) {{title}}
-          if isNullBoardSelected
-            option(value='null' selected="selected") {{_ 'custom-field-dropdown-none'}}
-          else
-            option(value='null') {{_ 'custom-field-dropdown-none'}}
-    div
-      hr
-
-    label
-      | {{_ 'deposit-subtasks-list'}}
-      select.js-field-deposit-list(disabled="{{#unless hasLists}}disabled{{/unless}}")
-          each lists
-            if isListSelected
-              option(value=_id selected="selected") {{title}}
-            else
-              option(value=_id) {{title}}
-
 template(name="createBoard")
   form
     label
@@ -282,13 +159,6 @@ template(name="createBoard")
       | /
       a.js-board-template {{_ 'template'}}
 
-template(name="chooseBoardSource")
-  ul.pop-over-list
-    li
-      a(href="{{pathFor '/import/trello'}}") {{_ 'from-trello'}}
-    li
-      a(href="{{pathFor '/import/wekan'}}") {{_ 'from-wekan'}}
-
 template(name="boardChangeTitlePopup")
   form
     label
@@ -302,28 +172,3 @@ template(name="boardChangeTitlePopup")
 template(name="boardCreateRulePopup")
   p {{_ 'close-board-pop'}}
   button.js-confirm.negate.full(type="submit") {{_ 'archive'}}
-
-
-template(name="archiveBoardPopup")
-  p {{_ 'close-board-pop'}}
-  button.js-confirm.negate.full(type="submit") {{_ 'archive'}}
-
-template(name="outgoingWebhooksPopup")
-  each integrations
-    form.integration-form
-      if title
-        h4 {{title}}
-      else
-        h4 {{_ 'no-name'}}
-      label
-        | URL
-        input.js-outgoing-webhooks-url(type="text" name="url" value=url)
-        input(type="hidden" value=_id name="id")
-      input.primary.wide(type="submit" value="{{_ 'save'}}")
-  form.integration-form
-    h4
-      | {{_ 'new-outgoing-webhook'}}
-    label
-      | URL
-      input.js-outgoing-webhooks-url(type="text" name="url" autofocus)
-    input.primary.wide(type="submit" value="{{_ 'save'}}")

+ 3 - 224
client/components/boards/boardHeader.js

@@ -1,49 +1,3 @@
-Template.boardMenuPopup.events({
-  'click .js-rename-board': Popup.open('boardChangeTitle'),
-  'click .js-custom-fields'() {
-    Sidebar.setView('customFields');
-    Popup.close();
-  },
-  'click .js-open-archives'() {
-    Sidebar.setView('archives');
-    Popup.close();
-  },
-  'click .js-change-board-color': Popup.open('boardChangeColor'),
-  'click .js-change-language': Popup.open('changeLanguage'),
-  'click .js-archive-board ': Popup.afterConfirm('archiveBoard', function() {
-    const currentBoard = Boards.findOne(Session.get('currentBoard'));
-    currentBoard.archive();
-    // XXX We should have some kind of notification on top of the page to
-    // confirm that the board was successfully archived.
-    FlowRouter.go('home');
-  }),
-  'click .js-delete-board': Popup.afterConfirm('deleteBoard', function() {
-    const currentBoard = Boards.findOne(Session.get('currentBoard'));
-    Popup.close();
-    Boards.remove(currentBoard._id);
-    FlowRouter.go('home');
-  }),
-  'click .js-outgoing-webhooks': Popup.open('outgoingWebhooks'),
-  'click .js-import-board': Popup.open('chooseBoardSource'),
-  'click .js-subtask-settings': Popup.open('boardSubtaskSettings'),
-});
-
-Template.boardMenuPopup.helpers({
-  exportUrl() {
-    const params = {
-      boardId: Session.get('currentBoard'),
-    };
-    const queryParams = {
-      authToken: Accounts._storedLoginToken(),
-    };
-    return FlowRouter.path('/api/boards/:boardId/export', params, queryParams);
-  },
-  exportFilename() {
-    const boardId = Session.get('currentBoard');
-    return `wekan-export-board-${boardId}.json`;
-  },
-});
-
 Template.boardChangeTitlePopup.events({
   submit(evt, tpl) {
     const newTitle = tpl.$('.js-board-name').val().trim();
@@ -81,12 +35,8 @@ BlazeComponent.extendComponent({
       'click .js-star-board'() {
         Meteor.user().toggleBoardStar(Session.get('currentBoard'));
       },
-      'click .js-open-board-menu': Popup.open('boardMenu'),
       'click .js-change-visibility': Popup.open('boardChangeVisibility'),
       'click .js-watch-board': Popup.open('boardChangeWatch'),
-      'click .js-open-archived-board'() {
-        Modal.open('archivedBoards');
-      },
       'click .js-toggle-board-view'() {
         const currentUser = Meteor.user();
         if (currentUser.profile.boardView === 'board-view-swimlanes') {
@@ -97,6 +47,9 @@ BlazeComponent.extendComponent({
           currentUser.setBoardView('board-view-lists');
         }
       },
+      'click .js-toggle-sidebar'() {
+        Sidebar.toggle();
+      },
       'click .js-open-filter-view'() {
         Sidebar.setView('filter');
       },
@@ -135,124 +88,6 @@ Template.boardHeaderBar.helpers({
   },
 });
 
-BlazeComponent.extendComponent({
-  backgroundColors() {
-    return Boards.simpleSchema()._schema.color.allowedValues;
-  },
-
-  isSelected() {
-    const currentBoard = Boards.findOne(Session.get('currentBoard'));
-    return currentBoard.color === this.currentData().toString();
-  },
-
-  events() {
-    return [{
-      'click .js-select-background'(evt) {
-        const currentBoard = Boards.findOne(Session.get('currentBoard'));
-        const newColor = this.currentData().toString();
-        currentBoard.setColor(newColor);
-        evt.preventDefault();
-      },
-    }];
-  },
-}).register('boardChangeColorPopup');
-
-BlazeComponent.extendComponent({
-  onCreated() {
-    this.currentBoard = Boards.findOne(Session.get('currentBoard'));
-  },
-
-  allowsSubtasks() {
-    return this.currentBoard.allowsSubtasks;
-  },
-
-  isBoardSelected() {
-    return this.currentBoard.subtasksDefaultBoardId === this.currentData()._id;
-  },
-
-  isNullBoardSelected() {
-    return (this.currentBoard.subtasksDefaultBoardId === null) || (this.currentBoard.subtasksDefaultBoardId === undefined);
-  },
-
-  boards() {
-    return Boards.find({
-      archived: false,
-      'members.userId': Meteor.userId(),
-    }, {
-      sort: ['title'],
-    });
-  },
-
-  lists() {
-    return Lists.find({
-      boardId: this.currentBoard._id,
-      archived: false,
-    }, {
-      sort: ['title'],
-    });
-  },
-
-  hasLists() {
-    return this.lists().count() > 0;
-  },
-
-  isListSelected() {
-    return this.currentBoard.subtasksDefaultBoardId === this.currentData()._id;
-  },
-
-  presentParentTask() {
-    let result = this.currentBoard.presentParentTask;
-    if ((result === null) || (result === undefined)) {
-      result = 'no-parent';
-    }
-    return result;
-  },
-
-  events() {
-    return [{
-      'click .js-field-has-subtasks'(evt) {
-        evt.preventDefault();
-        this.currentBoard.allowsSubtasks = !this.currentBoard.allowsSubtasks;
-        this.currentBoard.setAllowsSubtasks(this.currentBoard.allowsSubtasks);
-        $('.js-field-has-subtasks .materialCheckBox').toggleClass('is-checked', this.currentBoard.allowsSubtasks);
-        $('.js-field-has-subtasks').toggleClass('is-checked', this.currentBoard.allowsSubtasks);
-        $('.js-field-deposit-board').prop('disabled', !this.currentBoard.allowsSubtasks);
-      },
-      'change .js-field-deposit-board'(evt) {
-        let value = evt.target.value;
-        if (value === 'null') {
-          value = null;
-        }
-        this.currentBoard.setSubtasksDefaultBoardId(value);
-        evt.preventDefault();
-      },
-      'change .js-field-deposit-list'(evt) {
-        this.currentBoard.setSubtasksDefaultListId(evt.target.value);
-        evt.preventDefault();
-      },
-      'click .js-field-show-parent-in-minicard'(evt) {
-        const value = evt.target.id || $(evt.target).parent()[0].id ||  $(evt.target).parent()[0].parent()[0].id;
-        const options = [
-          'prefix-with-full-path',
-          'prefix-with-parent',
-          'subtext-with-full-path',
-          'subtext-with-parent',
-          'no-parent'];
-        options.forEach(function(element) {
-          if (element !== value) {
-            $(`#${element} .materialCheckBox`).toggleClass('is-checked', false);
-            $(`#${element}`).toggleClass('is-checked', false);
-          }
-        });
-        $(`#${value} .materialCheckBox`).toggleClass('is-checked', true);
-        $(`#${value}`).toggleClass('is-checked', true);
-        this.currentBoard.setPresentParentTask(value);
-        evt.preventDefault();
-      },
-    }];
-  },
-}).register('boardSubtaskSettingsPopup');
-
 const CreateBoard = BlazeComponent.extendComponent({
   template() {
     return 'createBoard';
@@ -301,20 +136,11 @@ const CreateBoard = BlazeComponent.extendComponent({
         this.setVisibility(this.currentData());
       },
       'click .js-change-visibility': this.toggleVisibilityMenu,
-      'click .js-import': Popup.open('boardImportBoard'),
-      submit: this.onSubmit,
-      'click .js-import-board': Popup.open('chooseBoardSource'),
       'click .js-board-template': Popup.open('searchElement'),
     }];
   },
 }).register('createBoardPopup');
 
-BlazeComponent.extendComponent({
-  template() {
-    return 'chooseBoardSource';
-  },
-}).register('chooseBoardSourcePopup');
-
 (class HeaderBarCreateBoard extends CreateBoard {
   onSubmit(evt) {
     super.onSubmit(evt);
@@ -364,50 +190,3 @@ BlazeComponent.extendComponent({
     }];
   },
 }).register('boardChangeWatchPopup');
-
-BlazeComponent.extendComponent({
-  integrations() {
-    const boardId = Session.get('currentBoard');
-    return Integrations.find({ boardId: `${boardId}` }).fetch();
-  },
-
-  integration(id) {
-    const boardId = Session.get('currentBoard');
-    return Integrations.findOne({ _id: id, boardId: `${boardId}` });
-  },
-
-  events() {
-    return [{
-      'submit'(evt) {
-        evt.preventDefault();
-        const url = evt.target.url.value;
-        const boardId = Session.get('currentBoard');
-        let id = null;
-        let integration = null;
-        if (evt.target.id) {
-          id = evt.target.id.value;
-          integration = this.integration(id);
-          if (url) {
-            Integrations.update(integration._id, {
-              $set: {
-                url: `${url}`,
-              },
-            });
-          } else {
-            Integrations.remove(integration._id);
-          }
-        } else if (url) {
-          Integrations.insert({
-            userId: Meteor.userId(),
-            enabled: true,
-            type: 'outgoing-webhooks',
-            url: `${url}`,
-            boardId: `${boardId}`,
-            activities: ['all'],
-          });
-        }
-        Popup.close();
-      },
-    }];
-  },
-}).register('outgoingWebhooksPopup');

+ 161 - 4
client/components/sidebar/sidebar.jade

@@ -1,9 +1,9 @@
 template(name="sidebar")
   .board-sidebar.sidebar(class="{{#if isOpen}}is-open{{/if}}")
-    a.sidebar-tongue.js-toggle-sidebar(
-      class="{{#if isTongueHidden}}is-hidden{{/if}}",
-      title="{{showTongueTitle}}")
-      i.fa.fa-navicon
+    //a.sidebar-tongue.js-toggle-sidebar(
+    //  class="{{#if isTongueHidden}}is-hidden{{/if}}",
+    //  title="{{showTongueTitle}}")
+    //  i.fa.fa-navicon
     .sidebar-shadow
       .sidebar-content.sidebar-shortcuts
         a.board-header-btn.js-shortcuts
@@ -34,6 +34,9 @@ template(name="membersWidget")
     h3
       i.fa.fa-user
       | {{_ 'members'}}
+      a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}").right
+        i.board-header-btn-icon.fa.fa-cog
+
     .board-widget-content
       each currentBoard.activeMembers
         +userAvatar(userId=this.userId showStatus=true)
@@ -53,6 +56,160 @@ template(name="membersWidget")
     button.js-member-invite-accept.primary {{_ 'accept'}}
     button.js-member-invite-decline {{_ 'decline'}}
 
+template(name="boardChangeVisibilityPopup")
+  +boardVisibilityList
+
+template(name="boardChangeWatchPopup")
+  ul.pop-over-list
+    li
+      with "watching"
+        a.js-select-watch
+          i.fa.fa-eye.colorful
+          | {{_ 'watching'}}
+          if watchCheck
+            i.fa.fa-check
+          span.sub-name {{_ 'watching-info'}}
+    li
+      with "tracking"
+        a.js-select-watch
+          i.fa.fa-bell.colorful
+          | {{_ 'tracking'}}
+          if watchCheck
+            i.fa.fa-check
+          span.sub-name {{_ 'tracking-info'}}
+    li
+      with "muted"
+        a.js-select-watch
+          i.fa.fa-bell-slash.colorful
+          | {{_ 'muted'}}
+          if watchCheck
+            i.fa.fa-check
+          span.sub-name {{_ 'muted-info'}}
+
+template(name="boardChangeColorPopup")
+  .board-backgrounds-list.clearfix
+    each backgroundColors
+      .board-background-select.js-select-background
+        span.background-box(class="board-color-{{this}}")
+          if isSelected
+            i.fa.fa-check
+
+template(name="boardSubtaskSettingsPopup")
+  form.board-subtask-settings
+    h3 {{_ 'show-parent-in-minicard'}}
+      a#prefix-with-full-path.flex.js-field-show-parent-in-minicard(class="{{#if $eq presentParentTask 'prefix-with-full-path'}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if $eq presentParentTask 'prefix-with-full-path'}}is-checked{{/if}}")
+        span {{_ 'prefix-with-full-path'}}
+      a#prefix-with-parent.flex.js-field-show-parent-in-minicard(class="{{#if $eq presentParentTask 'prefix-with-parent'}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if $eq presentParentTask 'prefix-with-parent'}}is-checked{{/if}}")
+        span {{_ 'prefix-with-parent'}}
+      a#subtext-with-full-path.flex.js-field-show-parent-in-minicard(class="{{#if $eq presentParentTask 'subtext-with-full-path'}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if $eq presentParentTask 'subtext-with-full-path'}}is-checked{{/if}}")
+        span {{_ 'subtext-with-full-path'}}
+      a#subtext-with-parent.flex.js-field-show-parent-in-minicard(class="{{#if $eq presentParentTask 'subtext-with-parent'}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if $eq presentParentTask 'subtext-with-parent'}}is-checked{{/if}}")
+        span {{_ 'subtext-with-parent'}}
+      a#no-parent.flex.js-field-show-parent-in-minicard(class="{{#if $eq presentParentTask 'no-parent'}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if $eq presentParentTask 'no-parent'}}is-checked{{/if}}")
+        span {{_ 'no-parent'}}
+    div
+      hr
+
+    div.check-div
+      a.flex.js-field-has-subtasks(class="{{#if allowsSubtasks}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if allowsSubtasks}}is-checked{{/if}}")
+        span {{_ 'show-subtasks-field'}}
+
+    label
+      | {{_ 'deposit-subtasks-board'}}
+      select.js-field-deposit-board(disabled="{{#unless allowsSubtasks}}disabled{{/unless}}")
+          each boards
+            if isBoardSelected
+              option(value=_id selected="selected") {{title}}
+            else
+              option(value=_id) {{title}}
+          if isNullBoardSelected
+            option(value='null' selected="selected") {{_ 'custom-field-dropdown-none'}}
+          else
+            option(value='null') {{_ 'custom-field-dropdown-none'}}
+    div
+      hr
+
+    label
+      | {{_ 'deposit-subtasks-list'}}
+      select.js-field-deposit-list(disabled="{{#unless hasLists}}disabled{{/unless}}")
+          each lists
+            if isListSelected
+              option(value=_id selected="selected") {{title}}
+            else
+              option(value=_id) {{title}}
+
+template(name="chooseBoardSource")
+  ul.pop-over-list
+    li
+      a(href="{{pathFor '/import/trello'}}") {{_ 'from-trello'}}
+    li
+      a(href="{{pathFor '/import/wekan'}}") {{_ 'from-wekan'}}
+
+template(name="archiveBoardPopup")
+  p {{_ 'close-board-pop'}}
+  button.js-confirm.negate.full(type="submit") {{_ 'archive'}}
+
+template(name="outgoingWebhooksPopup")
+  each integrations
+    form.integration-form
+      if title
+        h4 {{title}}
+      else
+        h4 {{_ 'no-name'}}
+      label
+        | URL
+        input.js-outgoing-webhooks-url(type="text" name="url" value=url)
+        input(type="hidden" value=_id name="id")
+      input.primary.wide(type="submit" value="{{_ 'save'}}")
+  form.integration-form
+    h4
+      | {{_ 'new-outgoing-webhook'}}
+    label
+      | URL
+      input.js-outgoing-webhooks-url(type="text" name="url" autofocus)
+    input.primary.wide(type="submit" value="{{_ 'save'}}")
+
+template(name="boardMenuPopup")
+  ul.pop-over-list
+    li: a.js-custom-fields {{_ 'custom-fields'}}
+    li: a.js-open-archives {{_ 'archived-items'}}
+    if currentUser.isBoardAdmin
+      li: a.js-change-board-color {{_ 'board-change-color'}}
+    //-
+      XXX Language should be handled by sandstorm, but for now display a
+      language selection link in the board menu. This link is normally present
+      in the header bar that is not displayed on sandstorm.
+    if isSandstorm
+      li: a.js-change-language {{_ 'language'}}
+  unless isSandstorm
+    if currentUser.isBoardAdmin
+      hr
+      ul.pop-over-list
+        li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}}
+        unless currentBoard.isTemplatesBoard
+          li: a.js-archive-board {{_ 'archive-board'}}
+        li: a.js-outgoing-webhooks {{_ 'outgoing-webhooks'}}
+      hr
+      ul.pop-over-list
+        li: a.js-subtask-settings {{_ 'subtask-settings'}}
+
+  if isSandstorm
+    hr
+    ul.pop-over-list
+      li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}}
+      li: a.js-import-board {{_ 'import-board-c'}}
+      li: a.js-archive-board {{_ 'archive-board'}}
+      li: a.js-outgoing-webhooks {{_ 'outgoing-webhooks'}}
+    hr
+    ul.pop-over-list
+      li: a.js-subtask-settings {{_ 'subtask-settings'}}
+
 template(name="labelsWidget")
   .board-widget.board-widget-labels
     h3

+ 224 - 0
client/components/sidebar/sidebar.js

@@ -142,6 +142,52 @@ Template.memberPopup.helpers({
   },
 });
 
+Template.boardMenuPopup.events({
+  'click .js-rename-board': Popup.open('boardChangeTitle'),
+  'click .js-custom-fields'() {
+    Sidebar.setView('customFields');
+    Popup.close();
+  },
+  'click .js-open-archives'() {
+    Sidebar.setView('archives');
+    Popup.close();
+  },
+  'click .js-change-board-color': Popup.open('boardChangeColor'),
+  'click .js-change-language': Popup.open('changeLanguage'),
+  'click .js-archive-board ': Popup.afterConfirm('archiveBoard', function() {
+    const currentBoard = Boards.findOne(Session.get('currentBoard'));
+    currentBoard.archive();
+    // XXX We should have some kind of notification on top of the page to
+    // confirm that the board was successfully archived.
+    FlowRouter.go('home');
+  }),
+  'click .js-delete-board': Popup.afterConfirm('deleteBoard', function() {
+    const currentBoard = Boards.findOne(Session.get('currentBoard'));
+    Popup.close();
+    Boards.remove(currentBoard._id);
+    FlowRouter.go('home');
+  }),
+  'click .js-outgoing-webhooks': Popup.open('outgoingWebhooks'),
+  'click .js-import-board': Popup.open('chooseBoardSource'),
+  'click .js-subtask-settings': Popup.open('boardSubtaskSettings'),
+});
+
+Template.boardMenuPopup.helpers({
+  exportUrl() {
+    const params = {
+      boardId: Session.get('currentBoard'),
+    };
+    const queryParams = {
+      authToken: Accounts._storedLoginToken(),
+    };
+    return FlowRouter.path('/api/boards/:boardId/export', params, queryParams);
+  },
+  exportFilename() {
+    const boardId = Session.get('currentBoard');
+    return `wekan-export-board-${boardId}.json`;
+  },
+});
+
 Template.memberPopup.events({
   'click .js-filter-member'() {
     Filter.members.toggle(this.userId);
@@ -190,7 +236,14 @@ Template.membersWidget.helpers({
 
 Template.membersWidget.events({
   'click .js-member': Popup.open('member'),
+  'click .js-open-board-menu': Popup.open('boardMenu'),
   'click .js-manage-board-members': Popup.open('addMember'),
+  'click .js-import': Popup.open('boardImportBoard'),
+  submit: this.onSubmit,
+  'click .js-import-board': Popup.open('chooseBoardSource'),
+  'click .js-open-archived-board'() {
+    Modal.open('archivedBoards');
+  },
   'click .sandstorm-powerbox-request-identity'() {
     window.sandstormRequestIdentity();
   },
@@ -209,6 +262,59 @@ Template.membersWidget.events({
   },
 });
 
+BlazeComponent.extendComponent({
+  integrations() {
+    const boardId = Session.get('currentBoard');
+    return Integrations.find({ boardId: `${boardId}` }).fetch();
+  },
+
+  integration(id) {
+    const boardId = Session.get('currentBoard');
+    return Integrations.findOne({ _id: id, boardId: `${boardId}` });
+  },
+
+  events() {
+    return [{
+      'submit'(evt) {
+        evt.preventDefault();
+        const url = evt.target.url.value;
+        const boardId = Session.get('currentBoard');
+        let id = null;
+        let integration = null;
+        if (evt.target.id) {
+          id = evt.target.id.value;
+          integration = this.integration(id);
+          if (url) {
+            Integrations.update(integration._id, {
+              $set: {
+                url: `${url}`,
+              },
+            });
+          } else {
+            Integrations.remove(integration._id);
+          }
+        } else if (url) {
+          Integrations.insert({
+            userId: Meteor.userId(),
+            enabled: true,
+            type: 'outgoing-webhooks',
+            url: `${url}`,
+            boardId: `${boardId}`,
+            activities: ['all'],
+          });
+        }
+        Popup.close();
+      },
+    }];
+  },
+}).register('outgoingWebhooksPopup');
+
+BlazeComponent.extendComponent({
+  template() {
+    return 'chooseBoardSource';
+  },
+}).register('chooseBoardSourcePopup');
+
 Template.labelsWidget.events({
   'click .js-label': Popup.open('editLabel'),
   'click .js-add-label': Popup.open('createLabel'),
@@ -258,6 +364,124 @@ function draggableMembersLabelsWidgets() {
 Template.membersWidget.onRendered(draggableMembersLabelsWidgets);
 Template.labelsWidget.onRendered(draggableMembersLabelsWidgets);
 
+BlazeComponent.extendComponent({
+  backgroundColors() {
+    return Boards.simpleSchema()._schema.color.allowedValues;
+  },
+
+  isSelected() {
+    const currentBoard = Boards.findOne(Session.get('currentBoard'));
+    return currentBoard.color === this.currentData().toString();
+  },
+
+  events() {
+    return [{
+      'click .js-select-background'(evt) {
+        const currentBoard = Boards.findOne(Session.get('currentBoard'));
+        const newColor = this.currentData().toString();
+        currentBoard.setColor(newColor);
+        evt.preventDefault();
+      },
+    }];
+  },
+}).register('boardChangeColorPopup');
+
+BlazeComponent.extendComponent({
+  onCreated() {
+    this.currentBoard = Boards.findOne(Session.get('currentBoard'));
+  },
+
+  allowsSubtasks() {
+    return this.currentBoard.allowsSubtasks;
+  },
+
+  isBoardSelected() {
+    return this.currentBoard.subtasksDefaultBoardId === this.currentData()._id;
+  },
+
+  isNullBoardSelected() {
+    return (this.currentBoard.subtasksDefaultBoardId === null) || (this.currentBoard.subtasksDefaultBoardId === undefined);
+  },
+
+  boards() {
+    return Boards.find({
+      archived: false,
+      'members.userId': Meteor.userId(),
+    }, {
+      sort: ['title'],
+    });
+  },
+
+  lists() {
+    return Lists.find({
+      boardId: this.currentBoard._id,
+      archived: false,
+    }, {
+      sort: ['title'],
+    });
+  },
+
+  hasLists() {
+    return this.lists().count() > 0;
+  },
+
+  isListSelected() {
+    return this.currentBoard.subtasksDefaultBoardId === this.currentData()._id;
+  },
+
+  presentParentTask() {
+    let result = this.currentBoard.presentParentTask;
+    if ((result === null) || (result === undefined)) {
+      result = 'no-parent';
+    }
+    return result;
+  },
+
+  events() {
+    return [{
+      'click .js-field-has-subtasks'(evt) {
+        evt.preventDefault();
+        this.currentBoard.allowsSubtasks = !this.currentBoard.allowsSubtasks;
+        this.currentBoard.setAllowsSubtasks(this.currentBoard.allowsSubtasks);
+        $('.js-field-has-subtasks .materialCheckBox').toggleClass('is-checked', this.currentBoard.allowsSubtasks);
+        $('.js-field-has-subtasks').toggleClass('is-checked', this.currentBoard.allowsSubtasks);
+        $('.js-field-deposit-board').prop('disabled', !this.currentBoard.allowsSubtasks);
+      },
+      'change .js-field-deposit-board'(evt) {
+        let value = evt.target.value;
+        if (value === 'null') {
+          value = null;
+        }
+        this.currentBoard.setSubtasksDefaultBoardId(value);
+        evt.preventDefault();
+      },
+      'change .js-field-deposit-list'(evt) {
+        this.currentBoard.setSubtasksDefaultListId(evt.target.value);
+        evt.preventDefault();
+      },
+      'click .js-field-show-parent-in-minicard'(evt) {
+        const value = evt.target.id || $(evt.target).parent()[0].id ||  $(evt.target).parent()[0].parent()[0].id;
+        const options = [
+          'prefix-with-full-path',
+          'prefix-with-parent',
+          'subtext-with-full-path',
+          'subtext-with-parent',
+          'no-parent'];
+        options.forEach(function(element) {
+          if (element !== value) {
+            $(`#${element} .materialCheckBox`).toggleClass('is-checked', false);
+            $(`#${element}`).toggleClass('is-checked', false);
+          }
+        });
+        $(`#${value} .materialCheckBox`).toggleClass('is-checked', true);
+        $(`#${value}`).toggleClass('is-checked', true);
+        this.currentBoard.setPresentParentTask(value);
+        evt.preventDefault();
+      },
+    }];
+  },
+}).register('boardSubtaskSettingsPopup');
+
 BlazeComponent.extendComponent({
   onCreated() {
     this.error = new ReactiveVar('');