Browse Source

Fixed Error in migrate-lists-to-per-swimlane migration.

Thanks to xet7 !

Fixes #5918
Lauri Ojansivu 1 week ago
parent
commit
cc99da5357

+ 6 - 3
client/components/boards/boardBody.jade

@@ -21,6 +21,10 @@ template(name="boardBody")
   if notDisplayThisBoard
    | {{_ 'tableVisibilityMode-allowPrivateOnly'}}
   else
+    // Debug information (remove in production)
+    if debugBoardState
+      .debug-info(style="position: fixed; top: 0; left: 0; background: rgba(0,0,0,0.8); color: white; padding: 10px; z-index: 9999; font-size: 12px;")
+        | Board: {{currentBoard.title}} | View: {{boardView}} | HasSwimlanes: {{hasSwimlanes}} | Swimlanes: {{currentBoard.swimlanes.length}}
     .board-wrapper(class=currentBoard.colorClass class="{{#if isMiniScreen}}mobile-view{{/if}}")
       .board-canvas.js-swimlanes(
         class="{{#if hasSwimlanes}}dragscroll{{/if}}"
@@ -39,9 +43,8 @@ template(name="boardBody")
             each currentBoard.swimlanes
               +swimlane(this)
           else
-            a.js-empty-board-add-swimlane(title="{{_ 'add-swimlane'}}")
-              h1.big-message.quiet
-                | {{_ 'add-swimlane'}} +
+            // Fallback: If no swimlanes exist, show lists instead of empty message
+            +listsGroup(currentBoard)
         else if isViewLists
           +listsGroup(currentBoard)
         else if isViewCalendar

+ 26 - 5
client/components/boards/boardBody.js

@@ -238,11 +238,16 @@ BlazeComponent.extendComponent({
       }
     }
 
-    // Observe for new popups/menus and set focus
+    // Observe for new popups/menus and set focus (but exclude swimlane content)
     const popupObserver = new MutationObserver(function(mutations) {
       mutations.forEach(function(mutation) {
         mutation.addedNodes.forEach(function(node) {
-          if (node.nodeType === 1 && (node.classList.contains('popup') || node.classList.contains('modal') || node.classList.contains('menu'))) {
+          if (node.nodeType === 1 && 
+              (node.classList.contains('popup') || node.classList.contains('modal') || node.classList.contains('menu')) &&
+              !node.closest('.js-swimlanes') && 
+              !node.closest('.swimlane') &&
+              !node.closest('.list') &&
+              !node.closest('.minicard')) {
             setTimeout(function() { focusFirstInteractive(node); }, 10);
           }
         });
@@ -601,10 +606,20 @@ BlazeComponent.extendComponent({
 
   hasSwimlanes() {
     const currentBoard = Utils.getCurrentBoard();
-    if (!currentBoard) return false;
+    if (!currentBoard) {
+      console.log('hasSwimlanes: No current board');
+      return false;
+    }
     
-    const swimlanes = currentBoard.swimlanes();
-    return swimlanes.length > 0;
+    try {
+      const swimlanes = currentBoard.swimlanes();
+      const hasSwimlanes = swimlanes && swimlanes.length > 0;
+      console.log('hasSwimlanes: Board has', swimlanes ? swimlanes.length : 0, 'swimlanes');
+      return hasSwimlanes;
+    } catch (error) {
+      console.error('hasSwimlanes: Error getting swimlanes:', error);
+      return false;
+    }
   },
 
 
@@ -618,6 +633,12 @@ BlazeComponent.extendComponent({
   },
 
   debugBoardState() {
+    // Enable debug mode by setting ?debug=1 in URL
+    const urlParams = new URLSearchParams(window.location.search);
+    return urlParams.get('debug') === '1';
+  },
+
+  debugBoardStateData() {
     const currentBoard = Utils.getCurrentBoard();
     const currentBoardId = Session.get('currentBoard');
     const isBoardReady = this.isBoardReady.get();

+ 34 - 31
client/components/settings/attachmentSettings.js

@@ -1,37 +1,6 @@
 import { ReactiveCache } from '/imports/reactiveCache';
 import { TAPi18n } from '/imports/i18n';
 
-// Template helpers for attachmentSettings
-Template.attachmentSettings.helpers({
-  loading() {
-    const instance = Template.instance();
-    if (instance && instance.loading) {
-      return instance.loading.get();
-    }
-    return attachmentSettings.loading.get();
-  },
-  showStorageSettings() {
-    const instance = Template.instance();
-    if (instance && instance.showStorageSettings) {
-      return instance.showStorageSettings.get();
-    }
-    return attachmentSettings.showStorageSettings.get();
-  },
-  showMigration() {
-    const instance = Template.instance();
-    if (instance && instance.showMigration) {
-      return instance.showMigration.get();
-    }
-    return attachmentSettings.showMigration.get();
-  },
-  showMonitoring() {
-    const instance = Template.instance();
-    if (instance && instance.showMonitoring) {
-      return instance.showMonitoring.get();
-    }
-    return attachmentSettings.showMonitoring.get();
-  }
-});
 import { Meteor } from 'meteor/meteor';
 import { Session } from 'meteor/session';
 import { Tracker } from 'meteor/tracker';
@@ -102,6 +71,20 @@ BlazeComponent.extendComponent({
     this.loadMonitoringData();
   },
 
+  // Template helpers for this component
+  loading() {
+    return this.loading.get();
+  },
+  showStorageSettings() {
+    return this.showStorageSettings.get();
+  },
+  showMigration() {
+    return this.showMigration.get();
+  },
+  showMonitoring() {
+    return this.showMonitoring.get();
+  },
+
   events() {
     return [
       {
@@ -497,5 +480,25 @@ BlazeComponent.extendComponent({
   }
 }).register('attachmentMonitoring');
 
+// Template helpers for attachmentSettings
+Template.attachmentSettings.helpers({
+  loading() {
+    const instance = Template.instance();
+    return instance.loading && instance.loading.get();
+  },
+  showStorageSettings() {
+    const instance = Template.instance();
+    return instance.showStorageSettings && instance.showStorageSettings.get();
+  },
+  showMigration() {
+    const instance = Template.instance();
+    return instance.showMigration && instance.showMigration.get();
+  },
+  showMonitoring() {
+    const instance = Template.instance();
+    return instance.showMonitoring && instance.showMonitoring.get();
+  },
+});
+
 // Export the attachment settings for use in other components
 export { attachmentSettings };

+ 24 - 0
client/components/settings/cronSettings.js

@@ -526,3 +526,27 @@ function pollMigrationProgress(instance) {
     });
   }, 1000);
 }
+
+// Template helpers for cronSettings
+Template.cronSettings.helpers({
+  loading() {
+    const instance = Template.instance();
+    return instance.loading && instance.loading.get();
+  },
+  showMigrations() {
+    const instance = Template.instance();
+    return instance.showMigrations && instance.showMigrations.get();
+  },
+  showBoardOperations() {
+    const instance = Template.instance();
+    return instance.showBoardOperations && instance.showBoardOperations.get();
+  },
+  showJobs() {
+    const instance = Template.instance();
+    return instance.showJobs && instance.showJobs.get();
+  },
+  showAddJob() {
+    const instance = Template.instance();
+    return instance.showAddJob && instance.showAddJob.get();
+  },
+});

+ 49 - 80
client/components/settings/settingBody.js

@@ -3,86 +3,6 @@ import { TAPi18n } from '/imports/i18n';
 import { ALLOWED_WAIT_SPINNERS } from '/config/const';
 import LockoutSettings from '/models/lockoutSettings';
 
-// Template helpers for settingBody
-Template.setting.helpers({
-  generalSetting() {
-    const instance = Template.instance();
-    if (instance && instance.generalSetting) {
-      return instance.generalSetting.get();
-    }
-    return false;
-  },
-  emailSetting() {
-    const instance = Template.instance();
-    if (instance && instance.emailSetting) {
-      return instance.emailSetting.get();
-    }
-    return false;
-  },
-  accountSetting() {
-    const instance = Template.instance();
-    if (instance && instance.accountSetting) {
-      return instance.accountSetting.get();
-    }
-    return false;
-  },
-  tableVisibilityModeSetting() {
-    const instance = Template.instance();
-    if (instance && instance.tableVisibilityModeSetting) {
-      return instance.tableVisibilityModeSetting.get();
-    }
-    return false;
-  },
-  announcementSetting() {
-    const instance = Template.instance();
-    if (instance && instance.announcementSetting) {
-      return instance.announcementSetting.get();
-    }
-    return false;
-  },
-  accessibilitySetting() {
-    const instance = Template.instance();
-    if (instance && instance.accessibilitySetting) {
-      return instance.accessibilitySetting.get();
-    }
-    return false;
-  },
-  layoutSetting() {
-    const instance = Template.instance();
-    if (instance && instance.layoutSetting) {
-      return instance.layoutSetting.get();
-    }
-    return false;
-  },
-  webhookSetting() {
-    const instance = Template.instance();
-    if (instance && instance.webhookSetting) {
-      return instance.webhookSetting.get();
-    }
-    return false;
-  },
-  attachmentSettings() {
-    const instance = Template.instance();
-    if (instance && instance.attachmentSettings) {
-      return instance.attachmentSettings.get();
-    }
-    return false;
-  },
-  cronSettings() {
-    const instance = Template.instance();
-    if (instance && instance.cronSettings) {
-      return instance.cronSettings.get();
-    }
-    return false;
-  },
-  loading() {
-    const instance = Template.instance();
-    if (instance && instance.loading) {
-      return instance.loading.get();
-    }
-    return false;
-  }
-});
 
 BlazeComponent.extendComponent({
   onCreated() {
@@ -110,6 +30,7 @@ BlazeComponent.extendComponent({
     Meteor.subscribe('lockoutSettings');
   },
 
+
   setError(error) {
     this.error.set(error);
   },
@@ -667,3 +588,51 @@ Template.selectSpinnerName.helpers({
     return Template.instance().data.spinnerName === match;
   },
 });
+
+// Template helpers for the setting template
+Template.setting.helpers({
+  generalSetting() {
+    const instance = Template.instance();
+    return instance.generalSetting && instance.generalSetting.get();
+  },
+  emailSetting() {
+    const instance = Template.instance();
+    return instance.emailSetting && instance.emailSetting.get();
+  },
+  accountSetting() {
+    const instance = Template.instance();
+    return instance.accountSetting && instance.accountSetting.get();
+  },
+  tableVisibilityModeSetting() {
+    const instance = Template.instance();
+    return instance.tableVisibilityModeSetting && instance.tableVisibilityModeSetting.get();
+  },
+  announcementSetting() {
+    const instance = Template.instance();
+    return instance.announcementSetting && instance.announcementSetting.get();
+  },
+  accessibilitySetting() {
+    const instance = Template.instance();
+    return instance.accessibilitySetting && instance.accessibilitySetting.get();
+  },
+  layoutSetting() {
+    const instance = Template.instance();
+    return instance.layoutSetting && instance.layoutSetting.get();
+  },
+  webhookSetting() {
+    const instance = Template.instance();
+    return instance.webhookSetting && instance.webhookSetting.get();
+  },
+  attachmentSettings() {
+    const instance = Template.instance();
+    return instance.attachmentSettings && instance.attachmentSettings.get();
+  },
+  cronSettings() {
+    const instance = Template.instance();
+    return instance.cronSettings && instance.cronSettings.get();
+  },
+  loading() {
+    const instance = Template.instance();
+    return instance.loading && instance.loading.get();
+  },
+});

+ 1 - 1
models/attachments.js

@@ -40,7 +40,7 @@ if (Meteor.isServer) {
     }
   }
 
-  storagePath = path.join(process.env.WRITABLE_PATH, 'attachments');
+  storagePath = path.join(process.env.WRITABLE_PATH || process.cwd(), 'attachments');
 }
 
 export const fileStoreStrategyFactory = new FileStoreStrategyFactory(AttachmentStoreStrategyFilesystem, storagePath, AttachmentStoreStrategyGridFs, attachmentBucket);

+ 1 - 1
models/avatars.js

@@ -40,7 +40,7 @@ if (Meteor.isServer) {
   }
 
   avatarsBucket = createBucket('avatars');
-  storagePath = path.join(process.env.WRITABLE_PATH, 'avatars');
+  storagePath = path.join(process.env.WRITABLE_PATH || process.cwd(), 'avatars');
 }
 
 const fileStoreStrategyFactory = new FileStoreStrategyFactory(FileStoreStrategyFilesystem, storagePath, FileStoreStrategyGridFs, avatarsBucket);

+ 12 - 10
models/boards.js

@@ -1148,13 +1148,13 @@ Boards.helpers({
         permission: this.permission,
         members: this.members,
         color: this.color,
-        description: TAPi18n.__('default-subtasks-board', {
+        description: TAPi18n && TAPi18n.i18n ? TAPi18n.__('default-subtasks-board', {
           board: this.title,
-        }),
+        }) : `Default subtasks board for ${this.title}`,
       });
 
       Swimlanes.insert({
-        title: TAPi18n.__('default'),
+        title: TAPi18n && TAPi18n.i18n ? TAPi18n.__('default') : 'Default',
         boardId: this.subtasksDefaultBoardId,
       });
       Boards.update(this._id, {
@@ -1181,13 +1181,13 @@ Boards.helpers({
         permission: this.permission,
         members: this.members,
         color: this.color,
-        description: TAPi18n.__('default-dates-board', {
+        description: TAPi18n && TAPi18n.i18n ? TAPi18n.__('default-dates-board', {
           board: this.title,
-        }),
+        }) : `Default dates board for ${this.title}`,
       });
 
       Swimlanes.insert({
-        title: TAPi18n.__('default'),
+        title: TAPi18n && TAPi18n.i18n ? TAPi18n.__('default') : 'Default',
         boardId: this.dateSettingsDefaultBoardId,
       });
       Boards.update(this._id, {
@@ -1209,7 +1209,7 @@ Boards.helpers({
       this.subtasksDefaultListId === undefined
     ) {
       this.subtasksDefaultListId = Lists.insert({
-        title: TAPi18n.__('queue'),
+        title: TAPi18n && TAPi18n.i18n ? TAPi18n.__('queue') : 'Queue',
         boardId: this._id,
         swimlaneId: this.getDefaultSwimline()._id, // Set default swimlane for subtasks list
       });
@@ -1228,7 +1228,7 @@ Boards.helpers({
       this.dateSettingsDefaultListId === undefined
     ) {
       this.dateSettingsDefaultListId = Lists.insert({
-        title: TAPi18n.__('queue'),
+        title: TAPi18n && TAPi18n.i18n ? TAPi18n.__('queue') : 'Queue',
         boardId: this._id,
         swimlaneId: this.getDefaultSwimline()._id, // Set default swimlane for date settings list
       });
@@ -1244,8 +1244,10 @@ Boards.helpers({
   getDefaultSwimline() {
     let result = ReactiveCache.getSwimlane({ boardId: this._id });
     if (result === undefined) {
+      // Use fallback title if i18n is not available (e.g., during migration)
+      const title = TAPi18n && TAPi18n.i18n ? TAPi18n.__('default') : 'Default';
       Swimlanes.insert({
-        title: TAPi18n.__('default'),
+        title: title,
         boardId: this._id,
       });
       result = ReactiveCache.getSwimlane({ boardId: this._id });
@@ -2212,7 +2214,7 @@ if (Meteor.isServer) {
         migrationVersion: 1, // Latest version - no migration needed
       });
       const swimlaneId = Swimlanes.insert({
-        title: TAPi18n.__('default'),
+        title: TAPi18n && TAPi18n.i18n ? TAPi18n.__('default') : 'Default',
         boardId: id,
       });
       JsonRoutes.sendResult(res, {

+ 4 - 4
models/users.js

@@ -2138,7 +2138,7 @@ if (Meteor.isServer) {
         const future3 = new Future();
         Boards.insert(
           {
-            title: TAPi18n.__('templates'),
+            title: TAPi18n && TAPi18n.i18n ? TAPi18n.__('templates') : 'Templates',
             permission: 'private',
             type: 'template-container',
           },
@@ -2154,7 +2154,7 @@ if (Meteor.isServer) {
             // Insert the card templates swimlane
             Swimlanes.insert(
               {
-                title: TAPi18n.__('card-templates-swimlane'),
+                title: TAPi18n && TAPi18n.i18n ? TAPi18n.__('card-templates-swimlane') : 'Card Templates',
                 boardId,
                 sort: 1,
                 type: 'template-container',
@@ -2174,7 +2174,7 @@ if (Meteor.isServer) {
             // Insert the list templates swimlane
             Swimlanes.insert(
               {
-                title: TAPi18n.__('list-templates-swimlane'),
+                title: TAPi18n && TAPi18n.i18n ? TAPi18n.__('list-templates-swimlane') : 'List Templates',
                 boardId,
                 sort: 2,
                 type: 'template-container',
@@ -2194,7 +2194,7 @@ if (Meteor.isServer) {
             // Insert the board templates swimlane
             Swimlanes.insert(
               {
-                title: TAPi18n.__('board-templates-swimlane'),
+                title: TAPi18n && TAPi18n.i18n ? TAPi18n.__('board-templates-swimlane') : 'Board Templates',
                 boardId,
                 sort: 3,
                 type: 'template-container',