From cea414b58959232e0fe390483218f9728567589b Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Fri, 17 Oct 2025 21:55:49 +0300 Subject: [PATCH 001/183] Updated translations. --- imports/i18n/data/he.i18n.json | 2 +- imports/i18n/data/sl.i18n.json | 1516 ++++++++++++++++---------------- 2 files changed, 759 insertions(+), 759 deletions(-) diff --git a/imports/i18n/data/he.i18n.json b/imports/i18n/data/he.i18n.json index 50533a957..920968822 100644 --- a/imports/i18n/data/he.i18n.json +++ b/imports/i18n/data/he.i18n.json @@ -1471,7 +1471,7 @@ "s3-size": "גודל ב־S3", "s3-storage": "S3", "scanning-status": "מצב סריקה", - "schedule": "Schedule", + "schedule": "לוח זמנים", "search-boards-or-operations": "חיפוש לוחות או פעולות…", "show-list-on-minicard": "הצגת רשימה בכרטיסון", "showing": "מוצג", diff --git a/imports/i18n/data/sl.i18n.json b/imports/i18n/data/sl.i18n.json index 7bbd76862..48daee11b 100644 --- a/imports/i18n/data/sl.i18n.json +++ b/imports/i18n/data/sl.i18n.json @@ -1,89 +1,89 @@ { - "accept": "Sprejmi", + "accept": "Accept", "act-activity-notify": "Activity Notification", - "act-addAttachment": "dodal priponko __attachment__ h kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-deleteAttachment": "odstranil priponko __attachment__ iz kartice __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-addSubtask": "dodal podopravilo __subtask__ h kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-addLabel": "Dodal oznako __label__ h kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-addedLabel": "Dodal oznako __label__ h kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-removeLabel": "Odstranil oznako __label__ iz kartice __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-removedLabel": "Odstranil oznako __label__ iz kartice __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-addChecklist": "dodal kontrolni seznam __checklist__ h kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-addChecklistItem": "dodal postavko __checklistItem__ kontrolnega seznama __checklist__ na kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-removeChecklist": "odstranil kontrolni seznam __checklist__ iz kartice __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-removeChecklistItem": "odstranil postavko __checklistItem__ kontrolnega seznama __checklist__ na kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-checkedItem": "obkljukal postavko __checklistItem__ kontrolnega seznama __checklist__ na kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-uncheckedItem": "odkljukal postavko __checklistItem__ kontrolnega seznama __checklist__ na kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-addAttachment": "added attachment __attachment__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-deleteAttachment": "deleted attachment __attachment__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-addSubtask": "added subtask __subtask__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-addLabel": "Added label __label__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-addedLabel": "Added label __label__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-removeLabel": "Removed label __label__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-removedLabel": "Removed label __label__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-addChecklist": "added checklist __checklist__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-addChecklistItem": "added checklist item __checklistItem__ to checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-removeChecklist": "removed checklist __checklist__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-removeChecklistItem": "removed checklist item __checklistItem__ from checklist __checkList__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-checkedItem": "checked __checklistItem__ of checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-uncheckedItem": "unchecked __checklistItem__ of checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__", "act-completeChecklist": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-uncompleteChecklist": "nedokončan kontrolni seznam __checklist__ na kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-addComment": "komentiral na kartici __card__: __comment__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-editComment": "uredil komentar na kartici __card__: __comment__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-deleteComment": "izbrisal komentar na kartici __card__: __comment__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-createBoard": "ustvaril tablo __board__", - "act-createSwimlane": "ustvaril plavalno stezo __swimlane__ na tabli __board__", - "act-createCard": "ustvaril kartico __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-createCustomField": "ustvaril poljubno polje __customField__ na tabli __board__", - "act-deleteCustomField": "izbrisal poljubno polje __customField__ na tabli __board__", - "act-setCustomField": "uredil poljubno polje __customField__: __customFieldValue__ na kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-createList": "dodal seznam __list__ na tablo __board__", - "act-addBoardMember": "dodal člana __member__ k tabli __board__", - "act-archivedBoard": "Tabla __board__ premaknjena v arhiv", - "act-archivedCard": "Kartica __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__ premaknjena v arhiv", - "act-archivedList": "Seznam __list__ na plavalni stezi __swimlane__ na tabli __board__ premaknjen v arhiv", - "act-archivedSwimlane": "Plavalna steza __swimlane__ na tabli __board__ premaknjena v arhiv", - "act-importBoard": "uvozil tablo __board__", - "act-importCard": "uvozil kartico __card__ na seznam __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-importList": "uvozil seznam __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-joinMember": "dodal član __member__ h kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-moveCard": "premakil kartico __card__ na tabli __board__ iz seznama __oldList__ na plavalni stezi __oldSwimlane__ na seznam __list__ na plavalni stezi __swimlane__", - "act-moveCardToOtherBoard": "premaknil kartico __card__ iz seznama __oldList__ na plavalni stezi __oldSwimlane__ na tabli __oldBoard__ na seznam __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-removeBoardMember": "odstranil člana __member__ iz table __board__", - "act-restoredCard": "obnovil kartico __card__ na seznam __list__ na plavalni stezi __swimlane__ na tabli __board__", - "act-unjoinMember": "odstranil člana __member__ iz kartice __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-uncompleteChecklist": "uncompleted checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-addComment": "commented on card __card__: __comment__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-editComment": "edited comment on card __card__: __comment__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-deleteComment": "deleted comment on card __card__: __comment__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-createBoard": "created board __board__", + "act-createSwimlane": "created swimlane __swimlane__ to board __board__", + "act-createCard": "created card __card__ to list __list__ at swimlane __swimlane__ at board __board__", + "act-createCustomField": "created custom field __customField__ at board __board__", + "act-deleteCustomField": "deleted custom field __customField__ at board __board__", + "act-setCustomField": "edited custom field __customField__: __customFieldValue__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-createList": "added list __list__ to board __board__", + "act-addBoardMember": "added member __member__ to board __board__", + "act-archivedBoard": "Board __board__ moved to Archive", + "act-archivedCard": "Card __card__ at list __list__ at swimlane __swimlane__ at board __board__ moved to Archive", + "act-archivedList": "List __list__ at swimlane __swimlane__ at board __board__ moved to Archive", + "act-archivedSwimlane": "Swimlane __swimlane__ at board __board__ moved to Archive", + "act-importBoard": "imported board __board__", + "act-importCard": "imported card __card__ to list __list__ at swimlane __swimlane__ at board __board__", + "act-importList": "imported list __list__ to swimlane __swimlane__ at board __board__", + "act-joinMember": "added member __member__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-moveCard": "moved card __card__ at board __board__ from list __oldList__ at swimlane __oldSwimlane__ to list __list__ at swimlane __swimlane__", + "act-moveCardToOtherBoard": "moved card __card__ from list __oldList__ at swimlane __oldSwimlane__ at board __oldBoard__ to list __list__ at swimlane __swimlane__ at board __board__", + "act-removeBoardMember": "removed member __member__ from board __board__", + "act-restoredCard": "restored card __card__ to list __list__ at swimlane __swimlane__ at board __board__", + "act-unjoinMember": "removed member __member__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__", "act-withBoardTitle": "__board__", "act-withCardTitle": "[__board__] __card__", - "actions": "Dejanja", - "activities": "Aktivnosti", - "activity": "Aktivnost", - "activity-added": "dodal %s v %s", - "activity-archived": "%s premaknjeno v arhiv", - "activity-attached": "pripel %s v %s", - "activity-created": "ustvaril %s", + "actions": "Actions", + "activities": "Activities", + "activity": "Activity", + "activity-added": "added %s to %s", + "activity-archived": "%s moved to Archive", + "activity-attached": "attached %s to %s", + "activity-created": "created %s", "activity-changedListTitle": "renamed list to %s", - "activity-customfield-created": "ustvaril poljubno polje%s", - "activity-excluded": "izključil %s iz %s", - "activity-imported": "uvozil %s v %s iz %s", - "activity-imported-board": "uvozil %s iz %s", - "activity-joined": "se je pridružil na %s", - "activity-moved": "premakil %s iz %s na %s", - "activity-on": "na %s", - "activity-removed": "odstranil %s iz %s", - "activity-sent": "poslano %s na %s", - "activity-unjoined": "se je odjavil iz %s", - "activity-subtask-added": "dodal podopravilo k %s", - "activity-checked-item": "obkljukal %s na kontrolnem seznamu %s od %s", - "activity-unchecked-item": "odkljukal %s na kontrolnem seznamu %s od %s", - "activity-checklist-added": "dodal kontrolni seznam na %s", - "activity-checklist-removed": "odstranil kontrolni seznam iz %s", - "activity-checklist-completed": "dokončan kontrolni seznam %s od %s", - "activity-checklist-uncompleted": "nedokončal kontrolni seznam %s od %s", - "activity-checklist-item-added": "dodal postavko kontrolnega seznama na '%s' v %s", - "activity-checklist-item-removed": "odstranil postavko kontrolnega seznama iz '%s' v %s", - "add": "Dodaj", - "activity-checked-item-card": "obkljukal %s na kontrolnem seznamu %s", - "activity-unchecked-item-card": "odkljukal %s na kontrolnem seznamu %s", + "activity-customfield-created": "created custom field %s", + "activity-excluded": "excluded %s from %s", + "activity-imported": "imported %s into %s from %s", + "activity-imported-board": "imported %s from %s", + "activity-joined": "joined %s", + "activity-moved": "moved %s from %s to %s", + "activity-on": "on %s", + "activity-removed": "removed %s from %s", + "activity-sent": "sent %s to %s", + "activity-unjoined": "unjoined %s", + "activity-subtask-added": "added subtask to %s", + "activity-checked-item": "checked %s in checklist %s of %s", + "activity-unchecked-item": "unchecked %s in checklist %s of %s", + "activity-checklist-added": "added checklist to %s", + "activity-checklist-removed": "removed a checklist from %s", + "activity-checklist-completed": "completed checklist %s of %s", + "activity-checklist-uncompleted": "uncompleted the checklist %s of %s", + "activity-checklist-item-added": "added checklist item to '%s' in %s", + "activity-checklist-item-removed": "removed a checklist item from '%s' in %s", + "add": "Add", + "activity-checked-item-card": "checked %s in checklist %s", + "activity-unchecked-item-card": "unchecked %s in checklist %s", "activity-checklist-completed-card": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__", - "activity-checklist-uncompleted-card": "nedokončal kontrolni seznam %s", - "activity-editComment": "uredil komentar %s", - "activity-deleteComment": "izbrisal komentar %s", + "activity-checklist-uncompleted-card": "uncompleted the checklist %s", + "activity-editComment": "edited comment %s", + "activity-deleteComment": "deleted comment %s", "activity-receivedDate": "edited received date to %s of %s", "activity-startDate": "edited start date to %s of %s", "activity-dueDate": "edited due date to %s of %s", "activity-endDate": "edited end date to %s of %s", - "add-attachment": "Dodaj priponko", - "add-board": "Dodaj tablo", + "add-attachment": "Add Attachment", + "add-board": "Add Board", "add-template": "Add Template", - "add-card": "Dodaj kartico", + "add-card": "Add Card", "add-card-to-top-of-list": "Add Card to Top of List", "add-card-to-bottom-of-list": "Add Card to Bottom of List", "setListWidthPopup-title": "Set Widths", @@ -96,60 +96,60 @@ "set-swimlane-height": "Set Swimlane Height", "set-swimlane-height-value": "Swimlane Height (pixels)", "swimlane-height-error-message": "Swimlane height must be a positive integer", - "add-swimlane": "Dodaj plavalno stezo", - "add-subtask": "Dodaj podopravilo", - "add-checklist": "Dodaj kontrolni seznam", - "add-checklist-item": "Dodaj postavko na kontrolni seznam", + "add-swimlane": "Add Swimlane", + "add-subtask": "Add Subtask", + "add-checklist": "Add Checklist", + "add-checklist-item": "Add an item to checklist", "close-add-checklist-item": "Close add an item to checklist form", "close-edit-checklist-item": "Close edit an item to checklist form", "convertChecklistItemToCardPopup-title": "Convert to Card", "add-cover": "Add cover image to minicard", - "add-label": "Dodaj oznako", - "add-list": "Dodaj seznam", + "add-label": "Add Label", + "add-list": "Add List", "add-after-list": "Add After List", - "add-members": "Dodaj člane", - "added": "Dodano", - "addMemberPopup-title": "Člani", - "memberPopup-title": "Nastavitve članov", - "admin": "Administrator", - "admin-desc": "Lahko gleda in ureja kartice, odstrani člane ter spreminja nastavitve table.", - "admin-announcement": "Najava", - "admin-announcement-active": "Aktivna vse-sistemska najava", - "admin-announcement-title": "Najava od administratorja", - "all-boards": "Vse table", - "and-n-other-card": "In __count__ druga kartica", - "and-n-other-card_plural": "In __count__ drugih kartic", - "apply": "Uporabi", - "app-is-offline": "Nalaganje, prosimo počakajte. Osveževanje strani bo povzročilo izgubo podatkov. Če nalaganje ne deluje, preverite, ali se strežnik ni ustavil.", + "add-members": "Add Members", + "added": "Added", + "addMemberPopup-title": "Members", + "memberPopup-title": "Member Settings", + "admin": "Admin", + "admin-desc": "Can view and edit cards, remove members, and change settings for the board.", + "admin-announcement": "Announcement", + "admin-announcement-active": "Active System-Wide Announcement", + "admin-announcement-title": "Announcement from Administrator", + "all-boards": "All Boards", + "and-n-other-card": "And __count__ other card", + "and-n-other-card_plural": "And __count__ other cards", + "apply": "Apply", + "app-is-offline": "Loading, please wait. Refreshing the page will cause data loss. If loading does not work, please check that server has not stopped.", "app-try-reconnect": "Try to reconnect.", - "archive": "premaknjena v arhiv", - "archive-all": "Premakni vse v arhiv", - "archive-board": "Arhiviraj tablo", + "archive": "Move to Archive", + "archive-all": "Move All to Archive", + "archive-board": "Move Board to Archive", "archive-board-confirm": "Are you sure you want to archive this board?", - "archive-card": "Arhiviraj kartico", - "archive-list": "Arhiviraj seznam", - "archive-swimlane": "Arhiviraj plavalno stezo", - "archive-selection": "Arhiviraj označeno", - "archiveBoardPopup-title": "Arhiviraj tablo?", - "archived-items": "Arhiv", - "archived-boards": "Table v arhivu", - "restore-board": "Obnovi tablo", - "no-archived-boards": "Nobene table ni v arhivu.", - "archives": "Arhiv", - "template": "Predloga", - "templates": "Predloge", + "archive-card": "Move Card to Archive", + "archive-list": "Move List to Archive", + "archive-swimlane": "Move Swimlane to Archive", + "archive-selection": "Move selection to Archive", + "archiveBoardPopup-title": "Move Board to Archive?", + "archived-items": "Archive", + "archived-boards": "Boards in Archive", + "restore-board": "Restore Board", + "no-archived-boards": "No Boards in Archive.", + "archives": "Archive", + "template": "Template", + "templates": "Templates", "template-container": "Template Container", "add-template-container": "Add Template Container", - "assign-member": "Dodeli člana", - "attached": "pripeto", - "attachment": "Priponka", - "attachment-delete-pop": "Brisanje priponke je trajno. Ne obstaja razveljavitev.", - "attachmentDeletePopup-title": "Briši priponko?", - "attachments": "Priponke", - "auto-watch": "Samodejno spremljaj ustvarjene table", + "assign-member": "Assign member", + "attached": "attached", + "attachment": "Attachment", + "attachment-delete-pop": "Deleting an attachment is permanent. There is no undo.", + "attachmentDeletePopup-title": "Delete Attachment?", + "attachments": "Attachments", + "auto-watch": "Automatically watch boards when they are created", "avatar-too-big": "The avatar is too large (__size__ max)", - "back": "Nazaj", - "board-change-color": "Spremeni barvo", + "back": "Back", + "board-change-color": "Change color", "board-change-background-image": "Change Background Image", "board-background-image-url": "Background Image URL", "add-background-image": "Add Background Image", @@ -160,23 +160,23 @@ "boardInfoOnMyBoards-title": "All Boards Settings", "show-card-counter-per-list": "Show card count per list", "show-board_members-avatar": "Show Board members avatars", - "board-nb-stars": "%s zvezdic", - "board-not-found": "Tabla ni najdena", - "board-private-info": "Ta tabla bo privatna.", - "board-public-info": "Ta tabla bo javna.", + "board-nb-stars": "%s stars", + "board-not-found": "Board not found", + "board-private-info": "This board will be private.", + "board-public-info": "This board will be public.", "board-drag-drop-reorder-or-click-open": "Drag and drop to reorder board icons. Click board icon to open board.", - "boardChangeColorPopup-title": "Spremeni ozadje table", + "boardChangeColorPopup-title": "Change Board Background", "boardChangeBackgroundImagePopup-title": "Change Background Image", - "allBoardsChangeColorPopup-title": "Spremeni barvo", + "allBoardsChangeColorPopup-title": "Change color", "allBoardsChangeBackgroundImagePopup-title": "Change Background Image", - "boardChangeTitlePopup-title": "Preimenuj tablo", - "boardChangeVisibilityPopup-title": "Spremeni vidnost", - "boardChangeWatchPopup-title": "Spremeni opazovanje", - "boardMenuPopup-title": "Nastavitve table", - "allBoardsMenuPopup-title": "Nastavitve", - "boardChangeViewPopup-title": "Pogled table", - "boards": "Table", - "board-view": "Pogled table", + "boardChangeTitlePopup-title": "Rename Board", + "boardChangeVisibilityPopup-title": "Change Visibility", + "boardChangeWatchPopup-title": "Change Watch", + "boardMenuPopup-title": "Board Settings", + "allBoardsMenuPopup-title": "Settings", + "boardChangeViewPopup-title": "Board View", + "boards": "Boards", + "board-view": "Board View", "desktop-mode": "Desktop Mode", "mobile-mode": "Mobile Mode", "mobile-desktop-toggle": "Toggle between Mobile and Desktop Mode", @@ -185,35 +185,35 @@ "click-to-change-zoom": "Click to change zoom level", "zoom-level": "Zoom Level", "enter-zoom-level": "Enter zoom level (50-300%):", - "board-view-cal": "Koledar", - "board-view-swimlanes": "Plavalne steze", - "board-view-collapse": "Skrči", + "board-view-cal": "Calendar", + "board-view-swimlanes": "Swimlanes", + "board-view-collapse": "Collapse", "board-view-gantt": "Gantt", - "board-view-lists": "Seznami", - "bucket-example": "Kot na primer \"Življenjski seznam\"", - "cancel": "Prekliči", - "card-archived": "Kartica je premaknjena v arhiv.", - "board-archived": "Tabla je premaknjena v arhiv.", - "card-comments-title": "Ta kartica ima %s komentar.", - "card-delete-notice": "Brisanje je trajno. Izgubili boste vsa dejanja, povezana s kartico.", - "card-delete-pop": "Vsa dejanja bodo odstranjena iz zgodovine dejavnosti. Kartice ne boste mogli znova odpreti. Razveljavitve ni.", - "card-delete-suggest-archive": "Kartico lahko premaknete v arhiv, da jo odstranite s table in ohranite dejavnost.", + "board-view-lists": "Lists", + "bucket-example": "Like “Bucket List” for example", + "cancel": "Cancel", + "card-archived": "This card is moved to Archive.", + "board-archived": "This board is moved to Archive.", + "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.", + "card-delete-suggest-archive": "You can move a card to Archive to remove it from the board and preserve the activity.", "card-archive-pop": "Card will not be visible at this list after archiving card.", "card-archive-suggest-cancel": "You can later restore card from Archive.", "card-due": "Due", - "card-due-on": "Rok", - "card-spent": "Porabljen čas", - "card-edit-attachments": "Uredi priponke", - "card-edit-custom-fields": "Uredi poljubna polja", - "card-edit-labels": "Uredi oznake", - "card-edit-members": "Uredi člane", - "card-labels-title": "Spremeni oznake za kartico.", - "card-members-title": "Dodaj ali odstrani člane table iz kartice.", - "card-start": "Začetek", - "card-start-on": "Začne ob", - "cardAttachmentsPopup-title": "Pripni od", - "cardCustomField-datePopup-title": "Spremeni datum", - "cardCustomFieldsPopup-title": "Uredi poljubna polja", + "card-due-on": "Due on", + "card-spent": "Spent Time", + "card-edit-attachments": "Edit attachments", + "card-edit-custom-fields": "Edit custom fields", + "card-edit-labels": "Edit labels", + "card-edit-members": "Edit members", + "card-labels-title": "Change the labels for the card.", + "card-members-title": "Add or remove members of the board from the card.", + "card-start": "Start", + "card-start-on": "Starts on", + "cardAttachmentsPopup-title": "Attach From", + "cardCustomField-datePopup-title": "Change date", + "cardCustomFieldsPopup-title": "Edit custom fields", "cardStartVotingPopup-title": "Start a vote", "positiveVoteMembersPopup-title": "Proponents", "negativeVoteMembersPopup-title": "Opponents", @@ -247,168 +247,168 @@ "set-estimation": "Set Estimation", "deletePokerPopup-title": "Delete planning poker?", "poker-delete-pop": "Deleting is permanent. You will lose all actions associated with this planning poker.", - "cardDeletePopup-title": "Briši kartico?", + "cardDeletePopup-title": "Delete Card?", "cardArchivePopup-title": "Archive Card?", - "cardDetailsActionsPopup-title": "Dejanja kartice", - "cardLabelsPopup-title": "Oznake", - "cardMembersPopup-title": "Člani", - "cardMorePopup-title": "Več", - "cardTemplatePopup-title": "Ustvari predlogo", - "cards": "Kartic", - "cards-count": "Kartic", - "cards-count-one": "Kartica", - "casSignIn": "Vpiši se s CAS", - "cardType-card": "Kartica", - "cardType-linkedCard": "Povezana kartica", - "cardType-linkedBoard": "Povezana tabla", - "change": "Spremeni", - "change-avatar": "Spremeni avatar", - "change-password": "Spremeni geslo", - "change-permissions": "Spremeni dovoljenja", - "change-settings": "Spremeni nastavitve", - "changeAvatarPopup-title": "Spremeni avatar", - "changeLanguagePopup-title": "Spremeni jezik", - "changePasswordPopup-title": "Spremeni geslo", - "changePermissionsPopup-title": "Spremeni dovoljenja", - "changeSettingsPopup-title": "Spremeni nastavitve", - "subtasks": "Podopravila", - "checklists": "Kontrolni seznami", - "click-to-star": "Kliknite, da označite tablo z zvezdico.", - "click-to-unstar": "Kliknite, da odznačite tablo z zvezdico.", + "cardDetailsActionsPopup-title": "Card Actions", + "cardLabelsPopup-title": "Labels", + "cardMembersPopup-title": "Members", + "cardMorePopup-title": "More", + "cardTemplatePopup-title": "Create template", + "cards": "Cards", + "cards-count": "Cards", + "cards-count-one": "Card", + "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", + "change-permissions": "Change permissions", + "change-settings": "Change Settings", + "changeAvatarPopup-title": "Change Avatar", + "changeLanguagePopup-title": "Change Language", + "changePasswordPopup-title": "Change Password", + "changePermissionsPopup-title": "Change Permissions", + "changeSettingsPopup-title": "Change Settings", + "subtasks": "Subtasks", + "checklists": "Checklists", + "click-to-star": "Click to star this board.", + "click-to-unstar": "Click to unstar this board.", "click-to-enable-auto-width": "Auto list width disabled. Click to enable.", "click-to-disable-auto-width": "Auto list width enabled. Click to disable.", "auto-list-width": "Auto list width", - "clipboard": "Odložišče ali povleci & spusti", - "close": "Zapri", - "close-board": "Zapri tablo", - "close-board-pop": "Tablo boste lahko obnovili s klikom na gumb »Arhiviraj« na vstopni strani.", + "clipboard": "Clipboard or drag & drop", + "close": "Close", + "close-board": "Close Board", + "close-board-pop": "You will be able to restore the board by clicking the “Archive” button from the home header.", "close-card": "Close Card", - "color-black": "črna", - "color-blue": "modra", - "color-crimson": "temno rdeča", - "color-darkgreen": "temno zelena", - "color-gold": "zlata", - "color-gray": "siva", - "color-green": "zelena", + "color-black": "black", + "color-blue": "blue", + "color-crimson": "crimson", + "color-darkgreen": "darkgreen", + "color-gold": "gold", + "color-gray": "gray", + "color-green": "green", "color-indigo": "indigo", - "color-lime": "limeta", + "color-lime": "lime", "color-magenta": "magenta", - "color-mistyrose": "rožnata", - "color-navy": "navy modra", - "color-orange": "oranžna", - "color-paleturquoise": "bledo turkizna", - "color-peachpuff": "breskvasta", - "color-pink": "roza", - "color-plum": "slivova", - "color-purple": "vijolična", - "color-red": "rdeča", - "color-saddlebrown": "rjava", - "color-silver": "srebrna", - "color-sky": "nebesna", - "color-slateblue": "skrilasto modra", - "color-white": "bela", - "color-yellow": "rumena", - "unset-color": "Onemogoči", + "color-mistyrose": "mistyrose", + "color-navy": "navy", + "color-orange": "orange", + "color-paleturquoise": "paleturquoise", + "color-peachpuff": "peachpuff", + "color-pink": "pink", + "color-plum": "plum", + "color-purple": "purple", + "color-red": "red", + "color-saddlebrown": "saddlebrown", + "color-silver": "silver", + "color-sky": "sky", + "color-slateblue": "slateblue", + "color-white": "white", + "color-yellow": "yellow", + "unset-color": "Unset", "comments": "Comments", - "comment": "Komentiraj", - "comment-placeholder": "Napiši komentar", - "comment-only": "Samo komentar", - "comment-only-desc": "Lahko komentirate samo na karticah.", + "comment": "Comment", + "comment-placeholder": "Write Comment", + "comment-only": "Comment only", + "comment-only-desc": "Can comment on cards only.", "comment-delete": "Are you sure you want to delete the comment?", "deleteCommentPopup-title": "Delete comment?", - "no-comments": "Ni komentarjev", - "no-comments-desc": "Ne morete videti komentarjev in dejavnosti.", - "worker": "Delavec", - "worker-desc": "Lahko samo premikam kartice, se dodelim na kartico in komentiram.", - "computer": "Računalnik", - "confirm-subtask-delete-popup": "Ste prepričani, da želite izbrisati podopravilo?", + "no-comments": "No comments", + "no-comments-desc": "Can not see comments and activities.", + "worker": "Worker", + "worker-desc": "Can only move cards, assign itself to card and comment.", + "computer": "Computer", + "confirm-subtask-delete-popup": "Are you sure you want to delete subtask?", "confirm-checklist-delete-popup": "Are you sure you want to delete the checklist?", "subtaskDeletePopup-title": "Delete Subtask?", "checklistDeletePopup-title": "Delete Checklist?", - "copy-card-link-to-clipboard": "Kopiraj povezavo kartice na odložišče", + "copy-card-link-to-clipboard": "Copy card link to clipboard", "copy-text-to-clipboard": "Copy text to clipboard", - "linkCardPopup-title": "Poveži kartico", - "searchElementPopup-title": "Išči", - "copyCardPopup-title": "Kopiraj kartico", + "linkCardPopup-title": "Link Card", + "searchElementPopup-title": "Search", + "copyCardPopup-title": "Copy Card", "copyManyCardsPopup-title": "Copy Template to Many Cards", - "copyManyCardsPopup-instructions": "Naslovi ciljnih kartic in opisi v JSON formatu", - "copyManyCardsPopup-format": "[ {\"naslov\": \"Naslov prve kartice\", \"opis\":\"Opis prve kartice\"}, {\"naslov\":\"Opis druge kartice\",\"opis\":\"Opis druge kartice\"},{\"naslov\":\"Naslov zadnje kartice\",\"opis\":\"Opis zadnje kartice\"} ]", - "create": "Ustvari", - "createBoardPopup-title": "Ustvari tablo", - "chooseBoardSourcePopup-title": "Uvozi tablo", - "createLabelPopup-title": "Ustvari oznako", - "createCustomField": "Ustvari polje", - "createCustomFieldPopup-title": "Ustvari polje", - "current": "trenutno", - "custom-field-delete-pop": "Razveljavitve ni. To bo odstranilo to poljubno polje iz vseh kartic in izbrisalo njegovo zgodovino.", - "custom-field-checkbox": "Potrditveno polje", + "copyManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format", + "copyManyCardsPopup-format": "[ {\"title\": \"First card title\", \"description\":\"First card description\"}, {\"title\":\"Second card title\",\"description\":\"Second card description\"},{\"title\":\"Last card title\",\"description\":\"Last card description\"} ]", + "create": "Create", + "createBoardPopup-title": "Create Board", + "chooseBoardSourcePopup-title": "Import board", + "createLabelPopup-title": "Create Label", + "createCustomField": "Create Field", + "createCustomFieldPopup-title": "Create Field", + "current": "current", + "custom-field-delete-pop": "There is no undo. This will remove this custom field from all cards and destroy its history.", + "custom-field-checkbox": "Checkbox", "custom-field-currency": "Currency", "custom-field-currency-option": "Currency Code", - "custom-field-date": "Datum", - "custom-field-dropdown": "Spustni seznam", - "custom-field-dropdown-none": "(nobeno)", - "custom-field-dropdown-options": "Možnosti seznama", - "custom-field-dropdown-options-placeholder": "Pritisnite enter da dodate več možnosti", - "custom-field-dropdown-unknown": "(neznano)", - "custom-field-number": "Število", - "custom-field-text": "Besedilo", - "custom-fields": "Poljubna polja", - "date": "Datum", - "decline": "Zavrni", - "default-avatar": "Privzeti avatar", - "delete": "Briši", - "deleteCustomFieldPopup-title": "Briši poljubno polje?", - "deleteLabelPopup-title": "Briši oznako?", - "description": "Opis", - "disambiguateMultiLabelPopup-title": "Razdvoji Dejanje Oznake", - "disambiguateMultiMemberPopup-title": "Razdvoji dejanje člana", - "discard": "Razveljavi", - "done": "Končano", - "download": "Prenos", - "edit": "Uredi", - "edit-avatar": "Spremeni avatar", - "edit-profile": "Uredi profil", - "edit-wip-limit": "Uredi omejitev št. kartic", - "soft-wip-limit": "Omehčaj omejitev št. kartic", - "editCardStartDatePopup-title": "Spremeni začetni datum", - "editCardDueDatePopup-title": "Spremeni datum zapadlosti", - "editCustomFieldPopup-title": "Uredi polje", + "custom-field-date": "Date", + "custom-field-dropdown": "Dropdown List", + "custom-field-dropdown-none": "(none)", + "custom-field-dropdown-options": "List Options", + "custom-field-dropdown-options-placeholder": "Press enter to add more options", + "custom-field-dropdown-unknown": "(unknown)", + "custom-field-number": "Number", + "custom-field-text": "Text", + "custom-fields": "Custom Fields", + "date": "Date", + "decline": "Decline", + "default-avatar": "Default avatar", + "delete": "Delete", + "deleteCustomFieldPopup-title": "Delete Custom Field?", + "deleteLabelPopup-title": "Delete Label?", + "description": "Description", + "disambiguateMultiLabelPopup-title": "Disambiguate Label Action", + "disambiguateMultiMemberPopup-title": "Disambiguate Member Action", + "discard": "Discard", + "done": "Done", + "download": "Download", + "edit": "Edit", + "edit-avatar": "Change Avatar", + "edit-profile": "Edit Profile", + "edit-wip-limit": "Edit WIP Limit", + "soft-wip-limit": "Soft WIP Limit", + "editCardStartDatePopup-title": "Change start date", + "editCardDueDatePopup-title": "Change due date", + "editCustomFieldPopup-title": "Edit Field", "addReactionPopup-title": "Add reaction", - "editCardSpentTimePopup-title": "Spremeni porabljen čas", - "editLabelPopup-title": "Spremeni oznako", - "editNotificationPopup-title": "Uredi obvestilo", - "editProfilePopup-title": "Uredi profil", - "email": "E-pošta", - "email-enrollAccount-subject": "Up. račun ustvarjen za vas na __siteName__", - "email-enrollAccount-text": "Pozdravljeni __user__,\n\nZa začetek uporabe kliknite spodnjo povezavo.\n\n__url__\n\nHvala.", - "email-fail": "Pošiljanje e-pošte ni uspelo", - "email-fail-text": "Napaka pri poskusu pošiljanja e-pošte", - "email-invalid": "Neveljavna e-pošta", - "email-invite": "Povabi z uporabo e-pošte", - "email-invite-subject": "__inviter__ vam je poslal povabilo", - "email-invite-text": "Spoštovani __user__,\n\n__inviter__ vas vabi k sodelovanju na tabli \"__board__\".\n\nProsimo sledite spodnji povezavi:\n\n__url__\n\nHvala.", - "email-resetPassword-subject": "Ponastavite geslo na __siteName__", - "email-resetPassword-text": "Pozdravljeni __user__,\n\nZa ponastavitev gesla kliknite na spodnjo povezavo.\n\n__url__\n\nHvala.", - "email-sent": "E-pošta poslana", - "email-verifyEmail-subject": "Preverite svoje e-poštni naslov na __siteName__", - "email-verifyEmail-text": "Pozdravljeni __user__,\n\nDa preverite e-poštni naslov za vaš uporabniški račun, kliknite na spodnjo povezavo.\n\n__url__\n\nHvala.", + "editCardSpentTimePopup-title": "Change spent time", + "editLabelPopup-title": "Change Label", + "editNotificationPopup-title": "Edit Notification", + "editProfilePopup-title": "Edit Profile", + "email": "Email", + "email-enrollAccount-subject": "An account created for you on __siteName__", + "email-enrollAccount-text": "Hello __user__,\n\nTo start using the service, simply click the link below.\n\n__url__\n\nThanks.", + "email-fail": "Sending email failed", + "email-fail-text": "Error trying to send email", + "email-invalid": "Invalid email", + "email-invite": "Invite via Email", + "email-invite-subject": "__inviter__ sent you an invitation", + "email-invite-text": "Dear __user__,\n\n__inviter__ invites you to join board \"__board__\" for collaborations.\n\nPlease follow the link below:\n\n__url__\n\nThanks.", + "email-resetPassword-subject": "Reset your password on __siteName__", + "email-resetPassword-text": "Hello __user__,\n\nTo reset your password, simply click the link below.\n\n__url__\n\nThanks.", + "email-sent": "Email sent", + "email-verifyEmail-subject": "Verify your email address on __siteName__", + "email-verifyEmail-text": "Hello __user__,\n\nTo verify your account email, simply click the link below.\n\n__url__\n\nThanks.", "enable-vertical-scrollbars": "Enable vertical scrollbars", - "enable-wip-limit": "Vklopi omejitev št. kartic", - "error-board-doesNotExist": "Ta tabla ne obstaja", - "error-board-notAdmin": "Nimate administrativnih pravic za tablo.", - "error-board-notAMember": "Niste član table.", - "error-json-malformed": "Vaše besedilo ni veljaven JSON", - "error-json-schema": "Vaši JSON podatki ne vsebujejo pravilnih informacij v ustreznem formatu", + "enable-wip-limit": "Enable WIP Limit", + "error-board-doesNotExist": "This board does not exist", + "error-board-notAdmin": "You need to be admin of this board to do that", + "error-board-notAMember": "You need to be a member of this board to do that", + "error-json-malformed": "Your text is not valid JSON", + "error-json-schema": "Your JSON data does not include the proper information in the correct format", "error-csv-schema": "Your CSV(Comma Separated Values)/TSV (Tab Separated Values) does not include the proper information in the correct format ", - "error-list-doesNotExist": "Seznam ne obstaja", - "error-user-doesNotExist": "Uporabnik ne obstaja", - "error-user-notAllowSelf": "Ne morete povabiti sebe", - "error-user-notCreated": "Ta uporabnik ni ustvarjen", - "error-username-taken": "To up. ime že obstaja", + "error-list-doesNotExist": "This list does not exist", + "error-user-doesNotExist": "This user does not exist", + "error-user-notAllowSelf": "You can not invite yourself", + "error-user-notCreated": "This user is not created", + "error-username-taken": "This username is already taken", "error-orgname-taken": "This organization name is already taken", "error-teamname-taken": "This team name is already taken", - "error-email-taken": "E-poštni naslov je že zaseden", - "export-board": "Izvozi tablo", + "error-email-taken": "Email has already been taken", + "export-board": "Export board", "export-board-json": "Export board to JSON", "export-board-csv": "Export board to CSV", "export-board-tsv": "Export board to TSV", @@ -418,21 +418,21 @@ "export-card": "Export card", "export-card-pdf": "Export card to PDF", "user-can-not-export-card-to-pdf": "User can not export card to PDF", - "exportBoardPopup-title": "Izvozi tablo", + "exportBoardPopup-title": "Export board", "exportCardPopup-title": "Export card", - "sort": "Sortiraj", + "sort": "Sort", "sorted": "Sorted", "remove-sort": "Remove sort", - "sort-desc": "Klikni za sortiranje seznama", - "list-sort-by": "Sortiraj po:", - "list-label-modifiedAt": "Nazadnje dostopano", - "list-label-title": "Ime seznama", - "list-label-sort": "Ročno nastavljen vrstni red", - "list-label-short-modifiedAt": "(N)", - "list-label-short-title": "(I)", - "list-label-short-sort": "(R)", - "filter": "Filtriraj", - "filter-cards": "Filtriraj kartice ali sezname", + "sort-desc": "Click to Sort List", + "list-sort-by": "Sort the List By:", + "list-label-modifiedAt": "Last Access Time", + "list-label-title": "Name of the List", + "list-label-sort": "Your Manual Order", + "list-label-short-modifiedAt": "(L)", + "list-label-short-title": "(N)", + "list-label-short-sort": "(M)", + "filter": "Filter", + "filter-cards": "Filter Cards or Lists", "filter-dates-label": "Filter by date", "filter-no-due-date": "No due date", "filter-overdue": "Overdue", @@ -440,197 +440,197 @@ "filter-due-this-week": "Due this week", "filter-due-next-week": "Due next week", "filter-due-tomorrow": "Due tomorrow", - "list-filter-label": "Filtriraj seznam po imenu", - "filter-clear": "Počisti filter", + "list-filter-label": "Filter List by Title", + "filter-clear": "Clear filter", "filter-labels-label": "Filter by label", - "filter-no-label": "Brez oznake", + "filter-no-label": "No label", "filter-member-label": "Filter by member", - "filter-no-member": "Brez člana", + "filter-no-member": "No member", "filter-assignee-label": "Filter by assignee", "filter-no-assignee": "No assignee", "filter-custom-fields-label": "Filter by Custom Fields", - "filter-no-custom-fields": "Brez poljubnih polj", - "filter-show-archive": "Prikaži arhivirane sezname", - "filter-hide-empty": "Skrij prazne sezname", - "filter-on": "Filter vklopljen", - "filter-on-desc": "Filtrirane kartice na tej tabli. Kliknite tukaj za urejanje filtra.", - "filter-to-selection": "Filtriraj izbrane", + "filter-no-custom-fields": "No Custom Fields", + "filter-show-archive": "Show archived lists", + "filter-hide-empty": "Hide empty lists", + "filter-on": "Filter is on", + "filter-on-desc": "You are filtering cards on this board. Click here to edit filter.", + "filter-to-selection": "Filter to selection", "other-filters-label": "Other Filters", - "advanced-filter-label": "Napredni filter", - "advanced-filter-description": "Napredni filter omogoča pripravo niza, ki vsebuje naslednje operaterje: == != <= >= && || () Preslednica se uporablja kot ločilo med operatorji. Vsa polja po meri lahko filtrirate tako, da vtipkate njihova imena in vrednosti. Na primer: Polje1 == Vrednost1. Opomba: Če polja ali vrednosti vsebujejo presledke, jih morate postaviti v enojne narekovaje. Primer: 'Polje 1' == 'Vrednost 1'. Če želite preskočiti posamezne kontrolne znake (' \\\\/), lahko uporabite \\\\\\. Na primer: Polje1 == I\\\\'m. Prav tako lahko kombinirate več pogojev. Na primer: F1 == V1 || F1 == V2. Običajno se vsi operaterji interpretirajo od leve proti desni. Vrstni red lahko spremenite tako, da postavite oklepaje. Na primer: F1 == V1 && ( F2 == V2 || F2 == V3 ). Prav tako lahko po besedilu iščete z uporabo pravil regex: F1 == /Tes.*/i", - "fullname": "Polno Ime", - "header-logo-title": "Pojdi nazaj na stran s tablami.", + "advanced-filter-label": "Advanced Filter", + "advanced-filter-description": "Advanced Filter allows to write a string containing following operators: == != <= >= && || ( ) A space is used as a separator between the Operators. You can filter for all Custom Fields by typing their names and values. For Example: Field1 == Value1. Note: If fields or values contains spaces, you need to encapsulate them into single quotes. For Example: 'Field 1' == 'Value 1'. For single control characters (' \\/) to be skipped, you can use \\. For example: Field1 == I\\'m. Also you can combine multiple conditions. For Example: F1 == V1 || F1 == V2. Normally all operators are interpreted from left to right. You can change the order by placing brackets. For Example: F1 == V1 && ( F2 == V2 || F2 == V3 ). Also you can search text fields using regex: F1 == /Tes.*/i", + "fullname": "Full Name", + "header-logo-title": "Go back to your boards page.", "show-activities": "Show Activities", - "headerBarCreateBoardPopup-title": "Ustvari tablo", - "home": "Domov", - "import": "Uvozi", + "headerBarCreateBoardPopup-title": "Create Board", + "home": "Home", + "import": "Import", "impersonate-user": "Impersonate user", - "link": "Poveži", - "import-board": "uvozi tablo", - "import-board-c": "Uvozi tablo", - "import-board-title-trello": "Uvozi tablo iz orodja Trello", - "import-board-title-wekan": "Uvozi tablo iz prejšnjega izvoza", + "link": "Link", + "import-board": "import board", + "import-board-c": "Import board", + "import-board-title-trello": "Import board from Trello", + "import-board-title-wekan": "Import board from previous export", "import-board-title-csv": "Import board from CSV/TSV", - "from-trello": "Iz orodja Trello", - "from-wekan": "Od prejšnjega izvoza", + "from-trello": "From Trello", + "from-wekan": "From previous export", "from-csv": "From CSV/TSV", - "import-board-instruction-trello": "V vaši Trello tabli pojdite na 'Meni', 'Več', 'Natisni in Izvozi', 'Izvozi JSON', in kopirajte prikazano besedilo.", + "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.", "import-board-instruction-csv": "Paste in your Comma Separated Values(CSV)/ Tab Separated Values (TSV) .", - "import-board-instruction-wekan": "V vaši tabli pojdite na 'Meni', 'Izvozi tablo' in kopirajte besedilo iz prenesene datoteke.", - "import-board-instruction-about-errors": "Pri napakah med uvozom table v nekaterih primerih uvažanje še deluje, uvožena tabla pa je na strani Vse Table.", - "import-json-placeholder": "Tukaj prilepite veljavne JSON podatke", + "import-board-instruction-wekan": "In your board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.", + "import-board-instruction-about-errors": "If you get errors when importing board, sometimes importing still works, and board is at All Boards page.", + "import-json-placeholder": "Paste your valid JSON data here", "import-csv-placeholder": "Paste your valid CSV/TSV data here", - "import-map-members": "Mapiraj člane", - "import-members-map": "Vaša uvožena tabla vsebuje nekaj članov. Prosimo mapirajte člane, ki jih želite uvoziti, z vašimi uporabniki.", + "import-map-members": "Map members", + "import-members-map": "Your imported board has some members. Please map the members you want to import to your users", "import-members-map-note": "Note: Unmapped members will be assigned to the current user.", - "import-show-user-mapping": "Preglejte povezane člane", - "import-user-select": "Izberite obstoječega uporabnika, ki ga želite uporabiti kot tega člana.", - "importMapMembersAddPopup-title": "Izberite člana", - "info": "Različica", - "initials": "Inicialke", - "invalid-date": "Neveljaven datum", - "invalid-time": "Neveljaven čas", - "invalid-user": "Neveljaven uporabnik", - "joined": "se je pridružil", - "just-invited": "Povabljeni ste k tej tabli", - "keyboard-shortcuts": "Bližnjice", - "label-create": "Ustvari oznako", - "label-default": "%s oznaka (privzeto)", - "label-delete-pop": "Razveljavitve ni. To bo odstranilo oznako iz vseh kartic in izbrisalo njeno zgodovino.", - "labels": "Oznake", - "language": "Jezik", - "last-admin-desc": "Ne morete zamenjati vlog, ker mora obstajati vsaj en admin.", - "leave-board": "Zapusti tablo", - "leave-board-pop": "Ste prepričani, da želite zapustiti tablo __boardTitle__? Odstranjeni boste iz vseh kartic na tej tabli.", - "leaveBoardPopup-title": "Zapusti tablo ?", - "link-card": "Poveži s kartico", - "list-archive-cards": "Arhiviraj vse kartice v seznamu", - "list-archive-cards-pop": "To bo odstranilo vse kartice tega seznama. Za ogled in vrnitev kartic iz arhiva na tablo, kliknite \"Meni\" > \"arhiv\".", - "list-move-cards": "Premakni vse kartice na seznamu", - "list-select-cards": "Izberi vse kartice na seznamu", - "set-color-list": "Nastavi barvo", - "listActionPopup-title": "Dejanja seznama", + "import-show-user-mapping": "Review members mapping", + "import-user-select": "Pick your existing user you want to use as this member", + "importMapMembersAddPopup-title": "Select member", + "info": "Version", + "initials": "Initials", + "invalid-date": "Invalid date", + "invalid-time": "Invalid time", + "invalid-user": "Invalid user", + "joined": "joined", + "just-invited": "You are just invited to this board", + "keyboard-shortcuts": "Keyboard shortcuts", + "label-create": "Create Label", + "label-default": "%s label (default)", + "label-delete-pop": "There is no undo. This will remove this label from all cards and destroy its history.", + "labels": "Labels", + "language": "Language", + "last-admin-desc": "You can’t change roles because there must be at least one admin.", + "leave-board": "Leave Board", + "leave-board-pop": "Are you sure you want to leave __boardTitle__? You will be removed from all cards on this board.", + "leaveBoardPopup-title": "Leave Board ?", + "link-card": "Link to this card", + "list-archive-cards": "Move all cards in this list to Archive", + "list-archive-cards-pop": "This will remove all the cards in this list from the board. To view cards in Archive and bring them back to the board, click “Menu” > “Archive”.", + "list-move-cards": "Move all cards in this list", + "list-select-cards": "Select all cards in this list", + "set-color-list": "Set Color", + "listActionPopup-title": "List Actions", "settingsUserPopup-title": "User Settings", "settingsTeamPopup-title": "Team Settings", "settingsOrgPopup-title": "Organization Settings", - "swimlaneActionPopup-title": "Dejanja plavalnih stez", - "swimlaneAddPopup-title": "Dodaj plavalno stezo spodaj", - "listImportCardPopup-title": "Uvozi Trello kartico", + "swimlaneActionPopup-title": "Swimlane Actions", + "swimlaneAddPopup-title": "Add a Swimlane below", + "listImportCardPopup-title": "Import a Trello card", "listImportCardsTsvPopup-title": "Import Excel CSV/TSV", - "listMorePopup-title": "Več", - "link-list": "Poveži s seznamom", - "list-delete-pop": "Vsa dejanja bodo odstranjena iz vira dejavnosti in seznama ne boste mogli obnoviti. Razveljavitve ni.", - "list-delete-suggest-archive": "Lahko premaknete seznam v arhiv, da ga odstranite iz table in ohranite dejavnosti.", - "lists": "Seznami", - "swimlanes": "Plavalne steze", - "log-out": "Odjava", - "log-in": "Prijava", - "loginPopup-title": "Prijava", - "memberMenuPopup-title": "Nastavitve članov", - "members": "Člani", - "menu": "Meni", - "move-selection": "Premakni izbiro", - "moveCardPopup-title": "Premakni kartico", - "moveCardToBottom-title": "Premakni na dno", - "moveCardToTop-title": "Premakni na vrh", - "moveSelectionPopup-title": "Premakni izbiro", - "multi-selection": "Multi-Izbira", + "listMorePopup-title": "More", + "link-list": "Link to this list", + "list-delete-pop": "All actions will be removed from the activity feed and you won't be able to recover the list. There is no undo.", + "list-delete-suggest-archive": "You can move a list to Archive to remove it from the board and preserve the activity.", + "lists": "Lists", + "swimlanes": "Swimlanes", + "log-out": "Log Out", + "log-in": "Log In", + "loginPopup-title": "Log In", + "memberMenuPopup-title": "Member Settings", + "members": "Members", + "menu": "Menu", + "move-selection": "Move selection", + "moveCardPopup-title": "Move Card", + "moveCardToBottom-title": "Move to Bottom", + "moveCardToTop-title": "Move to Top", + "moveSelectionPopup-title": "Move selection", + "multi-selection": "Multi-Selection", "multi-selection-label": "Set label for selection", "multi-selection-member": "Set member for selection", - "multi-selection-on": "Multi-Izbira je omogočena", - "muted": "Utišano", - "muted-info": "O spremembah na tej tabli ne boste prejemali obvestil.", - "my-boards": "Moje Table", - "name": "Ime", - "no-archived-cards": "Ni kartic v arhivu", - "no-archived-lists": "Ni seznamov v arhivu", - "no-archived-swimlanes": "Ni plavalnih stez v arhivu", - "no-results": "Ni zadetkov", - "normal": "Normalno", - "normal-desc": "Lahko gleda in ureja kartice. Ne more spreminjati nastavitev.", - "not-accepted-yet": "Povabilo še ni sprejeto.", + "multi-selection-on": "Multi-Selection is on", + "muted": "Muted", + "muted-info": "You will never be notified of any changes in this board", + "my-boards": "My Boards", + "name": "Name", + "no-archived-cards": "No cards in Archive.", + "no-archived-lists": "No lists in Archive.", + "no-archived-swimlanes": "No swimlanes in Archive.", + "no-results": "No results", + "normal": "Normal", + "normal-desc": "Can view and edit cards. Can't change settings.", + "not-accepted-yet": "Invitation not accepted yet", "notify-participate": "Receive updates to any cards you participate as creator or member", - "notify-watch": "Prejemajte posodobitve opazovanih tabel, seznamov ali kartic", - "optional": "opcijsko", - "or": "ali", - "page-maybe-private": "Ta stran je morda privatna. Verjetno si jo lahko ogledate poprijavi.", - "page-not-found": "Stran ne obstaja.", - "password": "Geslo", - "paste-or-dragdrop": "prilepi ali povleci & spusti datoteko slike (samo slika)", - "participating": "Sodelovanje", - "preview": "Predogled", - "previewAttachedImagePopup-title": "Predogled", - "previewClipboardImagePopup-title": "Predogled", - "private": "Zasebno", - "private-desc": "Ta tabla je zasebna. Vsebino lahko vidijo ali urejajo samo dodani uporabniki.", - "profile": "Profil", - "public": "Javno", - "public-desc": "Ta tabla je javna. Vidna je vsakomur s povezavo do table in bo prikazana v zadetkih iskalnikov kot Google. Urejajo jo lahko samo člani table.", - "quick-access-description": "Če tablo označite z zvezdico, bo tukaj dodana bližnjica.", + "notify-watch": "Receive updates to any boards, lists, or cards you’re watching", + "optional": "optional", + "or": "or", + "page-maybe-private": "This page may be private. You may be able to view it by logging in.", + "page-not-found": "Page not found.", + "password": "Password", + "paste-or-dragdrop": "to paste, or drag & drop image file to it (image only)", + "participating": "Participating", + "preview": "Preview", + "previewAttachedImagePopup-title": "Preview", + "previewClipboardImagePopup-title": "Preview", + "private": "Private", + "private-desc": "This board is private. Only people added to the board can view and edit it.", + "profile": "Profile", + "public": "Public", + "public-desc": "This board is public. It's visible to anyone with the link and will show up in search engines like Google. Only people added to the board can edit.", + "quick-access-description": "Star a board to add a shortcut in this bar.", "remove-cover": "Remove cover image from minicard", - "remove-from-board": "Odstrani iz table", - "remove-label": "Odstrani oznako", - "listDeletePopup-title": "Odstrani seznam?", - "remove-member": "Odstrani člana", - "remove-member-from-card": "Odstrani iz kartice", - "remove-member-pop": "Odstrani __name__ (__username__) iz __boardTitle__? Član bo odstranjen iz vseh kartic te table in bo prejel obvestilo.", - "removeMemberPopup-title": "Odstrani člana?", - "rename": "Preimenuj", - "rename-board": "Preimenuj tablo", - "restore": "Obnovi", + "remove-from-board": "Remove from Board", + "remove-label": "Remove Label", + "listDeletePopup-title": "Delete List ?", + "remove-member": "Remove Member", + "remove-member-from-card": "Remove from Card", + "remove-member-pop": "Remove __name__ (__username__) from __boardTitle__? The member will be removed from all cards on this board. They will receive a notification.", + "removeMemberPopup-title": "Remove Member?", + "rename": "Rename", + "rename-board": "Rename Board", + "restore": "Restore", "rescue-card-description": "Show rescue dialogue before closing for unsaved card descriptions", "rescue-card-description-dialogue": "Overwrite current card description with your changes?", - "save": "Shrani", - "search": "Išči", - "rules": "Pravila", + "save": "Save", + "search": "Search", + "rules": "Rules", "search-cards": "Search from card/list titles, descriptions and custom fields on this board", "search-example": "Write text you search and press Enter", - "select-color": "Izberi barvo", + "select-color": "Select Color", "select-board": "Select Board", - "set-wip-limit-value": "Omeji maksimalno število opravil v seznamu", - "setWipLimitPopup-title": "Omeji število kartic", + "set-wip-limit-value": "Set a limit for the maximum number of tasks in this list", + "setWipLimitPopup-title": "Set WIP Limit", "shortcut-add-self": "Add yourself to current card", - "shortcut-assign-self": "Dodeli sebe k trenutni kartici", - "shortcut-autocomplete-emoji": "Samodokončaj emoji", - "shortcut-autocomplete-members": "Samodokončaj člane", - "shortcut-clear-filters": "Počisti vse filtre", - "shortcut-close-dialog": "Zapri dialog", - "shortcut-filter-my-cards": "Filtriraj moje kartice", + "shortcut-assign-self": "Assign yourself to current card", + "shortcut-autocomplete-emoji": "Autocomplete emoji", + "shortcut-autocomplete-members": "Autocomplete members", + "shortcut-clear-filters": "Clear all filters", + "shortcut-close-dialog": "Close Dialog", + "shortcut-filter-my-cards": "Filter my cards", "shortcut-filter-my-assigned-cards": "Filter my assigned cards", - "shortcut-show-shortcuts": "Prikaži seznam bližnjic", + "shortcut-show-shortcuts": "Bring up this shortcuts list", "shortcut-toggle-filterbar": "Toggle Filter Sidebar", "shortcut-toggle-searchbar": "Toggle Search Sidebar", - "shortcut-toggle-sidebar": "Preklopi stransko vrstico table", - "show-cards-minimum-count": "Prikaži število kartic, če seznam vsebuje več kot", - "sidebar-open": "Odpri stransko vrstico", - "sidebar-close": "Zapri stransko vrstico", - "signupPopup-title": "Ustvari up. račun", - "star-board-title": "Označite tablo z zvezdico, da bo prikazana na vrhu v seznamu tabel.", - "starred-boards": "Table z zvezdico", - "starred-boards-description": "Table z zvezdico se prikažejo na vrhu vašega seznama tabel.", - "subscribe": "Naročite se", - "team": "Skupina", - "this-board": "tablo", - "this-card": "kartico", - "spent-time-hours": "Porabljen čas (ure)", - "overtime-hours": "Presežen čas (ure)", - "overtime": "Presežen čas", - "has-overtime-cards": "Ima kartice s preseženim časom", - "has-spenttime-cards": "Ima kartice s porabljenim časom", - "time": "Čas", - "title": "Naslov", + "shortcut-toggle-sidebar": "Toggle Board Sidebar", + "show-cards-minimum-count": "Show cards count if list contains more than", + "sidebar-open": "Open Sidebar", + "sidebar-close": "Close Sidebar", + "signupPopup-title": "Create an Account", + "star-board-title": "Click to star this board. It will show up at top of your boards list.", + "starred-boards": "Starred Boards", + "starred-boards-description": "Starred boards show up at the top of your boards list.", + "subscribe": "Subscribe", + "team": "Team", + "this-board": "this board", + "this-card": "this card", + "spent-time-hours": "Spent time (hours)", + "overtime-hours": "Overtime (hours)", + "overtime": "Overtime", + "has-overtime-cards": "Has overtime cards", + "has-spenttime-cards": "Has spent time cards", + "time": "Time", + "title": "Title", "toggle-assignees": "Toggle assignees 1-9 for card (By order of addition to board).", "toggle-labels": "Toggle labels 1-9 for card. Multi-Selection adds labels 1-9", "remove-labels-multiselect": "Multi-Selection removes labels 1-9", - "tracking": "Sledenje", - "tracking-info": "Obveščeni boste o vseh spremembah nad karticami, kjer ste lastnik ali član.", - "type": "Tip", - "unassign-member": "Odjavi člana", - "unsaved-description": "Imate neshranjen opis.", - "unwatch": "Prekliči opazovanje", - "upload": "Naloži", - "upload-avatar": "Naloži avatar", - "uploaded-avatar": "Naložil avatar", + "tracking": "Tracking", + "tracking-info": "You will be notified of any changes to those cards you are involved as creator or member.", + "type": "Type", + "unassign-member": "Unassign member", + "unsaved-description": "You have an unsaved description.", + "unwatch": "Unwatch", + "upload": "Upload", + "upload-avatar": "Upload an avatar", + "uploaded-avatar": "Uploaded an avatar", "uploading-files": "Uploading files", "upload-failed": "Upload failed", "upload-completed": "Upload completed", @@ -642,317 +642,317 @@ "custom-help-link-url": "Custom Help Link URL", "text-below-custom-login-logo": "Text below Custom Login Logo", "automatic-linked-url-schemes": "Custom URL Schemes which should automatically be clickable. One URL Scheme per line", - "username": "Up. ime", + "username": "Username", "import-usernames": "Import Usernames", - "view-it": "Poglej", - "warn-list-archived": "opozorilo: ta kartica je v seznamu v arhivu", - "watch": "Opazuj", - "watching": "Opazuje", - "watching-info": "O spremembah na tej tabli boste obveščeni", - "welcome-board": "Tabla Dobrodošli", - "welcome-swimlane": "Mejnik 1", - "welcome-list1": "Osnove", - "welcome-list2": "Napredno", - "card-templates-swimlane": "Predloge kartice", - "list-templates-swimlane": "Predloge seznama", - "board-templates-swimlane": "Predloge table", - "what-to-do": "Kaj želite storiti?", - "wipLimitErrorPopup-title": "Neveljaven limit št. kartic", - "wipLimitErrorPopup-dialog-pt1": "Število opravil v seznamu je višje od limita št. kartic.", - "wipLimitErrorPopup-dialog-pt2": "Prosimo premaknite nekaj opravil iz tega seznama ali nastavite višji limit št. kartic.", - "admin-panel": "Skrbniška plošča", - "settings": "Nastavitve", - "people": "Ljudje", - "registration": "Registracija", - "disable-self-registration": "Onemogoči samo-registracijo", + "view-it": "View it", + "warn-list-archived": "warning: this card is in an list at Archive", + "watch": "Watch", + "watching": "Watching", + "watching-info": "You will be notified of any change in this board", + "welcome-board": "Welcome Board", + "welcome-swimlane": "Milestone 1", + "welcome-list1": "Basics", + "welcome-list2": "Advanced", + "card-templates-swimlane": "Card Templates", + "list-templates-swimlane": "List Templates", + "board-templates-swimlane": "Board Templates", + "what-to-do": "What do you want to do?", + "wipLimitErrorPopup-title": "Invalid WIP Limit", + "wipLimitErrorPopup-dialog-pt1": "The number of tasks in this list is higher than the WIP limit you've defined.", + "wipLimitErrorPopup-dialog-pt2": "Please move some tasks out of this list, or set a higher WIP limit.", + "admin-panel": "Admin Panel", + "settings": "Settings", + "people": "People", + "registration": "Registration", + "disable-self-registration": "Disable Self-Registration", "disable-forgot-password": "Disable Forgot Password", - "invite": "Povabi", - "invite-people": "Povabi ljudi", - "to-boards": "K tabli(am)", - "email-addresses": "E-poštni naslovi", - "smtp-host-description": "Naslov vašega strežnika SMTP.", - "smtp-port-description": "Vrata vašega strežnika SMTP za odhodno pošto.", - "smtp-tls-description": "Omogoči šifriranje TLS za SMTP strežnik.", + "invite": "Invite", + "invite-people": "Invite People", + "to-boards": "To board(s)", + "email-addresses": "Email Addresses", + "smtp-host-description": "The address of the SMTP server that handles your emails.", + "smtp-port-description": "The port your SMTP server uses for outgoing emails.", + "smtp-tls-description": "Enable TLS support for SMTP server", "smtp-host": "SMTP Host", - "smtp-port": "SMTP vrata", - "smtp-username": "Up. ime", - "smtp-password": "Geslo", - "smtp-tls": "TLS podpora", - "send-from": "Od", - "send-smtp-test": "Pošljite testno e-pošto na svoj naslov", - "invitation-code": "Koda Povabila", - "email-invite-register-subject": "__inviter__ vam je poslal povabilo", - "email-invite-register-text": "Dragi __user__,\n\n__inviter__ vas vabi na kanban tablo za sodelovanje.\n\nProsimo sledite spodnji povezavi:\n__url__\n\nVaša koda povabila je: __icode__\n\nHvala.", - "email-smtp-test-subject": "SMTP testna e-pošta", - "email-smtp-test-text": "Uspešno ste poslali e-pošto", - "error-invitation-code-not-exist": "Koda povabila ne obstaja", - "error-notAuthorized": "Nimate pravic za ogled te strani.", - "webhook-title": "Ime spletnega vmesnika (webhook)", - "webhook-token": "Žeton (opcijsko za avtentikacijo)", - "outgoing-webhooks": "Izhodni spletni vmesniki (webhooks)", - "bidirectional-webhooks": "Dvo-smerni spletni vmesniki (webhooks)", - "outgoingWebhooksPopup-title": "Izhodni spletni vmesniki (webhooks)", - "boardCardTitlePopup-title": "Filter po naslovu kartice", - "disable-webhook": "Onemogoči ta spletni vmesnik (webhook)", - "global-webhook": "Globalni spletni vmesnik (webhook)", - "new-outgoing-webhook": "Nov izhodni spletni vmesnik (webhook)", - "no-name": "(Neznano)", - "Node_version": "Node različica", - "Meteor_version": "Meteor različica", - "MongoDB_version": "MongoDB različica", + "smtp-port": "SMTP Port", + "smtp-username": "Username", + "smtp-password": "Password", + "smtp-tls": "TLS support", + "send-from": "From", + "send-smtp-test": "Send a test email to yourself", + "invitation-code": "Invitation Code", + "email-invite-register-subject": "__inviter__ sent you an invitation", + "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to kanban board for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.", + "email-smtp-test-subject": "SMTP Test Email", + "email-smtp-test-text": "You have successfully sent an email", + "error-invitation-code-not-exist": "Invitation code doesn't exist", + "error-notAuthorized": "You are not authorized to view this page.", + "webhook-title": "Webhook Name", + "webhook-token": "Token (Optional for Authentication)", + "outgoing-webhooks": "Outgoing Webhooks", + "bidirectional-webhooks": "Two-Way Webhooks", + "outgoingWebhooksPopup-title": "Outgoing Webhooks", + "boardCardTitlePopup-title": "Card Title Filter", + "disable-webhook": "Disable This Webhook", + "global-webhook": "Global Webhooks", + "new-outgoing-webhook": "New Outgoing Webhook", + "no-name": "(Unknown)", + "Node_version": "Node version", + "Meteor_version": "Meteor version", + "MongoDB_version": "MongoDB version", "MongoDB_storage_engine": "MongoDB storage engine", - "MongoDB_Oplog_enabled": "MongoDB Oplog omogočen", - "OS_Arch": "OS Arhitektura", - "OS_Cpus": "OS število CPU", - "OS_Freemem": "OS prost pomnilnik", - "OS_Loadavg": "OS povp. obremenitev", - "OS_Platform": "OS platforma", - "OS_Release": "OS izdaja", - "OS_Totalmem": "OS skupni pomnilnik", - "OS_Type": "OS tip", - "OS_Uptime": "OS čas delovanja", - "days": "dnevi", - "hours": "ure", - "minutes": "minute", - "seconds": "sekunde", - "show-field-on-card": "Prikaži to polje na kartici", + "MongoDB_Oplog_enabled": "MongoDB Oplog enabled", + "OS_Arch": "OS Arch", + "OS_Cpus": "OS CPU Count", + "OS_Freemem": "OS Free Memory", + "OS_Loadavg": "OS Load Average", + "OS_Platform": "OS Platform", + "OS_Release": "OS Release", + "OS_Totalmem": "OS Total Memory", + "OS_Type": "OS Type", + "OS_Uptime": "OS Uptime", + "days": "days", + "hours": "hours", + "minutes": "minutes", + "seconds": "seconds", + "show-field-on-card": "Show this field on card", "automatically-field-on-card": "Add field to new cards", "always-field-on-card": "Add field to all cards", - "showLabel-field-on-card": "Prikaži oznako polja na mini kartici", + "showLabel-field-on-card": "Show field label on minicard", "showSum-field-on-list": "Show sum of fields at top of list", - "yes": "Da", - "no": "Ne", - "accounts": "Up. računi", - "accounts-allowEmailChange": "Dovoli spremembo e-poštnega naslova", - "accounts-allowUserNameChange": "Dovoli spremembo up. imena", + "yes": "Yes", + "no": "No", + "accounts": "Accounts", + "accounts-allowEmailChange": "Allow Email Change", + "accounts-allowUserNameChange": "Allow Username Change", "tableVisibilityMode-allowPrivateOnly": "Boards visibility: Allow private boards only", "tableVisibilityMode" : "Boards visibility", - "createdAt": "Ustvarjen ob", + "createdAt": "Created at", "modifiedAt": "Modified at", - "verified": "Preverjeno", - "active": "Aktivno", - "card-received": "Prejeto", - "card-received-on": "Prejeto ob", - "card-end": "Konec", - "card-end-on": "Končano na", - "editCardReceivedDatePopup-title": "Spremeni datum prejema", - "editCardEndDatePopup-title": "Spremeni končni datum", - "setCardColorPopup-title": "Nastavi barvo", - "setCardActionsColorPopup-title": "Izberi barvo", - "setSwimlaneColorPopup-title": "Izberi barvo", - "setListColorPopup-title": "Izberi barvo", - "assigned-by": "Dodelil", - "requested-by": "Zahteval", + "verified": "Verified", + "active": "Active", + "card-received": "Received", + "card-received-on": "Received on", + "card-end": "End", + "card-end-on": "Ends on", + "editCardReceivedDatePopup-title": "Change received date", + "editCardEndDatePopup-title": "Change end date", + "setCardColorPopup-title": "Set color", + "setCardActionsColorPopup-title": "Choose a color", + "setSwimlaneColorPopup-title": "Choose a color", + "setListColorPopup-title": "Choose a color", + "assigned-by": "Assigned By", + "requested-by": "Requested By", "card-sorting-by-number": "Card sorting by number", - "board-delete-notice": "Brisanje je trajno. Izgubili boste vse sezname, kartice in akcije, povezane z desko.", - "delete-board-confirm-popup": "Vsi seznami, kartice, oznake in dejavnosti bodo izbrisani in vsebine table ne boste mogli obnoviti. Razveljavitve ni.", - "boardDeletePopup-title": "Izbriši tablo?", - "delete-board": "Izbriši tablo", - "default-subtasks-board": "Podopravila za tablo", - "default": "Privzeto", - "defaultdefault": "Privzeto", - "queue": "Čakalna vrsta", - "subtask-settings": "Nastavitve podopravil", - "card-settings": "Nastavitve kartice", + "board-delete-notice": "Deleting is permanent. You will lose all lists, cards and actions associated with this board.", + "delete-board-confirm-popup": "All lists, cards, labels, and activities will be deleted and you won't be able to recover the board contents. There is no undo.", + "boardDeletePopup-title": "Delete Board?", + "delete-board": "Delete Board", + "default-subtasks-board": "Subtasks for __board__ board", + "default": "Default", + "defaultdefault": "Default", + "queue": "Queue", + "subtask-settings": "Subtasks Settings", + "card-settings": "Card Settings", "minicard-settings": "Minicard Settings", - "boardSubtaskSettingsPopup-title": "Nastavitve podopravil table", - "boardCardSettingsPopup-title": "Nastavitve kartice", + "boardSubtaskSettingsPopup-title": "Board Subtasks Settings", + "boardCardSettingsPopup-title": "Card Settings", "boardMinicardSettingsPopup-title": "Minicard Settings", - "deposit-subtasks-board": "Deponiraj podopravila na tablo:", - "deposit-subtasks-list": "Ciljni seznam za deponirana podopravila:", - "show-parent-in-minicard": "Pokaži starša na mini-kartici:", + "deposit-subtasks-board": "Deposit subtasks to this board:", + "deposit-subtasks-list": "Landing list for subtasks deposited here:", + "show-parent-in-minicard": "Show parent in minicard:", "description-on-minicard": "Description on minicard", "cover-attachment-on-minicard": "Cover image on minicard", "badge-attachment-on-minicard": "Count of attachments on minicard", "card-sorting-by-number-on-minicard": "Card sorting by number on minicard", - "prefix-with-full-path": "Predpona s celotno potjo", - "prefix-with-parent": "Predpona s staršem", - "subtext-with-full-path": "Podbesedilo s celotno potjo", - "subtext-with-parent": "Podbesedilo s staršem", - "change-card-parent": "Zamenjaj starša kartice", - "parent-card": "Starševska kartica", - "source-board": "Izvorna tabla", - "no-parent": "Ne prikaži starša", - "activity-added-label": "dodal oznako '%s' do %s", - "activity-removed-label": "odstranil oznako '%s' od %s", - "activity-delete-attach": "izbrisal priponko od %s", - "activity-added-label-card": "dodal oznako '%s'", - "activity-removed-label-card": "izbrisal oznako '%s'", - "activity-delete-attach-card": "izbrisal priponko", - "activity-set-customfield": "nastavi polje po meri '%s' do '%s' v %s", - "activity-unset-customfield": "zbriši polje po meri '%s' v %s", - "r-rule": "Pravilo", - "r-add-trigger": "Dodaj prožilec", - "r-add-action": "Dodaj akcijo", - "r-board-rules": "Pravila table", - "r-add-rule": "Dodaj pravilo", - "r-view-rule": "Poglej pravilo", - "r-delete-rule": "Izbriši pravilo", - "r-new-rule-name": "Ime novega pravila", - "r-no-rules": "Ni pravil", + "prefix-with-full-path": "Prefix with full path", + "prefix-with-parent": "Prefix with parent", + "subtext-with-full-path": "Subtext with full path", + "subtext-with-parent": "Subtext with parent", + "change-card-parent": "Change card's parent", + "parent-card": "Parent card", + "source-board": "Source board", + "no-parent": "Don't show parent", + "activity-added-label": "added label '%s' to %s", + "activity-removed-label": "removed label '%s' from %s", + "activity-delete-attach": "deleted an attachment from %s", + "activity-added-label-card": "added label '%s'", + "activity-removed-label-card": "removed label '%s'", + "activity-delete-attach-card": "deleted an attachment", + "activity-set-customfield": "set custom field '%s' to '%s' in %s", + "activity-unset-customfield": "unset custom field '%s' in %s", + "r-rule": "Rule", + "r-add-trigger": "Add trigger", + "r-add-action": "Add action", + "r-board-rules": "Board rules", + "r-add-rule": "Add rule", + "r-view-rule": "View rule", + "r-delete-rule": "Delete rule", + "r-new-rule-name": "New rule title", + "r-no-rules": "No rules", "r-trigger": "Trigger", "r-action": "Action", - "r-when-a-card": "Ko je kartica", - "r-is": " ", - "r-is-moved": "premaknjena", + "r-when-a-card": "When a card", + "r-is": "is", + "r-is-moved": "is moved", "r-added-to": "Added to", - "r-removed-from": "izbrisan iz", - "r-the-board": "tabla", - "r-list": "seznam", - "set-filter": "Nastavi filter", - "r-moved-to": "premaknjena v", - "r-moved-from": "premaknjena iz", - "r-archived": "premaknjena v arhiv", - "r-unarchived": "obnovljena iz arhiva", - "r-a-card": "kartico", - "r-when-a-label-is": "Ko je oznaka", - "r-when-the-label": "Ko je oznaka", - "r-list-name": "ime sezn.", - "r-when-a-member": "Ko je član", - "r-when-the-member": "Ko je član", - "r-name": "ime", - "r-when-a-attach": "Ko je priponka", - "r-when-a-checklist": "Ko je kontrolni seznam", - "r-when-the-checklist": "Ko kontrolni seznam", - "r-completed": "zaključen", - "r-made-incomplete": "nastavljen kot nedokončan", - "r-when-a-item": "Ko je kontrolni seznam", - "r-when-the-item": "Ko je element kontrolnega seznama", - "r-checked": "označen", - "r-unchecked": "odznačen", - "r-move-card-to": "Premakni kartico na", - "r-top-of": "Vrh", - "r-bottom-of": "Dno", - "r-its-list": "pripadajočega seznama", - "r-archive": "premaknjena v arhiv", - "r-unarchive": "Obnovi iz arhiva", - "r-card": "kartico", - "r-add": "Dodaj", - "r-remove": "Odstrani", - "r-label": "oznaka", - "r-member": "član", - "r-remove-all": "Izbriši vse člane iz kartice", - "r-set-color": "Nastavi barvo na", - "r-checklist": "kontrolni seznam", - "r-check-all": "Označi vse", - "r-uncheck-all": "Odznači vse", - "r-items-check": "postavke kontrolnega lista", - "r-check": "Označi", - "r-uncheck": "Odznači", - "r-item": "postavka", - "r-of-checklist": "kontrolnega seznama", - "r-send-email": "Pošlji e-pošto", - "r-to": "naslovnik", + "r-removed-from": "Removed from", + "r-the-board": "the board", + "r-list": "list", + "set-filter": "Set Filter", + "r-moved-to": "Moved to", + "r-moved-from": "Moved from", + "r-archived": "Moved to Archive", + "r-unarchived": "Restored from Archive", + "r-a-card": "a card", + "r-when-a-label-is": "When a label is", + "r-when-the-label": "When the label", + "r-list-name": "list name", + "r-when-a-member": "When a member is", + "r-when-the-member": "When the member", + "r-name": "name", + "r-when-a-attach": "When an attachment", + "r-when-a-checklist": "When a checklist is", + "r-when-the-checklist": "When the checklist", + "r-completed": "Completed", + "r-made-incomplete": "Made incomplete", + "r-when-a-item": "When a checklist item is", + "r-when-the-item": "When the checklist item", + "r-checked": "Checked", + "r-unchecked": "Unchecked", + "r-move-card-to": "Move card to", + "r-top-of": "Top of", + "r-bottom-of": "Bottom of", + "r-its-list": "its list", + "r-archive": "Move to Archive", + "r-unarchive": "Restore from Archive", + "r-card": "card", + "r-add": "Add", + "r-remove": "Remove", + "r-label": "label", + "r-member": "member", + "r-remove-all": "Remove all members from the card", + "r-set-color": "Set color to", + "r-checklist": "checklist", + "r-check-all": "Check all", + "r-uncheck-all": "Uncheck all", + "r-items-check": "items of checklist", + "r-check": "Check", + "r-uncheck": "Uncheck", + "r-item": "item", + "r-of-checklist": "of checklist", + "r-send-email": "Send an email", + "r-to": "to", "r-of": "of", - "r-subject": "zadeva", - "r-rule-details": "Podrobnosti pravila", - "r-d-move-to-top-gen": "Premakni kartico na vrh pripadajočega sezama", - "r-d-move-to-top-spec": "Premakni kartico na vrh seznama", - "r-d-move-to-bottom-gen": "Premakni kartico na dno pripadajočega seznama", - "r-d-move-to-bottom-spec": "Premakni kartico na dno seznama", - "r-d-send-email": "Pošlji e-pošto", - "r-d-send-email-to": "naslovnik", - "r-d-send-email-subject": "zadeva", - "r-d-send-email-message": "vsebina", - "r-d-archive": "Premakni kartico v arhiv", - "r-d-unarchive": "Obnovi kartico iz arhiva", - "r-d-add-label": "Dodaj oznako", - "r-d-remove-label": "Izbriši oznako", - "r-create-card": "Ustvari novo kartico", - "r-in-list": "v seznamu", - "r-in-swimlane": "v plavalni stezi", - "r-d-add-member": "Dodaj člana", - "r-d-remove-member": "Odstrani člana", - "r-d-remove-all-member": "Odstrani vse člane", - "r-d-check-all": "Označi vse elemente seznama", - "r-d-uncheck-all": "Odznači vse elemente seznama", - "r-d-check-one": "Označi element", - "r-d-uncheck-one": "Odznači element", - "r-d-check-of-list": "kontrolnega seznama", - "r-d-add-checklist": "Dodaj kontrolni list", - "r-d-remove-checklist": "Odstrani kotrolni list", - "r-by": "od", - "r-add-checklist": "Dodaj kontrolni list", - "r-with-items": "s postavkami", - "r-items-list": "el1,el2,el3", - "r-add-swimlane": "Dodaj plavalno stezo", - "r-swimlane-name": "ime pl. steze", + "r-subject": "subject", + "r-rule-details": "Rule details", + "r-d-move-to-top-gen": "Move card to top of its list", + "r-d-move-to-top-spec": "Move card to top of list", + "r-d-move-to-bottom-gen": "Move card to bottom of its list", + "r-d-move-to-bottom-spec": "Move card to bottom of list", + "r-d-send-email": "Send email", + "r-d-send-email-to": "to", + "r-d-send-email-subject": "subject", + "r-d-send-email-message": "message", + "r-d-archive": "Move card to Archive", + "r-d-unarchive": "Restore card from Archive", + "r-d-add-label": "Add label", + "r-d-remove-label": "Remove label", + "r-create-card": "Create new card", + "r-in-list": "in list", + "r-in-swimlane": "in swimlane", + "r-d-add-member": "Add member", + "r-d-remove-member": "Remove member", + "r-d-remove-all-member": "Remove all member", + "r-d-check-all": "Check all items of a list", + "r-d-uncheck-all": "Uncheck all items of a list", + "r-d-check-one": "Check item", + "r-d-uncheck-one": "Uncheck item", + "r-d-check-of-list": "of checklist", + "r-d-add-checklist": "Add checklist", + "r-d-remove-checklist": "Remove checklist", + "r-by": "by", + "r-add-checklist": "Add checklist", + "r-with-items": "with items", + "r-items-list": "item1,item2,item3", + "r-add-swimlane": "Add swimlane", + "r-swimlane-name": "swimlane name", "r-board-note": "Note: leave a field empty to match every possible value. ", - "r-checklist-note": "Opomba: elementi kontrolnega seznama morajo biti zapisani kot vrednosti, ločene z vejicami.", - "r-when-a-card-is-moved": "Ko je kartica premaknjena v drug seznam", - "r-set": "Nastavi", - "r-update": "Posodobi", - "r-datefield": "polje z datumom", - "r-df-start-at": "začetek", - "r-df-due-at": "rok", - "r-df-end-at": "konec", - "r-df-received-at": "prejeto", - "r-to-current-datetime": "v trenutni datum/čas", - "r-remove-value-from": "Izbriši vrednost iz", + "r-checklist-note": "Note: checklist's items have to be written as comma separated values.", + "r-when-a-card-is-moved": "When a card is moved to another list", + "r-set": "Set", + "r-update": "Update", + "r-datefield": "date field", + "r-df-start-at": "start", + "r-df-due-at": "due", + "r-df-end-at": "end", + "r-df-received-at": "received", + "r-to-current-datetime": "to current date/time", + "r-remove-value-from": "Remove value from", "r-link-card": "Link card to", "ldap": "LDAP", "oauth2": "OAuth2", "cas": "CAS", - "authentication-method": "Metoda avtentikacije", - "authentication-type": "Način avtentikacije", - "custom-product-name": "Ime izdelka po meri", - "layout": "Postavitev", - "hide-logo": "Skrij logo", + "authentication-method": "Authentication method", + "authentication-type": "Authentication type", + "custom-product-name": "Custom Product Name", + "layout": "Layout", + "hide-logo": "Hide Logo", "hide-card-counter-list": "Hide card counter list on All Boards", "hide-board-member-list": "Hide board member list on All Boards", - "add-custom-html-after-body-start": "Dodaj HTML po meri po začetku", - "add-custom-html-before-body-end": "Dodaj HMTL po meri po koncu", - "error-undefined": "Prišlo je do napake", - "error-ldap-login": "Prišlo je do napake ob prijavi", - "display-authentication-method": "Prikaži metodo avtentikacije", + "add-custom-html-after-body-start": "Add Custom HTML after start", + "add-custom-html-before-body-end": "Add Custom HTML before end", + "error-undefined": "Something went wrong", + "error-ldap-login": "An error occurred while trying to login", + "display-authentication-method": "Display Authentication Method", "oidc-button-text": "Customize the OIDC button text", - "default-authentication-method": "Privzeta metoda avtentikacije", - "duplicate-board": "Dupliciraj tablo", + "default-authentication-method": "Default Authentication Method", + "duplicate-board": "Duplicate Board", "duplicate-board-confirm": "Are you sure you want to duplicate this board?", "org-number": "The number of organizations is: ", "team-number": "The number of teams is: ", "people-number": "The number of people is: ", - "swimlaneDeletePopup-title": "Zbriši plavalno stezo?", - "swimlane-delete-pop": "Vsa dejanja bodo odstranjena iz seznama dejavnosti. Plavalne steze ne boste mogli obnoviti. Razveljavitve ni.", - "restore-all": "Obnovi vse", - "delete-all": "Izbriši vse", - "loading": "Nalagam, prosimo počakajte", - "previous_as": "zadnji čas je bil", - "act-a-dueAt": "spremenil rok zapadlosti na \nKdaj: __timeValue__\nKje: __card__\n prejšnji rok zapadlosti je bil __timeOldValue__", - "act-a-endAt": "spremenil čas dokončanja na __timeValue__ iz (__timeOldValue__)", - "act-a-startAt": "spremenil čas pričetka na __timeValue__ iz (__timeOldValue__)", - "act-a-receivedAt": "spremenil čas prejema na __timeValue__ iz (__timeOldValue__)", - "a-dueAt": "spremenil rok v", - "a-endAt": "spremenil končni čas v", - "a-startAt": "spremenil začetni čas v", - "a-receivedAt": "spremenil čas prejetja v", - "almostdue": "trenutni rok %s se približuje", - "pastdue": "trenutni rok %s je potekel", - "duenow": "trenutni rok %s je danes", - "act-newDue": "__list__/__card__ ima 1. opomnik roka zapadlosti [__board__]", - "act-withDue": "__list__/__card__ opomniki roka zapadlosti [__board__]", - "act-almostdue": "je opomnil trenuten rok zapadlosti (__timeValue__) kartice __card__ se bliža", - "act-pastdue": "je opomnil trenuten rok zapadlosti (__timeValue__) kartice __card__ je potekel", - "act-duenow": "je opomnil trenuten rok zapadlosti (__timeValue__) kartice __card__ je sedaj", - "act-atUserComment": "Omenjeni ste bili v [__board__] __list__/__card__", - "delete-user-confirm-popup": "Ali ste prepričani, da želite izbrisati ta račun? Razveljavitve ni.", + "swimlaneDeletePopup-title": "Delete Swimlane ?", + "swimlane-delete-pop": "All actions will be removed from the activity feed and you won't be able to recover the swimlane. There is no undo.", + "restore-all": "Restore all", + "delete-all": "Delete all", + "loading": "Loading, please wait.", + "previous_as": "last time was", + "act-a-dueAt": "modified due time to \nWhen: __timeValue__\nWhere: __card__\n previous due was __timeOldValue__", + "act-a-endAt": "modified ending time to __timeValue__ from (__timeOldValue__)", + "act-a-startAt": "modified starting time to __timeValue__ from (__timeOldValue__)", + "act-a-receivedAt": "modified received time to __timeValue__ from (__timeOldValue__)", + "a-dueAt": "modified due time to be", + "a-endAt": "modified ending time to be", + "a-startAt": "modified starting time to be", + "a-receivedAt": "modified received time to be", + "almostdue": "current due time %s is approaching", + "pastdue": "current due time %s is past", + "duenow": "current due time %s is today", + "act-newDue": "__list__/__card__ has 1st due reminder [__board__]", + "act-withDue": "__list__/__card__ due reminders [__board__]", + "act-almostdue": "was reminding the current due (__timeValue__) of __card__ is approaching", + "act-pastdue": "was reminding the current due (__timeValue__) of __card__ is past", + "act-duenow": "was reminding the current due (__timeValue__) of __card__ is now", + "act-atUserComment": "You were mentioned in [__board__] __list__/__card__", + "delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.", "delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.", "delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.", - "accounts-allowUserDelete": "Dovoli uporabnikom, da sami izbrišejo svoj račun", - "hide-minicard-label-text": "Skrij besedilo oznak na karticah", - "show-desktop-drag-handles": "Pokaži ročke za povleko na namizju", - "assignee": "Dodeljen član", - "cardAssigneesPopup-title": "Dodeljen član", - "addmore-detail": "Dodaj podrobnejši opis", - "show-on-card": "Prikaži na kartici", + "accounts-allowUserDelete": "Allow users to self delete their account", + "hide-minicard-label-text": "Hide minicard label text", + "show-desktop-drag-handles": "Show desktop drag handles", + "assignee": "Assignee", + "cardAssigneesPopup-title": "Assignee", + "addmore-detail": "Add a more detailed description", + "show-on-card": "Show on Card", "show-on-minicard": "Show on Minicard", - "new": "Novo", + "new": "New", "editOrgPopup-title": "Edit Organization", "newOrgPopup-title": "New Organization", "editTeamPopup-title": "Edit Team", "newTeamPopup-title": "New Team", - "editUserPopup-title": "Uredi uporabnika", - "newUserPopup-title": "Nov uporabnik", + "editUserPopup-title": "Edit User", + "newUserPopup-title": "New User", "notifications": "Notifications", "help": "Help", "view-all": "View All", @@ -991,13 +991,13 @@ "website": "Website", "person": "Person", "my-cards": "My Cards", - "card": "Kartica", + "card": "Card", "list": "List", "board": "Board", "context-separator": "/", "myCardsViewChange-title": "My Cards View", "myCardsViewChangePopup-title": "My Cards View", - "myCardsViewChange-choice-boards": "Table", + "myCardsViewChange-choice-boards": "Boards", "myCardsViewChange-choice-table": "Table", "myCardsSortChange-title": "My Cards Sort", "myCardsSortChangePopup-title": "My Cards Sort", @@ -1028,19 +1028,19 @@ "operator-board-abbrev": "b", "operator-swimlane": "swimlane", "operator-swimlane-abbrev": "s", - "operator-list": "seznam", + "operator-list": "list", "operator-list-abbrev": "l", - "operator-label": "oznaka", + "operator-label": "label", "operator-label-abbrev": "#", "operator-user": "user", "operator-user-abbrev": "@", - "operator-member": "član", + "operator-member": "member", "operator-member-abbrev": "m", "operator-assignee": "assignee", "operator-assignee-abbrev": "a", "operator-creator": "creator", "operator-status": "status", - "operator-due": "rok", + "operator-due": "due", "operator-created": "created", "operator-modified": "modified", "operator-sort": "sort", @@ -1059,16 +1059,16 @@ "predicate-month": "month", "predicate-quarter": "quarter", "predicate-year": "year", - "predicate-due": "rok", + "predicate-due": "due", "predicate-modified": "modified", "predicate-created": "created", "predicate-attachment": "attachment", "predicate-description": "description", - "predicate-checklist": "kontrolni seznam", - "predicate-start": "začetek", - "predicate-end": "konec", + "predicate-checklist": "checklist", + "predicate-start": "start", + "predicate-end": "end", "predicate-assignee": "assignee", - "predicate-member": "član", + "predicate-member": "member", "predicate-public": "public", "predicate-private": "private", "predicate-selector": "selector", @@ -1119,7 +1119,7 @@ "globalSearch-instructions-notes-5": "By default archived cards are not searched.", "link-to-search": "Link to this search", "excel-font": "Arial", - "number": "Število", + "number": "Number", "label-colors": "Label Colors", "label-names": "Label Names", "archived-at": "archived at", @@ -1185,7 +1185,7 @@ "add-teams-label": "Added teams are displayed below:", "remove-team-from-table": "Are you sure you want to remove this team from the board ?", "confirm-btn": "Confirm", - "remove-btn": "Odstrani", + "remove-btn": "Remove", "filter-card-title-label": "Filter by card title", "invite-people-success": "Invitation to register sent with success", "invite-people-error": "Error while sending invitation to register", @@ -1242,7 +1242,7 @@ "storage": "Storage", "action": "Action", "board-title": "Board Title", - "attachmentRenamePopup-title": "Preimenuj", + "attachmentRenamePopup-title": "Rename", "uploading": "Uploading", "remaining_time": "Remaining time", "speed": "Speed", @@ -1253,7 +1253,7 @@ "forgot-password": "Forgot password", "minicardDetailsActionsPopup-title": "Card Details", "Mongo_sessions_count": "Mongo sessions count", - "change-visibility": "Spremeni vidnost", + "change-visibility": "Change Visibility", "max-upload-filesize": "Max upload filesize in bytes:", "allowed-upload-filetypes": "Allowed upload filetypes:", "max-avatar-filesize": "Max avatar filesize in bytes:", @@ -1267,19 +1267,19 @@ "editTranslationPopup-title": "Edit custom translation string", "settingsTranslationPopup-title": "Delete this custom translation string?", "translation": "Translation", - "text": "Besedilo", + "text": "Text", "translation-text": "Translation text", "show-subtasks-field": "Show subtasks field", "show-week-of-year": "Show week of year (ISO 8601)", "convert-to-markdown": "Convert to markdown", "import-board-zip": "Add .zip file that has board JSON files, and board name subdirectories with attachments", - "collapse": "Skrči", + "collapse": "Collapse", "uncollapse": "Uncollapse", "hideCheckedChecklistItems": "Hide checked checklist items", "hideAllChecklistItems": "Hide all checklist items", "support": "Support", "supportPopup-title": "Support", - "accessibility": "Dostopnost", + "accessibility": "Accessibility", "accessibility-page-enabled": "Accessibility page enabled", "accessibility-info-not-added-yet": "Accessibility info has not been added yet", "accessibility-title": "Accessibility title", @@ -1307,7 +1307,7 @@ "admin-people-filter-show": "Show:", "admin-people-filter-all": "All Users", "admin-people-filter-locked": "Locked Users Only", - "admin-people-filter-active": "Aktivno", + "admin-people-filter-active": "Active", "admin-people-filter-inactive": "Not Active", "admin-people-active-status": "Active Status", "admin-people-user-active": "User is active - click to deactivate", @@ -1396,7 +1396,7 @@ "card-show-lists-on-minicard": "Show Lists on Minicard", "cleanup": "Cleanup", "cleanup-old-jobs": "Cleanup Old Jobs", - "completed": "zaključen", + "completed": "Completed", "conversion-info-text": "This conversion is performed once per board and improves performance. You can continue using the board normally.", "converting-board": "Converting Board", "converting-board-description": "Converting board structure for improved functionality. This may take a few moments.", From b06daff4c7e63453643459f7d8798fde97e3200c Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 19 Oct 2025 10:57:36 +0300 Subject: [PATCH 002/183] Fix add and drag drop attachments to minicards and card. Thanks to xet7 ! Fixes #5946, fixes #5436, fixes #2936, fixes #1926, fixes #300, fixes #142 --- client/components/boards/boardBody.css | 7 +++ client/components/boards/boardBody.js | 25 +++++++++ client/components/cards/attachments.js | 2 +- client/components/cards/cardDetails.js | 77 +++++++++++++------------- client/components/cards/minicard.js | 77 +++++++++++++------------- client/components/lists/list.js | 9 +++ 6 files changed, 122 insertions(+), 75 deletions(-) diff --git a/client/components/boards/boardBody.css b/client/components/boards/boardBody.css index 32770eda6..a0ebce8f4 100644 --- a/client/components/boards/boardBody.css +++ b/client/components/boards/boardBody.css @@ -496,3 +496,10 @@ font-size: 25px; cursor: pointer; } + +/* Global file drag over state for board canvas */ +.board-canvas.file-drag-over { + background-color: rgba(0, 123, 255, 0.05) !important; + border: 2px dashed #007bff !important; + transition: all 0.2s ease; +} diff --git a/client/components/boards/boardBody.js b/client/components/boards/boardBody.js index c7d77eb93..cb13abebd 100644 --- a/client/components/boards/boardBody.js +++ b/client/components/boards/boardBody.js @@ -721,6 +721,31 @@ BlazeComponent.extendComponent({ } }, 'click .js-empty-board-add-swimlane': Popup.open('swimlaneAdd'), + // Global drag and drop file upload handlers for better visual feedback + 'dragover .board-canvas'(event) { + const dataTransfer = event.originalEvent.dataTransfer; + if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) { + event.preventDefault(); + // Add visual indicator that files can be dropped + $('.board-canvas').addClass('file-drag-over'); + } + }, + 'dragleave .board-canvas'(event) { + const dataTransfer = event.originalEvent.dataTransfer; + if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) { + // Only remove class if we're leaving the board canvas entirely + if (!event.currentTarget.contains(event.relatedTarget)) { + $('.board-canvas').removeClass('file-drag-over'); + } + } + }, + 'drop .board-canvas'(event) { + const dataTransfer = event.originalEvent.dataTransfer; + if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) { + event.preventDefault(); + $('.board-canvas').removeClass('file-drag-over'); + } + }, }, ]; }, diff --git a/client/components/cards/attachments.js b/client/components/cards/attachments.js index 18788f22d..a883877e1 100644 --- a/client/components/cards/attachments.js +++ b/client/components/cards/attachments.js @@ -343,7 +343,7 @@ export function handleFileUpload(card, files) { } // Check if user can modify the card - if (!card.canModifyCard()) { + if (!Utils.canModifyCard()) { if (process.env.DEBUG === 'true') { console.warn('User does not have permission to modify this card'); } diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js index da95765ba..df9e33a81 100644 --- a/client/components/cards/cardDetails.js +++ b/client/components/cards/cardDetails.js @@ -504,55 +504,58 @@ BlazeComponent.extendComponent({ }, // Drag and drop file upload handlers 'dragover .js-card-details'(event) { - event.preventDefault(); - event.stopPropagation(); + // Only prevent default for file drags to avoid interfering with other drag operations + const dataTransfer = event.originalEvent.dataTransfer; + if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) { + event.preventDefault(); + event.stopPropagation(); + } }, 'dragenter .js-card-details'(event) { - event.preventDefault(); - event.stopPropagation(); - const card = this.data(); - const board = card.board(); - // Only allow drag-and-drop if user can modify card and board allows attachments - if (card.canModifyCard() && board && board.allowsAttachments) { - // Check if the drag contains files - const dataTransfer = event.originalEvent.dataTransfer; - if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) { + const dataTransfer = event.originalEvent.dataTransfer; + if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) { + event.preventDefault(); + event.stopPropagation(); + const card = this.data(); + const board = card.board(); + // Only allow drag-and-drop if user can modify card and board allows attachments + if (Utils.canModifyCard() && board && board.allowsAttachments) { $(event.currentTarget).addClass('is-dragging-over'); } } }, 'dragleave .js-card-details'(event) { - event.preventDefault(); - event.stopPropagation(); - $(event.currentTarget).removeClass('is-dragging-over'); + const dataTransfer = event.originalEvent.dataTransfer; + if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) { + event.preventDefault(); + event.stopPropagation(); + $(event.currentTarget).removeClass('is-dragging-over'); + } }, 'drop .js-card-details'(event) { - event.preventDefault(); - event.stopPropagation(); - $(event.currentTarget).removeClass('is-dragging-over'); - - const card = this.data(); - const board = card.board(); - - // Check permissions - if (!card.canModifyCard() || !board || !board.allowsAttachments) { - return; - } - - // Check if this is a file drop (not a checklist item reorder) const dataTransfer = event.originalEvent.dataTransfer; - if (!dataTransfer || !dataTransfer.files || dataTransfer.files.length === 0) { - return; - } + if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) { + event.preventDefault(); + event.stopPropagation(); + $(event.currentTarget).removeClass('is-dragging-over'); - // Check if the drop contains files (not just text/HTML) - if (!dataTransfer.types.includes('Files')) { - return; - } + const card = this.data(); + const board = card.board(); - const files = dataTransfer.files; - if (files && files.length > 0) { - handleFileUpload(card, files); + // Check permissions + if (!Utils.canModifyCard() || !board || !board.allowsAttachments) { + return; + } + + // Check if this is a file drop (not a checklist item reorder) + if (!dataTransfer.files || dataTransfer.files.length === 0) { + return; + } + + const files = dataTransfer.files; + if (files && files.length > 0) { + handleFileUpload(card, files); + } } }, }, diff --git a/client/components/cards/minicard.js b/client/components/cards/minicard.js index 2cecccd7e..91ebddc8c 100644 --- a/client/components/cards/minicard.js +++ b/client/components/cards/minicard.js @@ -111,55 +111,58 @@ BlazeComponent.extendComponent({ 'click .js-open-minicard-details-menu': Popup.open('minicardDetailsActions'), // Drag and drop file upload handlers 'dragover .minicard'(event) { - event.preventDefault(); - event.stopPropagation(); + // Only prevent default for file drags to avoid interfering with sortable + const dataTransfer = event.originalEvent.dataTransfer; + if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) { + event.preventDefault(); + event.stopPropagation(); + } }, 'dragenter .minicard'(event) { - event.preventDefault(); - event.stopPropagation(); - const card = this.data(); - const board = card.board(); - // Only allow drag-and-drop if user can modify card and board allows attachments - if (card.canModifyCard() && board && board.allowsAttachments) { - // Check if the drag contains files - const dataTransfer = event.originalEvent.dataTransfer; - if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) { + const dataTransfer = event.originalEvent.dataTransfer; + if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) { + event.preventDefault(); + event.stopPropagation(); + const card = this.data(); + const board = card.board(); + // Only allow drag-and-drop if user can modify card and board allows attachments + if (Utils.canModifyCard() && board && board.allowsAttachments) { $(event.currentTarget).addClass('is-dragging-over'); } } }, 'dragleave .minicard'(event) { - event.preventDefault(); - event.stopPropagation(); - $(event.currentTarget).removeClass('is-dragging-over'); + const dataTransfer = event.originalEvent.dataTransfer; + if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) { + event.preventDefault(); + event.stopPropagation(); + $(event.currentTarget).removeClass('is-dragging-over'); + } }, 'drop .minicard'(event) { - event.preventDefault(); - event.stopPropagation(); - $(event.currentTarget).removeClass('is-dragging-over'); - - const card = this.data(); - const board = card.board(); - - // Check permissions - if (!card.canModifyCard() || !board || !board.allowsAttachments) { - return; - } - - // Check if this is a file drop (not a card reorder) const dataTransfer = event.originalEvent.dataTransfer; - if (!dataTransfer || !dataTransfer.files || dataTransfer.files.length === 0) { - return; - } + if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) { + event.preventDefault(); + event.stopPropagation(); + $(event.currentTarget).removeClass('is-dragging-over'); - // Check if the drop contains files (not just text/HTML) - if (!dataTransfer.types.includes('Files')) { - return; - } + const card = this.data(); + const board = card.board(); - const files = dataTransfer.files; - if (files && files.length > 0) { - handleFileUpload(card, files); + // Check permissions + if (!Utils.canModifyCard() || !board || !board.allowsAttachments) { + return; + } + + // Check if this is a file drop (not a card reorder) + if (!dataTransfer.files || dataTransfer.files.length === 0) { + return; + } + + const files = dataTransfer.files; + if (files && files.length > 0) { + handleFileUpload(card, files); + } } }, } diff --git a/client/components/lists/list.js b/client/components/lists/list.js index 6c3695ebf..da7c16e16 100644 --- a/client/components/lists/list.js +++ b/client/components/lists/list.js @@ -157,6 +157,15 @@ BlazeComponent.extendComponent({ } else { $cards.sortable({ handle: '.minicard', + // Prevent sortable from interfering with file drag and drop + start: function(event, ui) { + // Check if this is a file drag operation + const dataTransfer = event.originalEvent?.dataTransfer; + if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) { + // Cancel sortable for file drags + return false; + } + }, }); } From 1134b45b05e564c1ca70fc13a90785a5d882df94 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 19 Oct 2025 11:07:49 +0300 Subject: [PATCH 003/183] Updated ChangeLog. --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc452b057..a3eaa879f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,15 @@ Fixing other platforms In Progress. [Upgrade WeKan](https://wekan.fi/upgrade/) +# Upcoming WeKan ® release + +This release fixes the following bugs: + +- [Fix add and drag drop attachments to minicards and card](https://github.com/wekan/wekan/commit/b06daff4c7e63453643459f7d8798fde97e3200c (HEAD -> main) + Thanks to xet7. + +Thanks to above GitHub users for their contributions and translators for their translations. + # v8.05 2025-10-17 WeKan ® release This release fixes the following bugs: From ef828bdd38d7873d6e76a57f48e530731be74b77 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 19 Oct 2025 14:19:51 +0300 Subject: [PATCH 004/183] Updated translations. --- imports/i18n/data/sl.i18n.json | 1516 ++++++++++++++++---------------- 1 file changed, 758 insertions(+), 758 deletions(-) diff --git a/imports/i18n/data/sl.i18n.json b/imports/i18n/data/sl.i18n.json index 48daee11b..7bbd76862 100644 --- a/imports/i18n/data/sl.i18n.json +++ b/imports/i18n/data/sl.i18n.json @@ -1,89 +1,89 @@ { - "accept": "Accept", + "accept": "Sprejmi", "act-activity-notify": "Activity Notification", - "act-addAttachment": "added attachment __attachment__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-deleteAttachment": "deleted attachment __attachment__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-addSubtask": "added subtask __subtask__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-addLabel": "Added label __label__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-addedLabel": "Added label __label__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-removeLabel": "Removed label __label__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-removedLabel": "Removed label __label__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-addChecklist": "added checklist __checklist__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-addChecklistItem": "added checklist item __checklistItem__ to checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-removeChecklist": "removed checklist __checklist__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-removeChecklistItem": "removed checklist item __checklistItem__ from checklist __checkList__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-checkedItem": "checked __checklistItem__ of checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-uncheckedItem": "unchecked __checklistItem__ of checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-addAttachment": "dodal priponko __attachment__ h kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-deleteAttachment": "odstranil priponko __attachment__ iz kartice __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-addSubtask": "dodal podopravilo __subtask__ h kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-addLabel": "Dodal oznako __label__ h kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-addedLabel": "Dodal oznako __label__ h kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-removeLabel": "Odstranil oznako __label__ iz kartice __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-removedLabel": "Odstranil oznako __label__ iz kartice __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-addChecklist": "dodal kontrolni seznam __checklist__ h kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-addChecklistItem": "dodal postavko __checklistItem__ kontrolnega seznama __checklist__ na kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-removeChecklist": "odstranil kontrolni seznam __checklist__ iz kartice __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-removeChecklistItem": "odstranil postavko __checklistItem__ kontrolnega seznama __checklist__ na kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-checkedItem": "obkljukal postavko __checklistItem__ kontrolnega seznama __checklist__ na kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-uncheckedItem": "odkljukal postavko __checklistItem__ kontrolnega seznama __checklist__ na kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", "act-completeChecklist": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-uncompleteChecklist": "uncompleted checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-addComment": "commented on card __card__: __comment__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-editComment": "edited comment on card __card__: __comment__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-deleteComment": "deleted comment on card __card__: __comment__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-createBoard": "created board __board__", - "act-createSwimlane": "created swimlane __swimlane__ to board __board__", - "act-createCard": "created card __card__ to list __list__ at swimlane __swimlane__ at board __board__", - "act-createCustomField": "created custom field __customField__ at board __board__", - "act-deleteCustomField": "deleted custom field __customField__ at board __board__", - "act-setCustomField": "edited custom field __customField__: __customFieldValue__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-createList": "added list __list__ to board __board__", - "act-addBoardMember": "added member __member__ to board __board__", - "act-archivedBoard": "Board __board__ moved to Archive", - "act-archivedCard": "Card __card__ at list __list__ at swimlane __swimlane__ at board __board__ moved to Archive", - "act-archivedList": "List __list__ at swimlane __swimlane__ at board __board__ moved to Archive", - "act-archivedSwimlane": "Swimlane __swimlane__ at board __board__ moved to Archive", - "act-importBoard": "imported board __board__", - "act-importCard": "imported card __card__ to list __list__ at swimlane __swimlane__ at board __board__", - "act-importList": "imported list __list__ to swimlane __swimlane__ at board __board__", - "act-joinMember": "added member __member__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__", - "act-moveCard": "moved card __card__ at board __board__ from list __oldList__ at swimlane __oldSwimlane__ to list __list__ at swimlane __swimlane__", - "act-moveCardToOtherBoard": "moved card __card__ from list __oldList__ at swimlane __oldSwimlane__ at board __oldBoard__ to list __list__ at swimlane __swimlane__ at board __board__", - "act-removeBoardMember": "removed member __member__ from board __board__", - "act-restoredCard": "restored card __card__ to list __list__ at swimlane __swimlane__ at board __board__", - "act-unjoinMember": "removed member __member__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__", + "act-uncompleteChecklist": "nedokončan kontrolni seznam __checklist__ na kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-addComment": "komentiral na kartici __card__: __comment__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-editComment": "uredil komentar na kartici __card__: __comment__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-deleteComment": "izbrisal komentar na kartici __card__: __comment__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-createBoard": "ustvaril tablo __board__", + "act-createSwimlane": "ustvaril plavalno stezo __swimlane__ na tabli __board__", + "act-createCard": "ustvaril kartico __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-createCustomField": "ustvaril poljubno polje __customField__ na tabli __board__", + "act-deleteCustomField": "izbrisal poljubno polje __customField__ na tabli __board__", + "act-setCustomField": "uredil poljubno polje __customField__: __customFieldValue__ na kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-createList": "dodal seznam __list__ na tablo __board__", + "act-addBoardMember": "dodal člana __member__ k tabli __board__", + "act-archivedBoard": "Tabla __board__ premaknjena v arhiv", + "act-archivedCard": "Kartica __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__ premaknjena v arhiv", + "act-archivedList": "Seznam __list__ na plavalni stezi __swimlane__ na tabli __board__ premaknjen v arhiv", + "act-archivedSwimlane": "Plavalna steza __swimlane__ na tabli __board__ premaknjena v arhiv", + "act-importBoard": "uvozil tablo __board__", + "act-importCard": "uvozil kartico __card__ na seznam __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-importList": "uvozil seznam __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-joinMember": "dodal član __member__ h kartici __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-moveCard": "premakil kartico __card__ na tabli __board__ iz seznama __oldList__ na plavalni stezi __oldSwimlane__ na seznam __list__ na plavalni stezi __swimlane__", + "act-moveCardToOtherBoard": "premaknil kartico __card__ iz seznama __oldList__ na plavalni stezi __oldSwimlane__ na tabli __oldBoard__ na seznam __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-removeBoardMember": "odstranil člana __member__ iz table __board__", + "act-restoredCard": "obnovil kartico __card__ na seznam __list__ na plavalni stezi __swimlane__ na tabli __board__", + "act-unjoinMember": "odstranil člana __member__ iz kartice __card__ na seznamu __list__ na plavalni stezi __swimlane__ na tabli __board__", "act-withBoardTitle": "__board__", "act-withCardTitle": "[__board__] __card__", - "actions": "Actions", - "activities": "Activities", - "activity": "Activity", - "activity-added": "added %s to %s", - "activity-archived": "%s moved to Archive", - "activity-attached": "attached %s to %s", - "activity-created": "created %s", + "actions": "Dejanja", + "activities": "Aktivnosti", + "activity": "Aktivnost", + "activity-added": "dodal %s v %s", + "activity-archived": "%s premaknjeno v arhiv", + "activity-attached": "pripel %s v %s", + "activity-created": "ustvaril %s", "activity-changedListTitle": "renamed list to %s", - "activity-customfield-created": "created custom field %s", - "activity-excluded": "excluded %s from %s", - "activity-imported": "imported %s into %s from %s", - "activity-imported-board": "imported %s from %s", - "activity-joined": "joined %s", - "activity-moved": "moved %s from %s to %s", - "activity-on": "on %s", - "activity-removed": "removed %s from %s", - "activity-sent": "sent %s to %s", - "activity-unjoined": "unjoined %s", - "activity-subtask-added": "added subtask to %s", - "activity-checked-item": "checked %s in checklist %s of %s", - "activity-unchecked-item": "unchecked %s in checklist %s of %s", - "activity-checklist-added": "added checklist to %s", - "activity-checklist-removed": "removed a checklist from %s", - "activity-checklist-completed": "completed checklist %s of %s", - "activity-checklist-uncompleted": "uncompleted the checklist %s of %s", - "activity-checklist-item-added": "added checklist item to '%s' in %s", - "activity-checklist-item-removed": "removed a checklist item from '%s' in %s", - "add": "Add", - "activity-checked-item-card": "checked %s in checklist %s", - "activity-unchecked-item-card": "unchecked %s in checklist %s", + "activity-customfield-created": "ustvaril poljubno polje%s", + "activity-excluded": "izključil %s iz %s", + "activity-imported": "uvozil %s v %s iz %s", + "activity-imported-board": "uvozil %s iz %s", + "activity-joined": "se je pridružil na %s", + "activity-moved": "premakil %s iz %s na %s", + "activity-on": "na %s", + "activity-removed": "odstranil %s iz %s", + "activity-sent": "poslano %s na %s", + "activity-unjoined": "se je odjavil iz %s", + "activity-subtask-added": "dodal podopravilo k %s", + "activity-checked-item": "obkljukal %s na kontrolnem seznamu %s od %s", + "activity-unchecked-item": "odkljukal %s na kontrolnem seznamu %s od %s", + "activity-checklist-added": "dodal kontrolni seznam na %s", + "activity-checklist-removed": "odstranil kontrolni seznam iz %s", + "activity-checklist-completed": "dokončan kontrolni seznam %s od %s", + "activity-checklist-uncompleted": "nedokončal kontrolni seznam %s od %s", + "activity-checklist-item-added": "dodal postavko kontrolnega seznama na '%s' v %s", + "activity-checklist-item-removed": "odstranil postavko kontrolnega seznama iz '%s' v %s", + "add": "Dodaj", + "activity-checked-item-card": "obkljukal %s na kontrolnem seznamu %s", + "activity-unchecked-item-card": "odkljukal %s na kontrolnem seznamu %s", "activity-checklist-completed-card": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__", - "activity-checklist-uncompleted-card": "uncompleted the checklist %s", - "activity-editComment": "edited comment %s", - "activity-deleteComment": "deleted comment %s", + "activity-checklist-uncompleted-card": "nedokončal kontrolni seznam %s", + "activity-editComment": "uredil komentar %s", + "activity-deleteComment": "izbrisal komentar %s", "activity-receivedDate": "edited received date to %s of %s", "activity-startDate": "edited start date to %s of %s", "activity-dueDate": "edited due date to %s of %s", "activity-endDate": "edited end date to %s of %s", - "add-attachment": "Add Attachment", - "add-board": "Add Board", + "add-attachment": "Dodaj priponko", + "add-board": "Dodaj tablo", "add-template": "Add Template", - "add-card": "Add Card", + "add-card": "Dodaj kartico", "add-card-to-top-of-list": "Add Card to Top of List", "add-card-to-bottom-of-list": "Add Card to Bottom of List", "setListWidthPopup-title": "Set Widths", @@ -96,60 +96,60 @@ "set-swimlane-height": "Set Swimlane Height", "set-swimlane-height-value": "Swimlane Height (pixels)", "swimlane-height-error-message": "Swimlane height must be a positive integer", - "add-swimlane": "Add Swimlane", - "add-subtask": "Add Subtask", - "add-checklist": "Add Checklist", - "add-checklist-item": "Add an item to checklist", + "add-swimlane": "Dodaj plavalno stezo", + "add-subtask": "Dodaj podopravilo", + "add-checklist": "Dodaj kontrolni seznam", + "add-checklist-item": "Dodaj postavko na kontrolni seznam", "close-add-checklist-item": "Close add an item to checklist form", "close-edit-checklist-item": "Close edit an item to checklist form", "convertChecklistItemToCardPopup-title": "Convert to Card", "add-cover": "Add cover image to minicard", - "add-label": "Add Label", - "add-list": "Add List", + "add-label": "Dodaj oznako", + "add-list": "Dodaj seznam", "add-after-list": "Add After List", - "add-members": "Add Members", - "added": "Added", - "addMemberPopup-title": "Members", - "memberPopup-title": "Member Settings", - "admin": "Admin", - "admin-desc": "Can view and edit cards, remove members, and change settings for the board.", - "admin-announcement": "Announcement", - "admin-announcement-active": "Active System-Wide Announcement", - "admin-announcement-title": "Announcement from Administrator", - "all-boards": "All Boards", - "and-n-other-card": "And __count__ other card", - "and-n-other-card_plural": "And __count__ other cards", - "apply": "Apply", - "app-is-offline": "Loading, please wait. Refreshing the page will cause data loss. If loading does not work, please check that server has not stopped.", + "add-members": "Dodaj člane", + "added": "Dodano", + "addMemberPopup-title": "Člani", + "memberPopup-title": "Nastavitve članov", + "admin": "Administrator", + "admin-desc": "Lahko gleda in ureja kartice, odstrani člane ter spreminja nastavitve table.", + "admin-announcement": "Najava", + "admin-announcement-active": "Aktivna vse-sistemska najava", + "admin-announcement-title": "Najava od administratorja", + "all-boards": "Vse table", + "and-n-other-card": "In __count__ druga kartica", + "and-n-other-card_plural": "In __count__ drugih kartic", + "apply": "Uporabi", + "app-is-offline": "Nalaganje, prosimo počakajte. Osveževanje strani bo povzročilo izgubo podatkov. Če nalaganje ne deluje, preverite, ali se strežnik ni ustavil.", "app-try-reconnect": "Try to reconnect.", - "archive": "Move to Archive", - "archive-all": "Move All to Archive", - "archive-board": "Move Board to Archive", + "archive": "premaknjena v arhiv", + "archive-all": "Premakni vse v arhiv", + "archive-board": "Arhiviraj tablo", "archive-board-confirm": "Are you sure you want to archive this board?", - "archive-card": "Move Card to Archive", - "archive-list": "Move List to Archive", - "archive-swimlane": "Move Swimlane to Archive", - "archive-selection": "Move selection to Archive", - "archiveBoardPopup-title": "Move Board to Archive?", - "archived-items": "Archive", - "archived-boards": "Boards in Archive", - "restore-board": "Restore Board", - "no-archived-boards": "No Boards in Archive.", - "archives": "Archive", - "template": "Template", - "templates": "Templates", + "archive-card": "Arhiviraj kartico", + "archive-list": "Arhiviraj seznam", + "archive-swimlane": "Arhiviraj plavalno stezo", + "archive-selection": "Arhiviraj označeno", + "archiveBoardPopup-title": "Arhiviraj tablo?", + "archived-items": "Arhiv", + "archived-boards": "Table v arhivu", + "restore-board": "Obnovi tablo", + "no-archived-boards": "Nobene table ni v arhivu.", + "archives": "Arhiv", + "template": "Predloga", + "templates": "Predloge", "template-container": "Template Container", "add-template-container": "Add Template Container", - "assign-member": "Assign member", - "attached": "attached", - "attachment": "Attachment", - "attachment-delete-pop": "Deleting an attachment is permanent. There is no undo.", - "attachmentDeletePopup-title": "Delete Attachment?", - "attachments": "Attachments", - "auto-watch": "Automatically watch boards when they are created", + "assign-member": "Dodeli člana", + "attached": "pripeto", + "attachment": "Priponka", + "attachment-delete-pop": "Brisanje priponke je trajno. Ne obstaja razveljavitev.", + "attachmentDeletePopup-title": "Briši priponko?", + "attachments": "Priponke", + "auto-watch": "Samodejno spremljaj ustvarjene table", "avatar-too-big": "The avatar is too large (__size__ max)", - "back": "Back", - "board-change-color": "Change color", + "back": "Nazaj", + "board-change-color": "Spremeni barvo", "board-change-background-image": "Change Background Image", "board-background-image-url": "Background Image URL", "add-background-image": "Add Background Image", @@ -160,23 +160,23 @@ "boardInfoOnMyBoards-title": "All Boards Settings", "show-card-counter-per-list": "Show card count per list", "show-board_members-avatar": "Show Board members avatars", - "board-nb-stars": "%s stars", - "board-not-found": "Board not found", - "board-private-info": "This board will be private.", - "board-public-info": "This board will be public.", + "board-nb-stars": "%s zvezdic", + "board-not-found": "Tabla ni najdena", + "board-private-info": "Ta tabla bo privatna.", + "board-public-info": "Ta tabla bo javna.", "board-drag-drop-reorder-or-click-open": "Drag and drop to reorder board icons. Click board icon to open board.", - "boardChangeColorPopup-title": "Change Board Background", + "boardChangeColorPopup-title": "Spremeni ozadje table", "boardChangeBackgroundImagePopup-title": "Change Background Image", - "allBoardsChangeColorPopup-title": "Change color", + "allBoardsChangeColorPopup-title": "Spremeni barvo", "allBoardsChangeBackgroundImagePopup-title": "Change Background Image", - "boardChangeTitlePopup-title": "Rename Board", - "boardChangeVisibilityPopup-title": "Change Visibility", - "boardChangeWatchPopup-title": "Change Watch", - "boardMenuPopup-title": "Board Settings", - "allBoardsMenuPopup-title": "Settings", - "boardChangeViewPopup-title": "Board View", - "boards": "Boards", - "board-view": "Board View", + "boardChangeTitlePopup-title": "Preimenuj tablo", + "boardChangeVisibilityPopup-title": "Spremeni vidnost", + "boardChangeWatchPopup-title": "Spremeni opazovanje", + "boardMenuPopup-title": "Nastavitve table", + "allBoardsMenuPopup-title": "Nastavitve", + "boardChangeViewPopup-title": "Pogled table", + "boards": "Table", + "board-view": "Pogled table", "desktop-mode": "Desktop Mode", "mobile-mode": "Mobile Mode", "mobile-desktop-toggle": "Toggle between Mobile and Desktop Mode", @@ -185,35 +185,35 @@ "click-to-change-zoom": "Click to change zoom level", "zoom-level": "Zoom Level", "enter-zoom-level": "Enter zoom level (50-300%):", - "board-view-cal": "Calendar", - "board-view-swimlanes": "Swimlanes", - "board-view-collapse": "Collapse", + "board-view-cal": "Koledar", + "board-view-swimlanes": "Plavalne steze", + "board-view-collapse": "Skrči", "board-view-gantt": "Gantt", - "board-view-lists": "Lists", - "bucket-example": "Like “Bucket List” for example", - "cancel": "Cancel", - "card-archived": "This card is moved to Archive.", - "board-archived": "This board is moved to Archive.", - "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.", - "card-delete-suggest-archive": "You can move a card to Archive to remove it from the board and preserve the activity.", + "board-view-lists": "Seznami", + "bucket-example": "Kot na primer \"Življenjski seznam\"", + "cancel": "Prekliči", + "card-archived": "Kartica je premaknjena v arhiv.", + "board-archived": "Tabla je premaknjena v arhiv.", + "card-comments-title": "Ta kartica ima %s komentar.", + "card-delete-notice": "Brisanje je trajno. Izgubili boste vsa dejanja, povezana s kartico.", + "card-delete-pop": "Vsa dejanja bodo odstranjena iz zgodovine dejavnosti. Kartice ne boste mogli znova odpreti. Razveljavitve ni.", + "card-delete-suggest-archive": "Kartico lahko premaknete v arhiv, da jo odstranite s table in ohranite dejavnost.", "card-archive-pop": "Card will not be visible at this list after archiving card.", "card-archive-suggest-cancel": "You can later restore card from Archive.", "card-due": "Due", - "card-due-on": "Due on", - "card-spent": "Spent Time", - "card-edit-attachments": "Edit attachments", - "card-edit-custom-fields": "Edit custom fields", - "card-edit-labels": "Edit labels", - "card-edit-members": "Edit members", - "card-labels-title": "Change the labels for the card.", - "card-members-title": "Add or remove members of the board from the card.", - "card-start": "Start", - "card-start-on": "Starts on", - "cardAttachmentsPopup-title": "Attach From", - "cardCustomField-datePopup-title": "Change date", - "cardCustomFieldsPopup-title": "Edit custom fields", + "card-due-on": "Rok", + "card-spent": "Porabljen čas", + "card-edit-attachments": "Uredi priponke", + "card-edit-custom-fields": "Uredi poljubna polja", + "card-edit-labels": "Uredi oznake", + "card-edit-members": "Uredi člane", + "card-labels-title": "Spremeni oznake za kartico.", + "card-members-title": "Dodaj ali odstrani člane table iz kartice.", + "card-start": "Začetek", + "card-start-on": "Začne ob", + "cardAttachmentsPopup-title": "Pripni od", + "cardCustomField-datePopup-title": "Spremeni datum", + "cardCustomFieldsPopup-title": "Uredi poljubna polja", "cardStartVotingPopup-title": "Start a vote", "positiveVoteMembersPopup-title": "Proponents", "negativeVoteMembersPopup-title": "Opponents", @@ -247,168 +247,168 @@ "set-estimation": "Set Estimation", "deletePokerPopup-title": "Delete planning poker?", "poker-delete-pop": "Deleting is permanent. You will lose all actions associated with this planning poker.", - "cardDeletePopup-title": "Delete Card?", + "cardDeletePopup-title": "Briši kartico?", "cardArchivePopup-title": "Archive Card?", - "cardDetailsActionsPopup-title": "Card Actions", - "cardLabelsPopup-title": "Labels", - "cardMembersPopup-title": "Members", - "cardMorePopup-title": "More", - "cardTemplatePopup-title": "Create template", - "cards": "Cards", - "cards-count": "Cards", - "cards-count-one": "Card", - "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", - "change-permissions": "Change permissions", - "change-settings": "Change Settings", - "changeAvatarPopup-title": "Change Avatar", - "changeLanguagePopup-title": "Change Language", - "changePasswordPopup-title": "Change Password", - "changePermissionsPopup-title": "Change Permissions", - "changeSettingsPopup-title": "Change Settings", - "subtasks": "Subtasks", - "checklists": "Checklists", - "click-to-star": "Click to star this board.", - "click-to-unstar": "Click to unstar this board.", + "cardDetailsActionsPopup-title": "Dejanja kartice", + "cardLabelsPopup-title": "Oznake", + "cardMembersPopup-title": "Člani", + "cardMorePopup-title": "Več", + "cardTemplatePopup-title": "Ustvari predlogo", + "cards": "Kartic", + "cards-count": "Kartic", + "cards-count-one": "Kartica", + "casSignIn": "Vpiši se s CAS", + "cardType-card": "Kartica", + "cardType-linkedCard": "Povezana kartica", + "cardType-linkedBoard": "Povezana tabla", + "change": "Spremeni", + "change-avatar": "Spremeni avatar", + "change-password": "Spremeni geslo", + "change-permissions": "Spremeni dovoljenja", + "change-settings": "Spremeni nastavitve", + "changeAvatarPopup-title": "Spremeni avatar", + "changeLanguagePopup-title": "Spremeni jezik", + "changePasswordPopup-title": "Spremeni geslo", + "changePermissionsPopup-title": "Spremeni dovoljenja", + "changeSettingsPopup-title": "Spremeni nastavitve", + "subtasks": "Podopravila", + "checklists": "Kontrolni seznami", + "click-to-star": "Kliknite, da označite tablo z zvezdico.", + "click-to-unstar": "Kliknite, da odznačite tablo z zvezdico.", "click-to-enable-auto-width": "Auto list width disabled. Click to enable.", "click-to-disable-auto-width": "Auto list width enabled. Click to disable.", "auto-list-width": "Auto list width", - "clipboard": "Clipboard or drag & drop", - "close": "Close", - "close-board": "Close Board", - "close-board-pop": "You will be able to restore the board by clicking the “Archive” button from the home header.", + "clipboard": "Odložišče ali povleci & spusti", + "close": "Zapri", + "close-board": "Zapri tablo", + "close-board-pop": "Tablo boste lahko obnovili s klikom na gumb »Arhiviraj« na vstopni strani.", "close-card": "Close Card", - "color-black": "black", - "color-blue": "blue", - "color-crimson": "crimson", - "color-darkgreen": "darkgreen", - "color-gold": "gold", - "color-gray": "gray", - "color-green": "green", + "color-black": "črna", + "color-blue": "modra", + "color-crimson": "temno rdeča", + "color-darkgreen": "temno zelena", + "color-gold": "zlata", + "color-gray": "siva", + "color-green": "zelena", "color-indigo": "indigo", - "color-lime": "lime", + "color-lime": "limeta", "color-magenta": "magenta", - "color-mistyrose": "mistyrose", - "color-navy": "navy", - "color-orange": "orange", - "color-paleturquoise": "paleturquoise", - "color-peachpuff": "peachpuff", - "color-pink": "pink", - "color-plum": "plum", - "color-purple": "purple", - "color-red": "red", - "color-saddlebrown": "saddlebrown", - "color-silver": "silver", - "color-sky": "sky", - "color-slateblue": "slateblue", - "color-white": "white", - "color-yellow": "yellow", - "unset-color": "Unset", + "color-mistyrose": "rožnata", + "color-navy": "navy modra", + "color-orange": "oranžna", + "color-paleturquoise": "bledo turkizna", + "color-peachpuff": "breskvasta", + "color-pink": "roza", + "color-plum": "slivova", + "color-purple": "vijolična", + "color-red": "rdeča", + "color-saddlebrown": "rjava", + "color-silver": "srebrna", + "color-sky": "nebesna", + "color-slateblue": "skrilasto modra", + "color-white": "bela", + "color-yellow": "rumena", + "unset-color": "Onemogoči", "comments": "Comments", - "comment": "Comment", - "comment-placeholder": "Write Comment", - "comment-only": "Comment only", - "comment-only-desc": "Can comment on cards only.", + "comment": "Komentiraj", + "comment-placeholder": "Napiši komentar", + "comment-only": "Samo komentar", + "comment-only-desc": "Lahko komentirate samo na karticah.", "comment-delete": "Are you sure you want to delete the comment?", "deleteCommentPopup-title": "Delete comment?", - "no-comments": "No comments", - "no-comments-desc": "Can not see comments and activities.", - "worker": "Worker", - "worker-desc": "Can only move cards, assign itself to card and comment.", - "computer": "Computer", - "confirm-subtask-delete-popup": "Are you sure you want to delete subtask?", + "no-comments": "Ni komentarjev", + "no-comments-desc": "Ne morete videti komentarjev in dejavnosti.", + "worker": "Delavec", + "worker-desc": "Lahko samo premikam kartice, se dodelim na kartico in komentiram.", + "computer": "Računalnik", + "confirm-subtask-delete-popup": "Ste prepričani, da želite izbrisati podopravilo?", "confirm-checklist-delete-popup": "Are you sure you want to delete the checklist?", "subtaskDeletePopup-title": "Delete Subtask?", "checklistDeletePopup-title": "Delete Checklist?", - "copy-card-link-to-clipboard": "Copy card link to clipboard", + "copy-card-link-to-clipboard": "Kopiraj povezavo kartice na odložišče", "copy-text-to-clipboard": "Copy text to clipboard", - "linkCardPopup-title": "Link Card", - "searchElementPopup-title": "Search", - "copyCardPopup-title": "Copy Card", + "linkCardPopup-title": "Poveži kartico", + "searchElementPopup-title": "Išči", + "copyCardPopup-title": "Kopiraj kartico", "copyManyCardsPopup-title": "Copy Template to Many Cards", - "copyManyCardsPopup-instructions": "Destination Card Titles and Descriptions in this JSON format", - "copyManyCardsPopup-format": "[ {\"title\": \"First card title\", \"description\":\"First card description\"}, {\"title\":\"Second card title\",\"description\":\"Second card description\"},{\"title\":\"Last card title\",\"description\":\"Last card description\"} ]", - "create": "Create", - "createBoardPopup-title": "Create Board", - "chooseBoardSourcePopup-title": "Import board", - "createLabelPopup-title": "Create Label", - "createCustomField": "Create Field", - "createCustomFieldPopup-title": "Create Field", - "current": "current", - "custom-field-delete-pop": "There is no undo. This will remove this custom field from all cards and destroy its history.", - "custom-field-checkbox": "Checkbox", + "copyManyCardsPopup-instructions": "Naslovi ciljnih kartic in opisi v JSON formatu", + "copyManyCardsPopup-format": "[ {\"naslov\": \"Naslov prve kartice\", \"opis\":\"Opis prve kartice\"}, {\"naslov\":\"Opis druge kartice\",\"opis\":\"Opis druge kartice\"},{\"naslov\":\"Naslov zadnje kartice\",\"opis\":\"Opis zadnje kartice\"} ]", + "create": "Ustvari", + "createBoardPopup-title": "Ustvari tablo", + "chooseBoardSourcePopup-title": "Uvozi tablo", + "createLabelPopup-title": "Ustvari oznako", + "createCustomField": "Ustvari polje", + "createCustomFieldPopup-title": "Ustvari polje", + "current": "trenutno", + "custom-field-delete-pop": "Razveljavitve ni. To bo odstranilo to poljubno polje iz vseh kartic in izbrisalo njegovo zgodovino.", + "custom-field-checkbox": "Potrditveno polje", "custom-field-currency": "Currency", "custom-field-currency-option": "Currency Code", - "custom-field-date": "Date", - "custom-field-dropdown": "Dropdown List", - "custom-field-dropdown-none": "(none)", - "custom-field-dropdown-options": "List Options", - "custom-field-dropdown-options-placeholder": "Press enter to add more options", - "custom-field-dropdown-unknown": "(unknown)", - "custom-field-number": "Number", - "custom-field-text": "Text", - "custom-fields": "Custom Fields", - "date": "Date", - "decline": "Decline", - "default-avatar": "Default avatar", - "delete": "Delete", - "deleteCustomFieldPopup-title": "Delete Custom Field?", - "deleteLabelPopup-title": "Delete Label?", - "description": "Description", - "disambiguateMultiLabelPopup-title": "Disambiguate Label Action", - "disambiguateMultiMemberPopup-title": "Disambiguate Member Action", - "discard": "Discard", - "done": "Done", - "download": "Download", - "edit": "Edit", - "edit-avatar": "Change Avatar", - "edit-profile": "Edit Profile", - "edit-wip-limit": "Edit WIP Limit", - "soft-wip-limit": "Soft WIP Limit", - "editCardStartDatePopup-title": "Change start date", - "editCardDueDatePopup-title": "Change due date", - "editCustomFieldPopup-title": "Edit Field", + "custom-field-date": "Datum", + "custom-field-dropdown": "Spustni seznam", + "custom-field-dropdown-none": "(nobeno)", + "custom-field-dropdown-options": "Možnosti seznama", + "custom-field-dropdown-options-placeholder": "Pritisnite enter da dodate več možnosti", + "custom-field-dropdown-unknown": "(neznano)", + "custom-field-number": "Število", + "custom-field-text": "Besedilo", + "custom-fields": "Poljubna polja", + "date": "Datum", + "decline": "Zavrni", + "default-avatar": "Privzeti avatar", + "delete": "Briši", + "deleteCustomFieldPopup-title": "Briši poljubno polje?", + "deleteLabelPopup-title": "Briši oznako?", + "description": "Opis", + "disambiguateMultiLabelPopup-title": "Razdvoji Dejanje Oznake", + "disambiguateMultiMemberPopup-title": "Razdvoji dejanje člana", + "discard": "Razveljavi", + "done": "Končano", + "download": "Prenos", + "edit": "Uredi", + "edit-avatar": "Spremeni avatar", + "edit-profile": "Uredi profil", + "edit-wip-limit": "Uredi omejitev št. kartic", + "soft-wip-limit": "Omehčaj omejitev št. kartic", + "editCardStartDatePopup-title": "Spremeni začetni datum", + "editCardDueDatePopup-title": "Spremeni datum zapadlosti", + "editCustomFieldPopup-title": "Uredi polje", "addReactionPopup-title": "Add reaction", - "editCardSpentTimePopup-title": "Change spent time", - "editLabelPopup-title": "Change Label", - "editNotificationPopup-title": "Edit Notification", - "editProfilePopup-title": "Edit Profile", - "email": "Email", - "email-enrollAccount-subject": "An account created for you on __siteName__", - "email-enrollAccount-text": "Hello __user__,\n\nTo start using the service, simply click the link below.\n\n__url__\n\nThanks.", - "email-fail": "Sending email failed", - "email-fail-text": "Error trying to send email", - "email-invalid": "Invalid email", - "email-invite": "Invite via Email", - "email-invite-subject": "__inviter__ sent you an invitation", - "email-invite-text": "Dear __user__,\n\n__inviter__ invites you to join board \"__board__\" for collaborations.\n\nPlease follow the link below:\n\n__url__\n\nThanks.", - "email-resetPassword-subject": "Reset your password on __siteName__", - "email-resetPassword-text": "Hello __user__,\n\nTo reset your password, simply click the link below.\n\n__url__\n\nThanks.", - "email-sent": "Email sent", - "email-verifyEmail-subject": "Verify your email address on __siteName__", - "email-verifyEmail-text": "Hello __user__,\n\nTo verify your account email, simply click the link below.\n\n__url__\n\nThanks.", + "editCardSpentTimePopup-title": "Spremeni porabljen čas", + "editLabelPopup-title": "Spremeni oznako", + "editNotificationPopup-title": "Uredi obvestilo", + "editProfilePopup-title": "Uredi profil", + "email": "E-pošta", + "email-enrollAccount-subject": "Up. račun ustvarjen za vas na __siteName__", + "email-enrollAccount-text": "Pozdravljeni __user__,\n\nZa začetek uporabe kliknite spodnjo povezavo.\n\n__url__\n\nHvala.", + "email-fail": "Pošiljanje e-pošte ni uspelo", + "email-fail-text": "Napaka pri poskusu pošiljanja e-pošte", + "email-invalid": "Neveljavna e-pošta", + "email-invite": "Povabi z uporabo e-pošte", + "email-invite-subject": "__inviter__ vam je poslal povabilo", + "email-invite-text": "Spoštovani __user__,\n\n__inviter__ vas vabi k sodelovanju na tabli \"__board__\".\n\nProsimo sledite spodnji povezavi:\n\n__url__\n\nHvala.", + "email-resetPassword-subject": "Ponastavite geslo na __siteName__", + "email-resetPassword-text": "Pozdravljeni __user__,\n\nZa ponastavitev gesla kliknite na spodnjo povezavo.\n\n__url__\n\nHvala.", + "email-sent": "E-pošta poslana", + "email-verifyEmail-subject": "Preverite svoje e-poštni naslov na __siteName__", + "email-verifyEmail-text": "Pozdravljeni __user__,\n\nDa preverite e-poštni naslov za vaš uporabniški račun, kliknite na spodnjo povezavo.\n\n__url__\n\nHvala.", "enable-vertical-scrollbars": "Enable vertical scrollbars", - "enable-wip-limit": "Enable WIP Limit", - "error-board-doesNotExist": "This board does not exist", - "error-board-notAdmin": "You need to be admin of this board to do that", - "error-board-notAMember": "You need to be a member of this board to do that", - "error-json-malformed": "Your text is not valid JSON", - "error-json-schema": "Your JSON data does not include the proper information in the correct format", + "enable-wip-limit": "Vklopi omejitev št. kartic", + "error-board-doesNotExist": "Ta tabla ne obstaja", + "error-board-notAdmin": "Nimate administrativnih pravic za tablo.", + "error-board-notAMember": "Niste član table.", + "error-json-malformed": "Vaše besedilo ni veljaven JSON", + "error-json-schema": "Vaši JSON podatki ne vsebujejo pravilnih informacij v ustreznem formatu", "error-csv-schema": "Your CSV(Comma Separated Values)/TSV (Tab Separated Values) does not include the proper information in the correct format ", - "error-list-doesNotExist": "This list does not exist", - "error-user-doesNotExist": "This user does not exist", - "error-user-notAllowSelf": "You can not invite yourself", - "error-user-notCreated": "This user is not created", - "error-username-taken": "This username is already taken", + "error-list-doesNotExist": "Seznam ne obstaja", + "error-user-doesNotExist": "Uporabnik ne obstaja", + "error-user-notAllowSelf": "Ne morete povabiti sebe", + "error-user-notCreated": "Ta uporabnik ni ustvarjen", + "error-username-taken": "To up. ime že obstaja", "error-orgname-taken": "This organization name is already taken", "error-teamname-taken": "This team name is already taken", - "error-email-taken": "Email has already been taken", - "export-board": "Export board", + "error-email-taken": "E-poštni naslov je že zaseden", + "export-board": "Izvozi tablo", "export-board-json": "Export board to JSON", "export-board-csv": "Export board to CSV", "export-board-tsv": "Export board to TSV", @@ -418,21 +418,21 @@ "export-card": "Export card", "export-card-pdf": "Export card to PDF", "user-can-not-export-card-to-pdf": "User can not export card to PDF", - "exportBoardPopup-title": "Export board", + "exportBoardPopup-title": "Izvozi tablo", "exportCardPopup-title": "Export card", - "sort": "Sort", + "sort": "Sortiraj", "sorted": "Sorted", "remove-sort": "Remove sort", - "sort-desc": "Click to Sort List", - "list-sort-by": "Sort the List By:", - "list-label-modifiedAt": "Last Access Time", - "list-label-title": "Name of the List", - "list-label-sort": "Your Manual Order", - "list-label-short-modifiedAt": "(L)", - "list-label-short-title": "(N)", - "list-label-short-sort": "(M)", - "filter": "Filter", - "filter-cards": "Filter Cards or Lists", + "sort-desc": "Klikni za sortiranje seznama", + "list-sort-by": "Sortiraj po:", + "list-label-modifiedAt": "Nazadnje dostopano", + "list-label-title": "Ime seznama", + "list-label-sort": "Ročno nastavljen vrstni red", + "list-label-short-modifiedAt": "(N)", + "list-label-short-title": "(I)", + "list-label-short-sort": "(R)", + "filter": "Filtriraj", + "filter-cards": "Filtriraj kartice ali sezname", "filter-dates-label": "Filter by date", "filter-no-due-date": "No due date", "filter-overdue": "Overdue", @@ -440,197 +440,197 @@ "filter-due-this-week": "Due this week", "filter-due-next-week": "Due next week", "filter-due-tomorrow": "Due tomorrow", - "list-filter-label": "Filter List by Title", - "filter-clear": "Clear filter", + "list-filter-label": "Filtriraj seznam po imenu", + "filter-clear": "Počisti filter", "filter-labels-label": "Filter by label", - "filter-no-label": "No label", + "filter-no-label": "Brez oznake", "filter-member-label": "Filter by member", - "filter-no-member": "No member", + "filter-no-member": "Brez člana", "filter-assignee-label": "Filter by assignee", "filter-no-assignee": "No assignee", "filter-custom-fields-label": "Filter by Custom Fields", - "filter-no-custom-fields": "No Custom Fields", - "filter-show-archive": "Show archived lists", - "filter-hide-empty": "Hide empty lists", - "filter-on": "Filter is on", - "filter-on-desc": "You are filtering cards on this board. Click here to edit filter.", - "filter-to-selection": "Filter to selection", + "filter-no-custom-fields": "Brez poljubnih polj", + "filter-show-archive": "Prikaži arhivirane sezname", + "filter-hide-empty": "Skrij prazne sezname", + "filter-on": "Filter vklopljen", + "filter-on-desc": "Filtrirane kartice na tej tabli. Kliknite tukaj za urejanje filtra.", + "filter-to-selection": "Filtriraj izbrane", "other-filters-label": "Other Filters", - "advanced-filter-label": "Advanced Filter", - "advanced-filter-description": "Advanced Filter allows to write a string containing following operators: == != <= >= && || ( ) A space is used as a separator between the Operators. You can filter for all Custom Fields by typing their names and values. For Example: Field1 == Value1. Note: If fields or values contains spaces, you need to encapsulate them into single quotes. For Example: 'Field 1' == 'Value 1'. For single control characters (' \\/) to be skipped, you can use \\. For example: Field1 == I\\'m. Also you can combine multiple conditions. For Example: F1 == V1 || F1 == V2. Normally all operators are interpreted from left to right. You can change the order by placing brackets. For Example: F1 == V1 && ( F2 == V2 || F2 == V3 ). Also you can search text fields using regex: F1 == /Tes.*/i", - "fullname": "Full Name", - "header-logo-title": "Go back to your boards page.", + "advanced-filter-label": "Napredni filter", + "advanced-filter-description": "Napredni filter omogoča pripravo niza, ki vsebuje naslednje operaterje: == != <= >= && || () Preslednica se uporablja kot ločilo med operatorji. Vsa polja po meri lahko filtrirate tako, da vtipkate njihova imena in vrednosti. Na primer: Polje1 == Vrednost1. Opomba: Če polja ali vrednosti vsebujejo presledke, jih morate postaviti v enojne narekovaje. Primer: 'Polje 1' == 'Vrednost 1'. Če želite preskočiti posamezne kontrolne znake (' \\\\/), lahko uporabite \\\\\\. Na primer: Polje1 == I\\\\'m. Prav tako lahko kombinirate več pogojev. Na primer: F1 == V1 || F1 == V2. Običajno se vsi operaterji interpretirajo od leve proti desni. Vrstni red lahko spremenite tako, da postavite oklepaje. Na primer: F1 == V1 && ( F2 == V2 || F2 == V3 ). Prav tako lahko po besedilu iščete z uporabo pravil regex: F1 == /Tes.*/i", + "fullname": "Polno Ime", + "header-logo-title": "Pojdi nazaj na stran s tablami.", "show-activities": "Show Activities", - "headerBarCreateBoardPopup-title": "Create Board", - "home": "Home", - "import": "Import", + "headerBarCreateBoardPopup-title": "Ustvari tablo", + "home": "Domov", + "import": "Uvozi", "impersonate-user": "Impersonate user", - "link": "Link", - "import-board": "import board", - "import-board-c": "Import board", - "import-board-title-trello": "Import board from Trello", - "import-board-title-wekan": "Import board from previous export", + "link": "Poveži", + "import-board": "uvozi tablo", + "import-board-c": "Uvozi tablo", + "import-board-title-trello": "Uvozi tablo iz orodja Trello", + "import-board-title-wekan": "Uvozi tablo iz prejšnjega izvoza", "import-board-title-csv": "Import board from CSV/TSV", - "from-trello": "From Trello", - "from-wekan": "From previous export", + "from-trello": "Iz orodja Trello", + "from-wekan": "Od prejšnjega izvoza", "from-csv": "From CSV/TSV", - "import-board-instruction-trello": "In your Trello board, go to 'Menu', then 'More', 'Print and Export', 'Export JSON', and copy the resulting text.", + "import-board-instruction-trello": "V vaši Trello tabli pojdite na 'Meni', 'Več', 'Natisni in Izvozi', 'Izvozi JSON', in kopirajte prikazano besedilo.", "import-board-instruction-csv": "Paste in your Comma Separated Values(CSV)/ Tab Separated Values (TSV) .", - "import-board-instruction-wekan": "In your board, go to 'Menu', then 'Export board', and copy the text in the downloaded file.", - "import-board-instruction-about-errors": "If you get errors when importing board, sometimes importing still works, and board is at All Boards page.", - "import-json-placeholder": "Paste your valid JSON data here", + "import-board-instruction-wekan": "V vaši tabli pojdite na 'Meni', 'Izvozi tablo' in kopirajte besedilo iz prenesene datoteke.", + "import-board-instruction-about-errors": "Pri napakah med uvozom table v nekaterih primerih uvažanje še deluje, uvožena tabla pa je na strani Vse Table.", + "import-json-placeholder": "Tukaj prilepite veljavne JSON podatke", "import-csv-placeholder": "Paste your valid CSV/TSV data here", - "import-map-members": "Map members", - "import-members-map": "Your imported board has some members. Please map the members you want to import to your users", + "import-map-members": "Mapiraj člane", + "import-members-map": "Vaša uvožena tabla vsebuje nekaj članov. Prosimo mapirajte člane, ki jih želite uvoziti, z vašimi uporabniki.", "import-members-map-note": "Note: Unmapped members will be assigned to the current user.", - "import-show-user-mapping": "Review members mapping", - "import-user-select": "Pick your existing user you want to use as this member", - "importMapMembersAddPopup-title": "Select member", - "info": "Version", - "initials": "Initials", - "invalid-date": "Invalid date", - "invalid-time": "Invalid time", - "invalid-user": "Invalid user", - "joined": "joined", - "just-invited": "You are just invited to this board", - "keyboard-shortcuts": "Keyboard shortcuts", - "label-create": "Create Label", - "label-default": "%s label (default)", - "label-delete-pop": "There is no undo. This will remove this label from all cards and destroy its history.", - "labels": "Labels", - "language": "Language", - "last-admin-desc": "You can’t change roles because there must be at least one admin.", - "leave-board": "Leave Board", - "leave-board-pop": "Are you sure you want to leave __boardTitle__? You will be removed from all cards on this board.", - "leaveBoardPopup-title": "Leave Board ?", - "link-card": "Link to this card", - "list-archive-cards": "Move all cards in this list to Archive", - "list-archive-cards-pop": "This will remove all the cards in this list from the board. To view cards in Archive and bring them back to the board, click “Menu” > “Archive”.", - "list-move-cards": "Move all cards in this list", - "list-select-cards": "Select all cards in this list", - "set-color-list": "Set Color", - "listActionPopup-title": "List Actions", + "import-show-user-mapping": "Preglejte povezane člane", + "import-user-select": "Izberite obstoječega uporabnika, ki ga želite uporabiti kot tega člana.", + "importMapMembersAddPopup-title": "Izberite člana", + "info": "Različica", + "initials": "Inicialke", + "invalid-date": "Neveljaven datum", + "invalid-time": "Neveljaven čas", + "invalid-user": "Neveljaven uporabnik", + "joined": "se je pridružil", + "just-invited": "Povabljeni ste k tej tabli", + "keyboard-shortcuts": "Bližnjice", + "label-create": "Ustvari oznako", + "label-default": "%s oznaka (privzeto)", + "label-delete-pop": "Razveljavitve ni. To bo odstranilo oznako iz vseh kartic in izbrisalo njeno zgodovino.", + "labels": "Oznake", + "language": "Jezik", + "last-admin-desc": "Ne morete zamenjati vlog, ker mora obstajati vsaj en admin.", + "leave-board": "Zapusti tablo", + "leave-board-pop": "Ste prepričani, da želite zapustiti tablo __boardTitle__? Odstranjeni boste iz vseh kartic na tej tabli.", + "leaveBoardPopup-title": "Zapusti tablo ?", + "link-card": "Poveži s kartico", + "list-archive-cards": "Arhiviraj vse kartice v seznamu", + "list-archive-cards-pop": "To bo odstranilo vse kartice tega seznama. Za ogled in vrnitev kartic iz arhiva na tablo, kliknite \"Meni\" > \"arhiv\".", + "list-move-cards": "Premakni vse kartice na seznamu", + "list-select-cards": "Izberi vse kartice na seznamu", + "set-color-list": "Nastavi barvo", + "listActionPopup-title": "Dejanja seznama", "settingsUserPopup-title": "User Settings", "settingsTeamPopup-title": "Team Settings", "settingsOrgPopup-title": "Organization Settings", - "swimlaneActionPopup-title": "Swimlane Actions", - "swimlaneAddPopup-title": "Add a Swimlane below", - "listImportCardPopup-title": "Import a Trello card", + "swimlaneActionPopup-title": "Dejanja plavalnih stez", + "swimlaneAddPopup-title": "Dodaj plavalno stezo spodaj", + "listImportCardPopup-title": "Uvozi Trello kartico", "listImportCardsTsvPopup-title": "Import Excel CSV/TSV", - "listMorePopup-title": "More", - "link-list": "Link to this list", - "list-delete-pop": "All actions will be removed from the activity feed and you won't be able to recover the list. There is no undo.", - "list-delete-suggest-archive": "You can move a list to Archive to remove it from the board and preserve the activity.", - "lists": "Lists", - "swimlanes": "Swimlanes", - "log-out": "Log Out", - "log-in": "Log In", - "loginPopup-title": "Log In", - "memberMenuPopup-title": "Member Settings", - "members": "Members", - "menu": "Menu", - "move-selection": "Move selection", - "moveCardPopup-title": "Move Card", - "moveCardToBottom-title": "Move to Bottom", - "moveCardToTop-title": "Move to Top", - "moveSelectionPopup-title": "Move selection", - "multi-selection": "Multi-Selection", + "listMorePopup-title": "Več", + "link-list": "Poveži s seznamom", + "list-delete-pop": "Vsa dejanja bodo odstranjena iz vira dejavnosti in seznama ne boste mogli obnoviti. Razveljavitve ni.", + "list-delete-suggest-archive": "Lahko premaknete seznam v arhiv, da ga odstranite iz table in ohranite dejavnosti.", + "lists": "Seznami", + "swimlanes": "Plavalne steze", + "log-out": "Odjava", + "log-in": "Prijava", + "loginPopup-title": "Prijava", + "memberMenuPopup-title": "Nastavitve članov", + "members": "Člani", + "menu": "Meni", + "move-selection": "Premakni izbiro", + "moveCardPopup-title": "Premakni kartico", + "moveCardToBottom-title": "Premakni na dno", + "moveCardToTop-title": "Premakni na vrh", + "moveSelectionPopup-title": "Premakni izbiro", + "multi-selection": "Multi-Izbira", "multi-selection-label": "Set label for selection", "multi-selection-member": "Set member for selection", - "multi-selection-on": "Multi-Selection is on", - "muted": "Muted", - "muted-info": "You will never be notified of any changes in this board", - "my-boards": "My Boards", - "name": "Name", - "no-archived-cards": "No cards in Archive.", - "no-archived-lists": "No lists in Archive.", - "no-archived-swimlanes": "No swimlanes in Archive.", - "no-results": "No results", - "normal": "Normal", - "normal-desc": "Can view and edit cards. Can't change settings.", - "not-accepted-yet": "Invitation not accepted yet", + "multi-selection-on": "Multi-Izbira je omogočena", + "muted": "Utišano", + "muted-info": "O spremembah na tej tabli ne boste prejemali obvestil.", + "my-boards": "Moje Table", + "name": "Ime", + "no-archived-cards": "Ni kartic v arhivu", + "no-archived-lists": "Ni seznamov v arhivu", + "no-archived-swimlanes": "Ni plavalnih stez v arhivu", + "no-results": "Ni zadetkov", + "normal": "Normalno", + "normal-desc": "Lahko gleda in ureja kartice. Ne more spreminjati nastavitev.", + "not-accepted-yet": "Povabilo še ni sprejeto.", "notify-participate": "Receive updates to any cards you participate as creator or member", - "notify-watch": "Receive updates to any boards, lists, or cards you’re watching", - "optional": "optional", - "or": "or", - "page-maybe-private": "This page may be private. You may be able to view it by logging in.", - "page-not-found": "Page not found.", - "password": "Password", - "paste-or-dragdrop": "to paste, or drag & drop image file to it (image only)", - "participating": "Participating", - "preview": "Preview", - "previewAttachedImagePopup-title": "Preview", - "previewClipboardImagePopup-title": "Preview", - "private": "Private", - "private-desc": "This board is private. Only people added to the board can view and edit it.", - "profile": "Profile", - "public": "Public", - "public-desc": "This board is public. It's visible to anyone with the link and will show up in search engines like Google. Only people added to the board can edit.", - "quick-access-description": "Star a board to add a shortcut in this bar.", + "notify-watch": "Prejemajte posodobitve opazovanih tabel, seznamov ali kartic", + "optional": "opcijsko", + "or": "ali", + "page-maybe-private": "Ta stran je morda privatna. Verjetno si jo lahko ogledate poprijavi.", + "page-not-found": "Stran ne obstaja.", + "password": "Geslo", + "paste-or-dragdrop": "prilepi ali povleci & spusti datoteko slike (samo slika)", + "participating": "Sodelovanje", + "preview": "Predogled", + "previewAttachedImagePopup-title": "Predogled", + "previewClipboardImagePopup-title": "Predogled", + "private": "Zasebno", + "private-desc": "Ta tabla je zasebna. Vsebino lahko vidijo ali urejajo samo dodani uporabniki.", + "profile": "Profil", + "public": "Javno", + "public-desc": "Ta tabla je javna. Vidna je vsakomur s povezavo do table in bo prikazana v zadetkih iskalnikov kot Google. Urejajo jo lahko samo člani table.", + "quick-access-description": "Če tablo označite z zvezdico, bo tukaj dodana bližnjica.", "remove-cover": "Remove cover image from minicard", - "remove-from-board": "Remove from Board", - "remove-label": "Remove Label", - "listDeletePopup-title": "Delete List ?", - "remove-member": "Remove Member", - "remove-member-from-card": "Remove from Card", - "remove-member-pop": "Remove __name__ (__username__) from __boardTitle__? The member will be removed from all cards on this board. They will receive a notification.", - "removeMemberPopup-title": "Remove Member?", - "rename": "Rename", - "rename-board": "Rename Board", - "restore": "Restore", + "remove-from-board": "Odstrani iz table", + "remove-label": "Odstrani oznako", + "listDeletePopup-title": "Odstrani seznam?", + "remove-member": "Odstrani člana", + "remove-member-from-card": "Odstrani iz kartice", + "remove-member-pop": "Odstrani __name__ (__username__) iz __boardTitle__? Član bo odstranjen iz vseh kartic te table in bo prejel obvestilo.", + "removeMemberPopup-title": "Odstrani člana?", + "rename": "Preimenuj", + "rename-board": "Preimenuj tablo", + "restore": "Obnovi", "rescue-card-description": "Show rescue dialogue before closing for unsaved card descriptions", "rescue-card-description-dialogue": "Overwrite current card description with your changes?", - "save": "Save", - "search": "Search", - "rules": "Rules", + "save": "Shrani", + "search": "Išči", + "rules": "Pravila", "search-cards": "Search from card/list titles, descriptions and custom fields on this board", "search-example": "Write text you search and press Enter", - "select-color": "Select Color", + "select-color": "Izberi barvo", "select-board": "Select Board", - "set-wip-limit-value": "Set a limit for the maximum number of tasks in this list", - "setWipLimitPopup-title": "Set WIP Limit", + "set-wip-limit-value": "Omeji maksimalno število opravil v seznamu", + "setWipLimitPopup-title": "Omeji število kartic", "shortcut-add-self": "Add yourself to current card", - "shortcut-assign-self": "Assign yourself to current card", - "shortcut-autocomplete-emoji": "Autocomplete emoji", - "shortcut-autocomplete-members": "Autocomplete members", - "shortcut-clear-filters": "Clear all filters", - "shortcut-close-dialog": "Close Dialog", - "shortcut-filter-my-cards": "Filter my cards", + "shortcut-assign-self": "Dodeli sebe k trenutni kartici", + "shortcut-autocomplete-emoji": "Samodokončaj emoji", + "shortcut-autocomplete-members": "Samodokončaj člane", + "shortcut-clear-filters": "Počisti vse filtre", + "shortcut-close-dialog": "Zapri dialog", + "shortcut-filter-my-cards": "Filtriraj moje kartice", "shortcut-filter-my-assigned-cards": "Filter my assigned cards", - "shortcut-show-shortcuts": "Bring up this shortcuts list", + "shortcut-show-shortcuts": "Prikaži seznam bližnjic", "shortcut-toggle-filterbar": "Toggle Filter Sidebar", "shortcut-toggle-searchbar": "Toggle Search Sidebar", - "shortcut-toggle-sidebar": "Toggle Board Sidebar", - "show-cards-minimum-count": "Show cards count if list contains more than", - "sidebar-open": "Open Sidebar", - "sidebar-close": "Close Sidebar", - "signupPopup-title": "Create an Account", - "star-board-title": "Click to star this board. It will show up at top of your boards list.", - "starred-boards": "Starred Boards", - "starred-boards-description": "Starred boards show up at the top of your boards list.", - "subscribe": "Subscribe", - "team": "Team", - "this-board": "this board", - "this-card": "this card", - "spent-time-hours": "Spent time (hours)", - "overtime-hours": "Overtime (hours)", - "overtime": "Overtime", - "has-overtime-cards": "Has overtime cards", - "has-spenttime-cards": "Has spent time cards", - "time": "Time", - "title": "Title", + "shortcut-toggle-sidebar": "Preklopi stransko vrstico table", + "show-cards-minimum-count": "Prikaži število kartic, če seznam vsebuje več kot", + "sidebar-open": "Odpri stransko vrstico", + "sidebar-close": "Zapri stransko vrstico", + "signupPopup-title": "Ustvari up. račun", + "star-board-title": "Označite tablo z zvezdico, da bo prikazana na vrhu v seznamu tabel.", + "starred-boards": "Table z zvezdico", + "starred-boards-description": "Table z zvezdico se prikažejo na vrhu vašega seznama tabel.", + "subscribe": "Naročite se", + "team": "Skupina", + "this-board": "tablo", + "this-card": "kartico", + "spent-time-hours": "Porabljen čas (ure)", + "overtime-hours": "Presežen čas (ure)", + "overtime": "Presežen čas", + "has-overtime-cards": "Ima kartice s preseženim časom", + "has-spenttime-cards": "Ima kartice s porabljenim časom", + "time": "Čas", + "title": "Naslov", "toggle-assignees": "Toggle assignees 1-9 for card (By order of addition to board).", "toggle-labels": "Toggle labels 1-9 for card. Multi-Selection adds labels 1-9", "remove-labels-multiselect": "Multi-Selection removes labels 1-9", - "tracking": "Tracking", - "tracking-info": "You will be notified of any changes to those cards you are involved as creator or member.", - "type": "Type", - "unassign-member": "Unassign member", - "unsaved-description": "You have an unsaved description.", - "unwatch": "Unwatch", - "upload": "Upload", - "upload-avatar": "Upload an avatar", - "uploaded-avatar": "Uploaded an avatar", + "tracking": "Sledenje", + "tracking-info": "Obveščeni boste o vseh spremembah nad karticami, kjer ste lastnik ali član.", + "type": "Tip", + "unassign-member": "Odjavi člana", + "unsaved-description": "Imate neshranjen opis.", + "unwatch": "Prekliči opazovanje", + "upload": "Naloži", + "upload-avatar": "Naloži avatar", + "uploaded-avatar": "Naložil avatar", "uploading-files": "Uploading files", "upload-failed": "Upload failed", "upload-completed": "Upload completed", @@ -642,317 +642,317 @@ "custom-help-link-url": "Custom Help Link URL", "text-below-custom-login-logo": "Text below Custom Login Logo", "automatic-linked-url-schemes": "Custom URL Schemes which should automatically be clickable. One URL Scheme per line", - "username": "Username", + "username": "Up. ime", "import-usernames": "Import Usernames", - "view-it": "View it", - "warn-list-archived": "warning: this card is in an list at Archive", - "watch": "Watch", - "watching": "Watching", - "watching-info": "You will be notified of any change in this board", - "welcome-board": "Welcome Board", - "welcome-swimlane": "Milestone 1", - "welcome-list1": "Basics", - "welcome-list2": "Advanced", - "card-templates-swimlane": "Card Templates", - "list-templates-swimlane": "List Templates", - "board-templates-swimlane": "Board Templates", - "what-to-do": "What do you want to do?", - "wipLimitErrorPopup-title": "Invalid WIP Limit", - "wipLimitErrorPopup-dialog-pt1": "The number of tasks in this list is higher than the WIP limit you've defined.", - "wipLimitErrorPopup-dialog-pt2": "Please move some tasks out of this list, or set a higher WIP limit.", - "admin-panel": "Admin Panel", - "settings": "Settings", - "people": "People", - "registration": "Registration", - "disable-self-registration": "Disable Self-Registration", + "view-it": "Poglej", + "warn-list-archived": "opozorilo: ta kartica je v seznamu v arhivu", + "watch": "Opazuj", + "watching": "Opazuje", + "watching-info": "O spremembah na tej tabli boste obveščeni", + "welcome-board": "Tabla Dobrodošli", + "welcome-swimlane": "Mejnik 1", + "welcome-list1": "Osnove", + "welcome-list2": "Napredno", + "card-templates-swimlane": "Predloge kartice", + "list-templates-swimlane": "Predloge seznama", + "board-templates-swimlane": "Predloge table", + "what-to-do": "Kaj želite storiti?", + "wipLimitErrorPopup-title": "Neveljaven limit št. kartic", + "wipLimitErrorPopup-dialog-pt1": "Število opravil v seznamu je višje od limita št. kartic.", + "wipLimitErrorPopup-dialog-pt2": "Prosimo premaknite nekaj opravil iz tega seznama ali nastavite višji limit št. kartic.", + "admin-panel": "Skrbniška plošča", + "settings": "Nastavitve", + "people": "Ljudje", + "registration": "Registracija", + "disable-self-registration": "Onemogoči samo-registracijo", "disable-forgot-password": "Disable Forgot Password", - "invite": "Invite", - "invite-people": "Invite People", - "to-boards": "To board(s)", - "email-addresses": "Email Addresses", - "smtp-host-description": "The address of the SMTP server that handles your emails.", - "smtp-port-description": "The port your SMTP server uses for outgoing emails.", - "smtp-tls-description": "Enable TLS support for SMTP server", + "invite": "Povabi", + "invite-people": "Povabi ljudi", + "to-boards": "K tabli(am)", + "email-addresses": "E-poštni naslovi", + "smtp-host-description": "Naslov vašega strežnika SMTP.", + "smtp-port-description": "Vrata vašega strežnika SMTP za odhodno pošto.", + "smtp-tls-description": "Omogoči šifriranje TLS za SMTP strežnik.", "smtp-host": "SMTP Host", - "smtp-port": "SMTP Port", - "smtp-username": "Username", - "smtp-password": "Password", - "smtp-tls": "TLS support", - "send-from": "From", - "send-smtp-test": "Send a test email to yourself", - "invitation-code": "Invitation Code", - "email-invite-register-subject": "__inviter__ sent you an invitation", - "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to kanban board for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.", - "email-smtp-test-subject": "SMTP Test Email", - "email-smtp-test-text": "You have successfully sent an email", - "error-invitation-code-not-exist": "Invitation code doesn't exist", - "error-notAuthorized": "You are not authorized to view this page.", - "webhook-title": "Webhook Name", - "webhook-token": "Token (Optional for Authentication)", - "outgoing-webhooks": "Outgoing Webhooks", - "bidirectional-webhooks": "Two-Way Webhooks", - "outgoingWebhooksPopup-title": "Outgoing Webhooks", - "boardCardTitlePopup-title": "Card Title Filter", - "disable-webhook": "Disable This Webhook", - "global-webhook": "Global Webhooks", - "new-outgoing-webhook": "New Outgoing Webhook", - "no-name": "(Unknown)", - "Node_version": "Node version", - "Meteor_version": "Meteor version", - "MongoDB_version": "MongoDB version", + "smtp-port": "SMTP vrata", + "smtp-username": "Up. ime", + "smtp-password": "Geslo", + "smtp-tls": "TLS podpora", + "send-from": "Od", + "send-smtp-test": "Pošljite testno e-pošto na svoj naslov", + "invitation-code": "Koda Povabila", + "email-invite-register-subject": "__inviter__ vam je poslal povabilo", + "email-invite-register-text": "Dragi __user__,\n\n__inviter__ vas vabi na kanban tablo za sodelovanje.\n\nProsimo sledite spodnji povezavi:\n__url__\n\nVaša koda povabila je: __icode__\n\nHvala.", + "email-smtp-test-subject": "SMTP testna e-pošta", + "email-smtp-test-text": "Uspešno ste poslali e-pošto", + "error-invitation-code-not-exist": "Koda povabila ne obstaja", + "error-notAuthorized": "Nimate pravic za ogled te strani.", + "webhook-title": "Ime spletnega vmesnika (webhook)", + "webhook-token": "Žeton (opcijsko za avtentikacijo)", + "outgoing-webhooks": "Izhodni spletni vmesniki (webhooks)", + "bidirectional-webhooks": "Dvo-smerni spletni vmesniki (webhooks)", + "outgoingWebhooksPopup-title": "Izhodni spletni vmesniki (webhooks)", + "boardCardTitlePopup-title": "Filter po naslovu kartice", + "disable-webhook": "Onemogoči ta spletni vmesnik (webhook)", + "global-webhook": "Globalni spletni vmesnik (webhook)", + "new-outgoing-webhook": "Nov izhodni spletni vmesnik (webhook)", + "no-name": "(Neznano)", + "Node_version": "Node različica", + "Meteor_version": "Meteor različica", + "MongoDB_version": "MongoDB različica", "MongoDB_storage_engine": "MongoDB storage engine", - "MongoDB_Oplog_enabled": "MongoDB Oplog enabled", - "OS_Arch": "OS Arch", - "OS_Cpus": "OS CPU Count", - "OS_Freemem": "OS Free Memory", - "OS_Loadavg": "OS Load Average", - "OS_Platform": "OS Platform", - "OS_Release": "OS Release", - "OS_Totalmem": "OS Total Memory", - "OS_Type": "OS Type", - "OS_Uptime": "OS Uptime", - "days": "days", - "hours": "hours", - "minutes": "minutes", - "seconds": "seconds", - "show-field-on-card": "Show this field on card", + "MongoDB_Oplog_enabled": "MongoDB Oplog omogočen", + "OS_Arch": "OS Arhitektura", + "OS_Cpus": "OS število CPU", + "OS_Freemem": "OS prost pomnilnik", + "OS_Loadavg": "OS povp. obremenitev", + "OS_Platform": "OS platforma", + "OS_Release": "OS izdaja", + "OS_Totalmem": "OS skupni pomnilnik", + "OS_Type": "OS tip", + "OS_Uptime": "OS čas delovanja", + "days": "dnevi", + "hours": "ure", + "minutes": "minute", + "seconds": "sekunde", + "show-field-on-card": "Prikaži to polje na kartici", "automatically-field-on-card": "Add field to new cards", "always-field-on-card": "Add field to all cards", - "showLabel-field-on-card": "Show field label on minicard", + "showLabel-field-on-card": "Prikaži oznako polja na mini kartici", "showSum-field-on-list": "Show sum of fields at top of list", - "yes": "Yes", - "no": "No", - "accounts": "Accounts", - "accounts-allowEmailChange": "Allow Email Change", - "accounts-allowUserNameChange": "Allow Username Change", + "yes": "Da", + "no": "Ne", + "accounts": "Up. računi", + "accounts-allowEmailChange": "Dovoli spremembo e-poštnega naslova", + "accounts-allowUserNameChange": "Dovoli spremembo up. imena", "tableVisibilityMode-allowPrivateOnly": "Boards visibility: Allow private boards only", "tableVisibilityMode" : "Boards visibility", - "createdAt": "Created at", + "createdAt": "Ustvarjen ob", "modifiedAt": "Modified at", - "verified": "Verified", - "active": "Active", - "card-received": "Received", - "card-received-on": "Received on", - "card-end": "End", - "card-end-on": "Ends on", - "editCardReceivedDatePopup-title": "Change received date", - "editCardEndDatePopup-title": "Change end date", - "setCardColorPopup-title": "Set color", - "setCardActionsColorPopup-title": "Choose a color", - "setSwimlaneColorPopup-title": "Choose a color", - "setListColorPopup-title": "Choose a color", - "assigned-by": "Assigned By", - "requested-by": "Requested By", + "verified": "Preverjeno", + "active": "Aktivno", + "card-received": "Prejeto", + "card-received-on": "Prejeto ob", + "card-end": "Konec", + "card-end-on": "Končano na", + "editCardReceivedDatePopup-title": "Spremeni datum prejema", + "editCardEndDatePopup-title": "Spremeni končni datum", + "setCardColorPopup-title": "Nastavi barvo", + "setCardActionsColorPopup-title": "Izberi barvo", + "setSwimlaneColorPopup-title": "Izberi barvo", + "setListColorPopup-title": "Izberi barvo", + "assigned-by": "Dodelil", + "requested-by": "Zahteval", "card-sorting-by-number": "Card sorting by number", - "board-delete-notice": "Deleting is permanent. You will lose all lists, cards and actions associated with this board.", - "delete-board-confirm-popup": "All lists, cards, labels, and activities will be deleted and you won't be able to recover the board contents. There is no undo.", - "boardDeletePopup-title": "Delete Board?", - "delete-board": "Delete Board", - "default-subtasks-board": "Subtasks for __board__ board", - "default": "Default", - "defaultdefault": "Default", - "queue": "Queue", - "subtask-settings": "Subtasks Settings", - "card-settings": "Card Settings", + "board-delete-notice": "Brisanje je trajno. Izgubili boste vse sezname, kartice in akcije, povezane z desko.", + "delete-board-confirm-popup": "Vsi seznami, kartice, oznake in dejavnosti bodo izbrisani in vsebine table ne boste mogli obnoviti. Razveljavitve ni.", + "boardDeletePopup-title": "Izbriši tablo?", + "delete-board": "Izbriši tablo", + "default-subtasks-board": "Podopravila za tablo", + "default": "Privzeto", + "defaultdefault": "Privzeto", + "queue": "Čakalna vrsta", + "subtask-settings": "Nastavitve podopravil", + "card-settings": "Nastavitve kartice", "minicard-settings": "Minicard Settings", - "boardSubtaskSettingsPopup-title": "Board Subtasks Settings", - "boardCardSettingsPopup-title": "Card Settings", + "boardSubtaskSettingsPopup-title": "Nastavitve podopravil table", + "boardCardSettingsPopup-title": "Nastavitve kartice", "boardMinicardSettingsPopup-title": "Minicard Settings", - "deposit-subtasks-board": "Deposit subtasks to this board:", - "deposit-subtasks-list": "Landing list for subtasks deposited here:", - "show-parent-in-minicard": "Show parent in minicard:", + "deposit-subtasks-board": "Deponiraj podopravila na tablo:", + "deposit-subtasks-list": "Ciljni seznam za deponirana podopravila:", + "show-parent-in-minicard": "Pokaži starša na mini-kartici:", "description-on-minicard": "Description on minicard", "cover-attachment-on-minicard": "Cover image on minicard", "badge-attachment-on-minicard": "Count of attachments on minicard", "card-sorting-by-number-on-minicard": "Card sorting by number on minicard", - "prefix-with-full-path": "Prefix with full path", - "prefix-with-parent": "Prefix with parent", - "subtext-with-full-path": "Subtext with full path", - "subtext-with-parent": "Subtext with parent", - "change-card-parent": "Change card's parent", - "parent-card": "Parent card", - "source-board": "Source board", - "no-parent": "Don't show parent", - "activity-added-label": "added label '%s' to %s", - "activity-removed-label": "removed label '%s' from %s", - "activity-delete-attach": "deleted an attachment from %s", - "activity-added-label-card": "added label '%s'", - "activity-removed-label-card": "removed label '%s'", - "activity-delete-attach-card": "deleted an attachment", - "activity-set-customfield": "set custom field '%s' to '%s' in %s", - "activity-unset-customfield": "unset custom field '%s' in %s", - "r-rule": "Rule", - "r-add-trigger": "Add trigger", - "r-add-action": "Add action", - "r-board-rules": "Board rules", - "r-add-rule": "Add rule", - "r-view-rule": "View rule", - "r-delete-rule": "Delete rule", - "r-new-rule-name": "New rule title", - "r-no-rules": "No rules", + "prefix-with-full-path": "Predpona s celotno potjo", + "prefix-with-parent": "Predpona s staršem", + "subtext-with-full-path": "Podbesedilo s celotno potjo", + "subtext-with-parent": "Podbesedilo s staršem", + "change-card-parent": "Zamenjaj starša kartice", + "parent-card": "Starševska kartica", + "source-board": "Izvorna tabla", + "no-parent": "Ne prikaži starša", + "activity-added-label": "dodal oznako '%s' do %s", + "activity-removed-label": "odstranil oznako '%s' od %s", + "activity-delete-attach": "izbrisal priponko od %s", + "activity-added-label-card": "dodal oznako '%s'", + "activity-removed-label-card": "izbrisal oznako '%s'", + "activity-delete-attach-card": "izbrisal priponko", + "activity-set-customfield": "nastavi polje po meri '%s' do '%s' v %s", + "activity-unset-customfield": "zbriši polje po meri '%s' v %s", + "r-rule": "Pravilo", + "r-add-trigger": "Dodaj prožilec", + "r-add-action": "Dodaj akcijo", + "r-board-rules": "Pravila table", + "r-add-rule": "Dodaj pravilo", + "r-view-rule": "Poglej pravilo", + "r-delete-rule": "Izbriši pravilo", + "r-new-rule-name": "Ime novega pravila", + "r-no-rules": "Ni pravil", "r-trigger": "Trigger", "r-action": "Action", - "r-when-a-card": "When a card", - "r-is": "is", - "r-is-moved": "is moved", + "r-when-a-card": "Ko je kartica", + "r-is": " ", + "r-is-moved": "premaknjena", "r-added-to": "Added to", - "r-removed-from": "Removed from", - "r-the-board": "the board", - "r-list": "list", - "set-filter": "Set Filter", - "r-moved-to": "Moved to", - "r-moved-from": "Moved from", - "r-archived": "Moved to Archive", - "r-unarchived": "Restored from Archive", - "r-a-card": "a card", - "r-when-a-label-is": "When a label is", - "r-when-the-label": "When the label", - "r-list-name": "list name", - "r-when-a-member": "When a member is", - "r-when-the-member": "When the member", - "r-name": "name", - "r-when-a-attach": "When an attachment", - "r-when-a-checklist": "When a checklist is", - "r-when-the-checklist": "When the checklist", - "r-completed": "Completed", - "r-made-incomplete": "Made incomplete", - "r-when-a-item": "When a checklist item is", - "r-when-the-item": "When the checklist item", - "r-checked": "Checked", - "r-unchecked": "Unchecked", - "r-move-card-to": "Move card to", - "r-top-of": "Top of", - "r-bottom-of": "Bottom of", - "r-its-list": "its list", - "r-archive": "Move to Archive", - "r-unarchive": "Restore from Archive", - "r-card": "card", - "r-add": "Add", - "r-remove": "Remove", - "r-label": "label", - "r-member": "member", - "r-remove-all": "Remove all members from the card", - "r-set-color": "Set color to", - "r-checklist": "checklist", - "r-check-all": "Check all", - "r-uncheck-all": "Uncheck all", - "r-items-check": "items of checklist", - "r-check": "Check", - "r-uncheck": "Uncheck", - "r-item": "item", - "r-of-checklist": "of checklist", - "r-send-email": "Send an email", - "r-to": "to", + "r-removed-from": "izbrisan iz", + "r-the-board": "tabla", + "r-list": "seznam", + "set-filter": "Nastavi filter", + "r-moved-to": "premaknjena v", + "r-moved-from": "premaknjena iz", + "r-archived": "premaknjena v arhiv", + "r-unarchived": "obnovljena iz arhiva", + "r-a-card": "kartico", + "r-when-a-label-is": "Ko je oznaka", + "r-when-the-label": "Ko je oznaka", + "r-list-name": "ime sezn.", + "r-when-a-member": "Ko je član", + "r-when-the-member": "Ko je član", + "r-name": "ime", + "r-when-a-attach": "Ko je priponka", + "r-when-a-checklist": "Ko je kontrolni seznam", + "r-when-the-checklist": "Ko kontrolni seznam", + "r-completed": "zaključen", + "r-made-incomplete": "nastavljen kot nedokončan", + "r-when-a-item": "Ko je kontrolni seznam", + "r-when-the-item": "Ko je element kontrolnega seznama", + "r-checked": "označen", + "r-unchecked": "odznačen", + "r-move-card-to": "Premakni kartico na", + "r-top-of": "Vrh", + "r-bottom-of": "Dno", + "r-its-list": "pripadajočega seznama", + "r-archive": "premaknjena v arhiv", + "r-unarchive": "Obnovi iz arhiva", + "r-card": "kartico", + "r-add": "Dodaj", + "r-remove": "Odstrani", + "r-label": "oznaka", + "r-member": "član", + "r-remove-all": "Izbriši vse člane iz kartice", + "r-set-color": "Nastavi barvo na", + "r-checklist": "kontrolni seznam", + "r-check-all": "Označi vse", + "r-uncheck-all": "Odznači vse", + "r-items-check": "postavke kontrolnega lista", + "r-check": "Označi", + "r-uncheck": "Odznači", + "r-item": "postavka", + "r-of-checklist": "kontrolnega seznama", + "r-send-email": "Pošlji e-pošto", + "r-to": "naslovnik", "r-of": "of", - "r-subject": "subject", - "r-rule-details": "Rule details", - "r-d-move-to-top-gen": "Move card to top of its list", - "r-d-move-to-top-spec": "Move card to top of list", - "r-d-move-to-bottom-gen": "Move card to bottom of its list", - "r-d-move-to-bottom-spec": "Move card to bottom of list", - "r-d-send-email": "Send email", - "r-d-send-email-to": "to", - "r-d-send-email-subject": "subject", - "r-d-send-email-message": "message", - "r-d-archive": "Move card to Archive", - "r-d-unarchive": "Restore card from Archive", - "r-d-add-label": "Add label", - "r-d-remove-label": "Remove label", - "r-create-card": "Create new card", - "r-in-list": "in list", - "r-in-swimlane": "in swimlane", - "r-d-add-member": "Add member", - "r-d-remove-member": "Remove member", - "r-d-remove-all-member": "Remove all member", - "r-d-check-all": "Check all items of a list", - "r-d-uncheck-all": "Uncheck all items of a list", - "r-d-check-one": "Check item", - "r-d-uncheck-one": "Uncheck item", - "r-d-check-of-list": "of checklist", - "r-d-add-checklist": "Add checklist", - "r-d-remove-checklist": "Remove checklist", - "r-by": "by", - "r-add-checklist": "Add checklist", - "r-with-items": "with items", - "r-items-list": "item1,item2,item3", - "r-add-swimlane": "Add swimlane", - "r-swimlane-name": "swimlane name", + "r-subject": "zadeva", + "r-rule-details": "Podrobnosti pravila", + "r-d-move-to-top-gen": "Premakni kartico na vrh pripadajočega sezama", + "r-d-move-to-top-spec": "Premakni kartico na vrh seznama", + "r-d-move-to-bottom-gen": "Premakni kartico na dno pripadajočega seznama", + "r-d-move-to-bottom-spec": "Premakni kartico na dno seznama", + "r-d-send-email": "Pošlji e-pošto", + "r-d-send-email-to": "naslovnik", + "r-d-send-email-subject": "zadeva", + "r-d-send-email-message": "vsebina", + "r-d-archive": "Premakni kartico v arhiv", + "r-d-unarchive": "Obnovi kartico iz arhiva", + "r-d-add-label": "Dodaj oznako", + "r-d-remove-label": "Izbriši oznako", + "r-create-card": "Ustvari novo kartico", + "r-in-list": "v seznamu", + "r-in-swimlane": "v plavalni stezi", + "r-d-add-member": "Dodaj člana", + "r-d-remove-member": "Odstrani člana", + "r-d-remove-all-member": "Odstrani vse člane", + "r-d-check-all": "Označi vse elemente seznama", + "r-d-uncheck-all": "Odznači vse elemente seznama", + "r-d-check-one": "Označi element", + "r-d-uncheck-one": "Odznači element", + "r-d-check-of-list": "kontrolnega seznama", + "r-d-add-checklist": "Dodaj kontrolni list", + "r-d-remove-checklist": "Odstrani kotrolni list", + "r-by": "od", + "r-add-checklist": "Dodaj kontrolni list", + "r-with-items": "s postavkami", + "r-items-list": "el1,el2,el3", + "r-add-swimlane": "Dodaj plavalno stezo", + "r-swimlane-name": "ime pl. steze", "r-board-note": "Note: leave a field empty to match every possible value. ", - "r-checklist-note": "Note: checklist's items have to be written as comma separated values.", - "r-when-a-card-is-moved": "When a card is moved to another list", - "r-set": "Set", - "r-update": "Update", - "r-datefield": "date field", - "r-df-start-at": "start", - "r-df-due-at": "due", - "r-df-end-at": "end", - "r-df-received-at": "received", - "r-to-current-datetime": "to current date/time", - "r-remove-value-from": "Remove value from", + "r-checklist-note": "Opomba: elementi kontrolnega seznama morajo biti zapisani kot vrednosti, ločene z vejicami.", + "r-when-a-card-is-moved": "Ko je kartica premaknjena v drug seznam", + "r-set": "Nastavi", + "r-update": "Posodobi", + "r-datefield": "polje z datumom", + "r-df-start-at": "začetek", + "r-df-due-at": "rok", + "r-df-end-at": "konec", + "r-df-received-at": "prejeto", + "r-to-current-datetime": "v trenutni datum/čas", + "r-remove-value-from": "Izbriši vrednost iz", "r-link-card": "Link card to", "ldap": "LDAP", "oauth2": "OAuth2", "cas": "CAS", - "authentication-method": "Authentication method", - "authentication-type": "Authentication type", - "custom-product-name": "Custom Product Name", - "layout": "Layout", - "hide-logo": "Hide Logo", + "authentication-method": "Metoda avtentikacije", + "authentication-type": "Način avtentikacije", + "custom-product-name": "Ime izdelka po meri", + "layout": "Postavitev", + "hide-logo": "Skrij logo", "hide-card-counter-list": "Hide card counter list on All Boards", "hide-board-member-list": "Hide board member list on All Boards", - "add-custom-html-after-body-start": "Add Custom HTML after start", - "add-custom-html-before-body-end": "Add Custom HTML before end", - "error-undefined": "Something went wrong", - "error-ldap-login": "An error occurred while trying to login", - "display-authentication-method": "Display Authentication Method", + "add-custom-html-after-body-start": "Dodaj HTML po meri po začetku", + "add-custom-html-before-body-end": "Dodaj HMTL po meri po koncu", + "error-undefined": "Prišlo je do napake", + "error-ldap-login": "Prišlo je do napake ob prijavi", + "display-authentication-method": "Prikaži metodo avtentikacije", "oidc-button-text": "Customize the OIDC button text", - "default-authentication-method": "Default Authentication Method", - "duplicate-board": "Duplicate Board", + "default-authentication-method": "Privzeta metoda avtentikacije", + "duplicate-board": "Dupliciraj tablo", "duplicate-board-confirm": "Are you sure you want to duplicate this board?", "org-number": "The number of organizations is: ", "team-number": "The number of teams is: ", "people-number": "The number of people is: ", - "swimlaneDeletePopup-title": "Delete Swimlane ?", - "swimlane-delete-pop": "All actions will be removed from the activity feed and you won't be able to recover the swimlane. There is no undo.", - "restore-all": "Restore all", - "delete-all": "Delete all", - "loading": "Loading, please wait.", - "previous_as": "last time was", - "act-a-dueAt": "modified due time to \nWhen: __timeValue__\nWhere: __card__\n previous due was __timeOldValue__", - "act-a-endAt": "modified ending time to __timeValue__ from (__timeOldValue__)", - "act-a-startAt": "modified starting time to __timeValue__ from (__timeOldValue__)", - "act-a-receivedAt": "modified received time to __timeValue__ from (__timeOldValue__)", - "a-dueAt": "modified due time to be", - "a-endAt": "modified ending time to be", - "a-startAt": "modified starting time to be", - "a-receivedAt": "modified received time to be", - "almostdue": "current due time %s is approaching", - "pastdue": "current due time %s is past", - "duenow": "current due time %s is today", - "act-newDue": "__list__/__card__ has 1st due reminder [__board__]", - "act-withDue": "__list__/__card__ due reminders [__board__]", - "act-almostdue": "was reminding the current due (__timeValue__) of __card__ is approaching", - "act-pastdue": "was reminding the current due (__timeValue__) of __card__ is past", - "act-duenow": "was reminding the current due (__timeValue__) of __card__ is now", - "act-atUserComment": "You were mentioned in [__board__] __list__/__card__", - "delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.", + "swimlaneDeletePopup-title": "Zbriši plavalno stezo?", + "swimlane-delete-pop": "Vsa dejanja bodo odstranjena iz seznama dejavnosti. Plavalne steze ne boste mogli obnoviti. Razveljavitve ni.", + "restore-all": "Obnovi vse", + "delete-all": "Izbriši vse", + "loading": "Nalagam, prosimo počakajte", + "previous_as": "zadnji čas je bil", + "act-a-dueAt": "spremenil rok zapadlosti na \nKdaj: __timeValue__\nKje: __card__\n prejšnji rok zapadlosti je bil __timeOldValue__", + "act-a-endAt": "spremenil čas dokončanja na __timeValue__ iz (__timeOldValue__)", + "act-a-startAt": "spremenil čas pričetka na __timeValue__ iz (__timeOldValue__)", + "act-a-receivedAt": "spremenil čas prejema na __timeValue__ iz (__timeOldValue__)", + "a-dueAt": "spremenil rok v", + "a-endAt": "spremenil končni čas v", + "a-startAt": "spremenil začetni čas v", + "a-receivedAt": "spremenil čas prejetja v", + "almostdue": "trenutni rok %s se približuje", + "pastdue": "trenutni rok %s je potekel", + "duenow": "trenutni rok %s je danes", + "act-newDue": "__list__/__card__ ima 1. opomnik roka zapadlosti [__board__]", + "act-withDue": "__list__/__card__ opomniki roka zapadlosti [__board__]", + "act-almostdue": "je opomnil trenuten rok zapadlosti (__timeValue__) kartice __card__ se bliža", + "act-pastdue": "je opomnil trenuten rok zapadlosti (__timeValue__) kartice __card__ je potekel", + "act-duenow": "je opomnil trenuten rok zapadlosti (__timeValue__) kartice __card__ je sedaj", + "act-atUserComment": "Omenjeni ste bili v [__board__] __list__/__card__", + "delete-user-confirm-popup": "Ali ste prepričani, da želite izbrisati ta račun? Razveljavitve ni.", "delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.", "delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.", - "accounts-allowUserDelete": "Allow users to self delete their account", - "hide-minicard-label-text": "Hide minicard label text", - "show-desktop-drag-handles": "Show desktop drag handles", - "assignee": "Assignee", - "cardAssigneesPopup-title": "Assignee", - "addmore-detail": "Add a more detailed description", - "show-on-card": "Show on Card", + "accounts-allowUserDelete": "Dovoli uporabnikom, da sami izbrišejo svoj račun", + "hide-minicard-label-text": "Skrij besedilo oznak na karticah", + "show-desktop-drag-handles": "Pokaži ročke za povleko na namizju", + "assignee": "Dodeljen član", + "cardAssigneesPopup-title": "Dodeljen član", + "addmore-detail": "Dodaj podrobnejši opis", + "show-on-card": "Prikaži na kartici", "show-on-minicard": "Show on Minicard", - "new": "New", + "new": "Novo", "editOrgPopup-title": "Edit Organization", "newOrgPopup-title": "New Organization", "editTeamPopup-title": "Edit Team", "newTeamPopup-title": "New Team", - "editUserPopup-title": "Edit User", - "newUserPopup-title": "New User", + "editUserPopup-title": "Uredi uporabnika", + "newUserPopup-title": "Nov uporabnik", "notifications": "Notifications", "help": "Help", "view-all": "View All", @@ -991,13 +991,13 @@ "website": "Website", "person": "Person", "my-cards": "My Cards", - "card": "Card", + "card": "Kartica", "list": "List", "board": "Board", "context-separator": "/", "myCardsViewChange-title": "My Cards View", "myCardsViewChangePopup-title": "My Cards View", - "myCardsViewChange-choice-boards": "Boards", + "myCardsViewChange-choice-boards": "Table", "myCardsViewChange-choice-table": "Table", "myCardsSortChange-title": "My Cards Sort", "myCardsSortChangePopup-title": "My Cards Sort", @@ -1028,19 +1028,19 @@ "operator-board-abbrev": "b", "operator-swimlane": "swimlane", "operator-swimlane-abbrev": "s", - "operator-list": "list", + "operator-list": "seznam", "operator-list-abbrev": "l", - "operator-label": "label", + "operator-label": "oznaka", "operator-label-abbrev": "#", "operator-user": "user", "operator-user-abbrev": "@", - "operator-member": "member", + "operator-member": "član", "operator-member-abbrev": "m", "operator-assignee": "assignee", "operator-assignee-abbrev": "a", "operator-creator": "creator", "operator-status": "status", - "operator-due": "due", + "operator-due": "rok", "operator-created": "created", "operator-modified": "modified", "operator-sort": "sort", @@ -1059,16 +1059,16 @@ "predicate-month": "month", "predicate-quarter": "quarter", "predicate-year": "year", - "predicate-due": "due", + "predicate-due": "rok", "predicate-modified": "modified", "predicate-created": "created", "predicate-attachment": "attachment", "predicate-description": "description", - "predicate-checklist": "checklist", - "predicate-start": "start", - "predicate-end": "end", + "predicate-checklist": "kontrolni seznam", + "predicate-start": "začetek", + "predicate-end": "konec", "predicate-assignee": "assignee", - "predicate-member": "member", + "predicate-member": "član", "predicate-public": "public", "predicate-private": "private", "predicate-selector": "selector", @@ -1119,7 +1119,7 @@ "globalSearch-instructions-notes-5": "By default archived cards are not searched.", "link-to-search": "Link to this search", "excel-font": "Arial", - "number": "Number", + "number": "Število", "label-colors": "Label Colors", "label-names": "Label Names", "archived-at": "archived at", @@ -1185,7 +1185,7 @@ "add-teams-label": "Added teams are displayed below:", "remove-team-from-table": "Are you sure you want to remove this team from the board ?", "confirm-btn": "Confirm", - "remove-btn": "Remove", + "remove-btn": "Odstrani", "filter-card-title-label": "Filter by card title", "invite-people-success": "Invitation to register sent with success", "invite-people-error": "Error while sending invitation to register", @@ -1242,7 +1242,7 @@ "storage": "Storage", "action": "Action", "board-title": "Board Title", - "attachmentRenamePopup-title": "Rename", + "attachmentRenamePopup-title": "Preimenuj", "uploading": "Uploading", "remaining_time": "Remaining time", "speed": "Speed", @@ -1253,7 +1253,7 @@ "forgot-password": "Forgot password", "minicardDetailsActionsPopup-title": "Card Details", "Mongo_sessions_count": "Mongo sessions count", - "change-visibility": "Change Visibility", + "change-visibility": "Spremeni vidnost", "max-upload-filesize": "Max upload filesize in bytes:", "allowed-upload-filetypes": "Allowed upload filetypes:", "max-avatar-filesize": "Max avatar filesize in bytes:", @@ -1267,19 +1267,19 @@ "editTranslationPopup-title": "Edit custom translation string", "settingsTranslationPopup-title": "Delete this custom translation string?", "translation": "Translation", - "text": "Text", + "text": "Besedilo", "translation-text": "Translation text", "show-subtasks-field": "Show subtasks field", "show-week-of-year": "Show week of year (ISO 8601)", "convert-to-markdown": "Convert to markdown", "import-board-zip": "Add .zip file that has board JSON files, and board name subdirectories with attachments", - "collapse": "Collapse", + "collapse": "Skrči", "uncollapse": "Uncollapse", "hideCheckedChecklistItems": "Hide checked checklist items", "hideAllChecklistItems": "Hide all checklist items", "support": "Support", "supportPopup-title": "Support", - "accessibility": "Accessibility", + "accessibility": "Dostopnost", "accessibility-page-enabled": "Accessibility page enabled", "accessibility-info-not-added-yet": "Accessibility info has not been added yet", "accessibility-title": "Accessibility title", @@ -1307,7 +1307,7 @@ "admin-people-filter-show": "Show:", "admin-people-filter-all": "All Users", "admin-people-filter-locked": "Locked Users Only", - "admin-people-filter-active": "Active", + "admin-people-filter-active": "Aktivno", "admin-people-filter-inactive": "Not Active", "admin-people-active-status": "Active Status", "admin-people-user-active": "User is active - click to deactivate", @@ -1396,7 +1396,7 @@ "card-show-lists-on-minicard": "Show Lists on Minicard", "cleanup": "Cleanup", "cleanup-old-jobs": "Cleanup Old Jobs", - "completed": "Completed", + "completed": "zaključen", "conversion-info-text": "This conversion is performed once per board and improves performance. You can continue using the board normally.", "converting-board": "Converting Board", "converting-board-description": "Converting board structure for improved functionality. This may take a few moments.", From 61f7099106efca8e8a1858847a80f78f71e7982c Mon Sep 17 00:00:00 2001 From: helioguardabaxo Date: Sun, 19 Oct 2025 09:24:18 -0300 Subject: [PATCH 005/183] Fix stared, archive and clone icons --- client/components/boards/boardsList.css | 28 +++++++------- client/components/boards/boardsList.jade | 47 ++++++++++++++---------- 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/client/components/boards/boardsList.css b/client/components/boards/boardsList.css index 995b22445..2c039e9d2 100644 --- a/client/components/boards/boardsList.css +++ b/client/components/boards/boardsList.css @@ -103,8 +103,8 @@ transform: rotate(4deg); display: block !important; } -.board-list li.starred .fa-star, -.board-list li.starred .fa-star-o { +.board-list li.starred .is-star-active, +.board-list li.starred .is-not-star-active { opacity: 1; } .board-list .board-list-item { @@ -154,8 +154,8 @@ .board-list .js-add-board :hover { background-color: #939393; } -.board-list .fa-star, -.board-list .fa-star-o { +.board-list .is-star-active, +.board-list .is-not-star-active { bottom: 0; font-size: 14px; height: 18px; @@ -212,30 +212,30 @@ transition-duration: 0.15s; transition-property: color, font-size, background; } -.board-list li:hover a:hover .fa-star, +.board-list li:hover a:hover .is-star-active, .board-list li:hover a:hover .fa-clone, .board-list li:hover a:hover .fa-archive, -.board-list li:hover a:hover .fa-star-o { +.board-list li:hover a:hover .is-not-star-active { color: #fff; } -.board-list li:hover a .fa-star, +.board-list li:hover a .is-star-active, .board-list li:hover a .fa-clone, .board-list li:hover a .fa-archive, -.board-list li:hover a .fa-star-o { +.board-list li:hover a .is-not-star-active { color: #fff; opacity: 0.75; } -.board-list li:hover a .fa-star:hover, +.board-list li:hover a .is-star-active:hover, .board-list li:hover a .fa-clone:hover, .board-list li:hover a .fa-archive:hover, -.board-list li:hover a .fa-star-o:hover { +.board-list li:hover a .is-not-star-active:hover { font-size: 18px; opacity: 1; } -.board-list li:hover a .fa-star.is-star-active, -.board-list li:hover a .fa-clone.is-star-active, -.board-list li:hover a .fa-archive.is-star-active, -.board-list li:hover a .fa-star-o.is-star-active { +.board-list li:hover a .is-star-active, +.board-list li:hover a .fa-clone, +.board-list li:hover a .fa-archive, +.board-list li:hover a .is-not-star-active { opacity: 1; } .board-backgrounds-list .board-background-select { diff --git a/client/components/boards/boardsList.jade b/client/components/boards/boardsList.jade index f5fb4522e..b612c4019 100644 --- a/client/components/boards/boardsList.jade +++ b/client/components/boards/boardsList.jade @@ -108,9 +108,10 @@ template(name="boardList") each list in boardLists _id .item | {{ list }} - i.fa.js-star-board( - class="fa-star{{#if isStarred}} is-star-active{{else}}-o{{/if}}" + a.js-star-board( + class="{{#if isStarred}}is-star-active{{else}}is-not-star-active{{/if}}" title="{{_ 'star-board-title'}}") + | {{#if isStarred}}⭐{{else}}☆{{/if}} p.board-list-item-desc +viewer = description @@ -124,26 +125,32 @@ template(name="boardList") title="{{_ 'drag-board'}}") else if isSandstorm - i.fa.js-clone-board( - class="fa-clone" - title="{{_ 'duplicate-board'}}") - i.fa.js-archive-board( - class="fa-archive" - title="{{_ 'archive-board'}}") + a.js-clone-board( + class="fa-clone" + title="{{_ 'duplicate-board'}}") + | 📋 + a.js-archive-board( + class="fa-archive" + title="{{_ 'archive-board'}}") + | 📦 else if isAdministrable - i.fa.js-clone-board( - class="fa-clone" - title="{{_ 'duplicate-board'}}") - i.fa.js-archive-board( - class="fa-archive" - title="{{_ 'archive-board'}}") + a.js-clone-board( + class="fa-clone" + title="{{_ 'duplicate-board'}}") + | 📋 + a.js-archive-board( + class="fa-archive" + title="{{_ 'archive-board'}}") + | 📦 else if currentUser.isAdmin - i.fa.js-clone-board( - class="fa-clone" - title="{{_ 'duplicate-board'}}") - i.fa.js-archive-board( - class="fa-archive" - title="{{_ 'archive-board'}}") + a.js-clone-board( + class="fa-clone" + title="{{_ 'duplicate-board'}}") + | 📋 + a.js-archive-board( + class="fa-archive" + title="{{_ 'archive-board'}}") + | 📦 template(name="boardListHeaderBar") h1 {{_ title }} From 5d2bfab0f5a9a1487683d859b62c7c65b83ddf8c Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 19 Oct 2025 18:12:25 +0300 Subject: [PATCH 006/183] Updated ChangeLog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3eaa879f..6f4f23a9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ This release fixes the following bugs: - [Fix add and drag drop attachments to minicards and card](https://github.com/wekan/wekan/commit/b06daff4c7e63453643459f7d8798fde97e3200c (HEAD -> main) Thanks to xet7. +- [Fix starred, archive and clone icons](https://github.com/wekan/wekan/pull/5953). + Thanks to helioguardabaxo. Thanks to above GitHub users for their contributions and translators for their translations. From d965faa3174dc81636106e6f81435b2750b0625f Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 19 Oct 2025 18:42:37 +0300 Subject: [PATCH 007/183] Fix Due dates to be color coded and have icons. Thanks to xet7 ! Fixes #5950 --- client/components/cards/cardDate.css | 2 +- client/components/cards/cardDate.jade | 16 ++++---- client/components/cards/cardDate.js | 35 ++++++++-------- client/components/cards/minicard.css | 57 +++++++++++++++++++++++++++ client/components/cards/minicard.jade | 7 +--- imports/lib/dateUtils.js | 41 +++++++++++++++++++ 6 files changed, 126 insertions(+), 32 deletions(-) diff --git a/client/components/cards/cardDate.css b/client/components/cards/cardDate.css index 952cc15b7..3305c6617 100644 --- a/client/components/cards/cardDate.css +++ b/client/components/cards/cardDate.css @@ -94,7 +94,7 @@ /* Generic date badge and custom field date */ .card-date:not(.received-date):not(.start-date):not(.due-date):not(.end-date) time::before { - content: "📅"; /* Calendar - represents generic date */ + /*content: "📅"; // Calendar - represents generic date */ } .card-date time::before { font-size: inherit; diff --git a/client/components/cards/cardDate.jade b/client/components/cards/cardDate.jade index c19f45528..b477ff723 100644 --- a/client/components/cards/cardDate.jade +++ b/client/components/cards/cardDate.jade @@ -24,14 +24,14 @@ template(name="dateCustomField") template(name="minicardReceivedDate") if canModifyCard - a.js-edit-date.card-date(title="{{_ 'card-received'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}") + a.js-edit-date.card-date.received-date(title="{{_ 'card-received'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}") time(datetime="{{showISODate}}") | {{showDate}} if showWeekOfYear b | {{showWeek}} else - a.card-date(title="{{_ 'card-received'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}") + a.card-date.received-date(title="{{_ 'card-received'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}") time(datetime="{{showISODate}}") | {{showDate}} if showWeekOfYear @@ -40,14 +40,14 @@ template(name="minicardReceivedDate") template(name="minicardStartDate") if canModifyCard - a.js-edit-date.card-date(title="{{_ 'card-start'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}") + a.js-edit-date.card-date.start-date(title="{{_ 'card-start'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}") time(datetime="{{showISODate}}") | {{showDate}} if showWeekOfYear b | {{showWeek}} else - a.card-date(title="{{_ 'card-start'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}") + a.card-date.start-date(title="{{_ 'card-start'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}") time(datetime="{{showISODate}}") | {{showDate}} if showWeekOfYear @@ -56,14 +56,14 @@ template(name="minicardStartDate") template(name="minicardDueDate") if canModifyCard - a.js-edit-date.card-date(title="{{_ 'card-due'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}") + a.js-edit-date.card-date.due-date(title="{{_ 'card-due'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}") time(datetime="{{showISODate}}") | {{showDate}} if showWeekOfYear b | {{showWeek}} else - a.card-date(title="{{_ 'card-due'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}") + a.card-date.due-date(title="{{_ 'card-due'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}") time(datetime="{{showISODate}}") | {{showDate}} if showWeekOfYear @@ -72,14 +72,14 @@ template(name="minicardDueDate") template(name="minicardEndDate") if canModifyCard - a.js-edit-date.card-date(title="{{_ 'card-end'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}") + a.js-edit-date.card-date.end-date(title="{{_ 'card-end'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}") time(datetime="{{showISODate}}") | {{showDate}} if showWeekOfYear b | {{showWeek}} else - a.card-date(title="{{_ 'card-end'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}") + a.card-date.end-date(title="{{_ 'card-end'}} {{_ 'predicate-week'}} {{#if showWeekOfYear}}{{showWeek}}{{/if}}" class="{{classes}}") time(datetime="{{showISODate}}") | {{showDate}} if showWeekOfYear diff --git a/client/components/cards/cardDate.js b/client/components/cards/cardDate.js index 451af183c..685504021 100644 --- a/client/components/cards/cardDate.js +++ b/client/components/cards/cardDate.js @@ -18,7 +18,8 @@ import { now, createDate, fromNow, - calendar + calendar, + diff } from '/imports/lib/dateUtils'; // editCardReceivedDatePopup @@ -169,9 +170,7 @@ class CardReceivedDate extends CardDate { } showTitle() { - return `${TAPi18n.__('card-received-on')} ${this.date - .get() - .format('LLLL')}`; + return `${TAPi18n.__('card-received-on')} ${format(this.date.get(), 'LLLL')}`; } events() { @@ -198,15 +197,15 @@ class CardStartDate extends CardDate { const theDate = this.date.get(); const now = this.now.get(); // if dueAt or endAt exist & are > startAt, startAt doesn't need to be flagged - if ((endAt && theDate.isAfter(endAt)) || (dueAt && theDate.isAfter(dueAt))) + if ((endAt && isAfter(theDate, endAt)) || (dueAt && isAfter(theDate, dueAt))) classes += 'long-overdue'; - else if (theDate.isAfter(now)) classes += ''; + else if (isAfter(theDate, now)) classes += ''; else classes += 'current'; return classes; } showTitle() { - return `${TAPi18n.__('card-start-on')} ${this.date.get().format('LLLL')}`; + return `${TAPi18n.__('card-start-on')} ${format(this.date.get(), 'LLLL')}`; } events() { @@ -232,17 +231,17 @@ class CardDueDate extends CardDate { const theDate = this.date.get(); const now = this.now.get(); // if the due date is after the end date, green - done early - if (endAt && theDate.isAfter(endAt)) classes += 'current'; + if (endAt && isAfter(theDate, endAt)) classes += 'current'; // if there is an end date, don't need to flag the due date else if (endAt) classes += ''; - else if (now.diff(theDate, 'days') >= 2) classes += 'long-overdue'; - else if (now.diff(theDate, 'minute') >= 0) classes += 'due'; - else if (now.diff(theDate, 'days') >= -1) classes += 'almost-due'; + else if (diff(now, theDate, 'days') >= 2) classes += 'long-overdue'; + else if (diff(now, theDate, 'minute') >= 0) classes += 'due'; + else if (diff(now, theDate, 'days') >= -1) classes += 'almost-due'; return classes; } showTitle() { - return `${TAPi18n.__('card-due-on')} ${this.date.get().format('LLLL')}`; + return `${TAPi18n.__('card-due-on')} ${format(this.date.get(), 'LLLL')}`; } events() { @@ -267,13 +266,13 @@ class CardEndDate extends CardDate { const dueAt = this.data().getDue(); const theDate = this.date.get(); if (!dueAt) classes += ''; - else if (theDate.isBefore(dueAt)) classes += 'current'; - else if (theDate.isAfter(dueAt)) classes += 'due'; + else if (isBefore(theDate, dueAt)) classes += 'current'; + else if (isAfter(theDate, dueAt)) classes += 'due'; return classes; } showTitle() { - return `${TAPi18n.__('card-end-on')} ${this.date.get().format('LLLL')}`; + return `${TAPi18n.__('card-end-on')} ${format(this.date.get(), 'LLLL')}`; } events() { @@ -315,7 +314,7 @@ class CardCustomFieldDate extends CardDate { } showTitle() { - return `${this.date.get().format('LLLL')}`; + return `${format(this.date.get(), 'LLLL')}`; } classes() { @@ -418,10 +417,10 @@ class PokerEndDate extends CardDate { return classes; } showDate() { - return this.date.get().format('l LT'); + return format(this.date.get(), 'l LT'); } showTitle() { - return `${TAPi18n.__('card-end-on')} ${this.date.get().format('LLLL')}`; + return `${TAPi18n.__('card-end-on')} ${format(this.date.get(), 'LLLL')}`; } events() { diff --git a/client/components/cards/minicard.css b/client/components/cards/minicard.css index 637be7763..b6007c7b3 100644 --- a/client/components/cards/minicard.css +++ b/client/components/cards/minicard.css @@ -169,6 +169,63 @@ margin-right: 0.4vw; } +/* Unicode icons for minicard dates - matching cardDate.css */ +.minicard .card-date.end-date time::before { + content: "🏁"; /* Finish flag - represents end/completion */ +} +.minicard .card-date.due-date time::before { + content: "⏰"; /* Alarm clock - represents due/deadline */ +} +.minicard .card-date.start-date time::before { + content: "🚀"; /* Rocket - represents start/launch */ +} +.minicard .card-date.received-date time::before { + content: "📥"; /* Inbox tray - represents received/incoming */ +} + +.minicard .card-date time::before { + font-size: inherit; + margin-right: 0.3em; + display: inline-block; +} + +/* Date type specific colors for minicards - matching cardDate.css */ +.minicard .card-date.received-date { + background-color: #dbdbdb; /* Grey for received - same as base card-date */ +} + +.minicard .card-date.received-date:hover, +.minicard .card-date.received-date.is-active { + background-color: #b3b3b3; +} + +.minicard .card-date.start-date { + background-color: #3cb500; /* Green for start */ +} + +.minicard .card-date.start-date:hover, +.minicard .card-date.start-date.is-active { + background-color: #2d8f00; +} + +.minicard .card-date.due-date { + background-color: #ff9f19; /* Orange for due */ +} + +.minicard .card-date.due-date:hover, +.minicard .card-date.due-date.is-active { + background-color: #e68a00; +} + +.minicard .card-date.end-date { + background-color: #a632db; /* Purple for end */ +} + +.minicard .card-date.end-date:hover, +.minicard .card-date.end-date.is-active { + background-color: #8a2bb8; +} + /* Font Awesome icons in minicard dates */ .minicard .card-date i.fa { margin-right: 0.3vw; diff --git a/client/components/cards/minicard.jade b/client/components/cards/minicard.jade index 686fd5eac..402b651c5 100644 --- a/client/components/cards/minicard.jade +++ b/client/components/cards/minicard.jade @@ -12,11 +12,8 @@ template(name="minicard") a.minicard-details-menu.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}") | ☰ .dates if getReceived - unless getStart - unless getDue - unless getEnd - .date - +minicardReceivedDate + .date + +minicardReceivedDate if getStart .date +minicardStartDate diff --git a/imports/lib/dateUtils.js b/imports/lib/dateUtils.js index de7d95ea7..2e781befc 100644 --- a/imports/lib/dateUtils.js +++ b/imports/lib/dateUtils.js @@ -304,6 +304,10 @@ export function format(date, format = 'L') { return d.toLocaleString(); case 'llll': return d.toLocaleString(); + case 'LLLL': + return d.toLocaleString(); + case 'l LT': + return `${month}/${day}/${year} ${hours}:${minutes}`; case 'YYYY-MM-DD': return `${year}-${month}-${day}`; case 'YYYY-MM-DD HH:mm': @@ -490,3 +494,40 @@ export function calendar(date, now = new Date()) { return format(d, 'L'); } + +/** + * Calculate the difference between two dates in the specified unit + * @param {Date|string} date1 - First date + * @param {Date|string} date2 - Second date + * @param {string} unit - Unit of measurement ('millisecond', 'second', 'minute', 'hour', 'day', 'week', 'month', 'year') + * @returns {number} Difference in the specified unit + */ +export function diff(date1, date2, unit = 'millisecond') { + const d1 = new Date(date1); + const d2 = new Date(date2); + + if (isNaN(d1.getTime()) || isNaN(d2.getTime())) return 0; + + const diffMs = d1.getTime() - d2.getTime(); + + switch (unit) { + case 'millisecond': + return diffMs; + case 'second': + return Math.floor(diffMs / 1000); + case 'minute': + return Math.floor(diffMs / (1000 * 60)); + case 'hour': + return Math.floor(diffMs / (1000 * 60 * 60)); + case 'day': + return Math.floor(diffMs / (1000 * 60 * 60 * 24)); + case 'week': + return Math.floor(diffMs / (1000 * 60 * 60 * 24 * 7)); + case 'month': + return Math.floor(diffMs / (1000 * 60 * 60 * 24 * 30)); + case 'year': + return Math.floor(diffMs / (1000 * 60 * 60 * 24 * 365)); + default: + return diffMs; + } +} From dc78e3b7a04539f7d1c1fb7c999706e0de0f72fb Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 19 Oct 2025 18:45:32 +0300 Subject: [PATCH 008/183] Updated ChangeLog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f4f23a9f..a0175c5ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ This release fixes the following bugs: Thanks to xet7. - [Fix starred, archive and clone icons](https://github.com/wekan/wekan/pull/5953). Thanks to helioguardabaxo. +- [Fix Due dates to be color coded and have unicode icons](https://github.com/wekan/wekan/commit/d965faa3174dc81636106e6f81435b2750b0625f). + Thanks to xet7. Thanks to above GitHub users for their contributions and translators for their translations. From 101048339bdd1e45f876aeb1aa5ec32ceda28139 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 19 Oct 2025 18:55:44 +0300 Subject: [PATCH 009/183] Fix Due dates to be color coded and have icons. Part 2. Thanks to xet7 ! Fixes #5950 --- client/components/cards/cardDate.css | 19 +++++++++++-------- client/components/cards/minicard.css | 15 +++++++++------ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/client/components/cards/cardDate.css b/client/components/cards/cardDate.css index 3305c6617..ba0b52b0f 100644 --- a/client/components/cards/cardDate.css +++ b/client/components/cards/cardDate.css @@ -45,39 +45,42 @@ /* Date type specific colors */ .card-date.received-date { - background-color: #0079bf; /* Blue for received */ + background-color: #dbdbdb; /* Light grey for received */ } .card-date.received-date:hover, .card-date.received-date.is-active { - background-color: #005a8b; + background-color: #b3b3b3; } .card-date.start-date { - background-color: #3cb500; /* Green for start */ + background-color: #90ee90; /* Light green for start */ + color: #000; /* Black text for start */ } .card-date.start-date:hover, .card-date.start-date.is-active { - background-color: #2d8f00; + background-color: #7dd87d; } .card-date.due-date { - background-color: #ff9f19; /* Orange for due */ + background-color: #ffd700; /* Yellow for due */ + color: #000; /* Black text for due */ } .card-date.due-date:hover, .card-date.due-date.is-active { - background-color: #e68a00; + background-color: #e6c200; } .card-date.end-date { - background-color: #a632db; /* Purple for end */ + background-color: #ffb3b3; /* Light red for end */ + color: #000; /* Black text for end */ } .card-date.end-date:hover, .card-date.end-date.is-active { - background-color: #8a2bb8; + background-color: #ff9999; } .card-date.end-date time::before { content: "🏁"; /* Finish flag - represents end/completion */ diff --git a/client/components/cards/minicard.css b/client/components/cards/minicard.css index b6007c7b3..774cbaf73 100644 --- a/client/components/cards/minicard.css +++ b/client/components/cards/minicard.css @@ -200,30 +200,33 @@ } .minicard .card-date.start-date { - background-color: #3cb500; /* Green for start */ + background-color: #90ee90; /* Light green for start */ + color: #000; /* Black text for start */ } .minicard .card-date.start-date:hover, .minicard .card-date.start-date.is-active { - background-color: #2d8f00; + background-color: #7dd87d; } .minicard .card-date.due-date { - background-color: #ff9f19; /* Orange for due */ + background-color: #ffd700; /* Yellow for due */ + color: #000; /* Black text for due */ } .minicard .card-date.due-date:hover, .minicard .card-date.due-date.is-active { - background-color: #e68a00; + background-color: #e6c200; } .minicard .card-date.end-date { - background-color: #a632db; /* Purple for end */ + background-color: #ffb3b3; /* Light red for end */ + color: #000; /* Black text for end */ } .minicard .card-date.end-date:hover, .minicard .card-date.end-date.is-active { - background-color: #8a2bb8; + background-color: #ff9999; } /* Font Awesome icons in minicard dates */ From 23860b1ee88eea627885f8e46628bbc3ce69b141 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 19 Oct 2025 18:58:36 +0300 Subject: [PATCH 010/183] Updated ChangeLog. --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0175c5ba..dcee98f39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,11 +23,13 @@ Fixing other platforms In Progress. This release fixes the following bugs: -- [Fix add and drag drop attachments to minicards and card](https://github.com/wekan/wekan/commit/b06daff4c7e63453643459f7d8798fde97e3200c (HEAD -> main) +- [Fix add and drag drop attachments to minicards and card](https://github.com/wekan/wekan/commit/b06daff4c7e63453643459f7d8798fde97e3200c). Thanks to xet7. - [Fix starred, archive and clone icons](https://github.com/wekan/wekan/pull/5953). Thanks to helioguardabaxo. -- [Fix Due dates to be color coded and have unicode icons](https://github.com/wekan/wekan/commit/d965faa3174dc81636106e6f81435b2750b0625f). +- Fix Due dates to be color coded and have unicode icons. + [Part 1](https://github.com/wekan/wekan/commit/d965faa3174dc81636106e6f81435b2750b0625f), + [Part 2](https://github.com/wekan/wekan/commit/101048339bdd1e45f876aeb1aa5ec32ceda28139). Thanks to xet7. Thanks to above GitHub users for their contributions and translators for their translations. From 66b444e2b0c9b2ed5f98cd1ff0cd9222b2d0c624 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 19 Oct 2025 20:05:36 +0300 Subject: [PATCH 011/183] Fix unable to see My Due Cards. Thanks to xet7 ! Fixes #5948 --- client/components/cards/cardDate.js | 6 +- client/components/main/dueCards.js | 65 +++++++-- client/lib/cardSearch.js | 216 +++++++++++++++++++++++----- server/publications/cards.js | 98 ++++++++++--- 4 files changed, 321 insertions(+), 64 deletions(-) diff --git a/client/components/cards/cardDate.js b/client/components/cards/cardDate.js index 685504021..8b87b0cf9 100644 --- a/client/components/cards/cardDate.js +++ b/client/components/cards/cardDate.js @@ -160,9 +160,9 @@ class CardReceivedDate extends CardDate { const theDate = this.date.get(); // if dueAt, endAt and startAt exist & are > receivedAt, receivedAt doesn't need to be flagged if ( - (startAt && theDate.isAfter(startAt)) || - (endAt && theDate.isAfter(endAt)) || - (dueAt && theDate.isAfter(dueAt)) + (startAt && isAfter(theDate, startAt)) || + (endAt && isAfter(theDate, endAt)) || + (dueAt && isAfter(theDate, dueAt)) ) classes += 'long-overdue'; else classes += 'current'; diff --git a/client/components/main/dueCards.js b/client/components/main/dueCards.js index da113c07a..9909b5f97 100644 --- a/client/components/main/dueCards.js +++ b/client/components/main/dueCards.js @@ -15,7 +15,7 @@ BlazeComponent.extendComponent({ dueCardsView() { // eslint-disable-next-line no-console // console.log('sort:', Utils.dueCardsView()); - return Utils.dueCardsView(); + return Utils && Utils.dueCardsView ? Utils.dueCardsView() : 'me'; }, events() { @@ -38,12 +38,16 @@ BlazeComponent.extendComponent({ return [ { 'click .js-due-cards-view-me'() { - Utils.setDueCardsView('me'); + if (Utils && Utils.setDueCardsView) { + Utils.setDueCardsView('me'); + } Popup.back(); }, 'click .js-due-cards-view-all'() { - Utils.setDueCardsView('all'); + if (Utils && Utils.setDueCardsView) { + Utils.setDueCardsView('all'); + } Popup.back(); }, }, @@ -54,7 +58,38 @@ BlazeComponent.extendComponent({ class DueCardsComponent extends CardSearchPagedComponent { onCreated() { super.onCreated(); - + + // Add a small delay to ensure ReactiveCache is ready + this.searchRetryCount = 0; + this.maxRetries = 3; + + // Use a timeout to ensure the search runs after the component is fully initialized + Meteor.setTimeout(() => { + this.performSearch(); + }, 100); + } + + performSearch() { + if (process.env.DEBUG === 'true') { + console.log('Performing due cards search, attempt:', this.searchRetryCount + 1); + } + + // Check if user is authenticated + const currentUser = ReactiveCache.getCurrentUser(); + if (!currentUser) { + if (process.env.DEBUG === 'true') { + console.log('User not authenticated, waiting...'); + } + Meteor.setTimeout(() => { + this.performSearch(); + }, 1000); + return; + } + + if (process.env.DEBUG === 'true') { + console.log('User authenticated:', currentUser.username); + } + const queryParams = new QueryParams(); queryParams.addPredicate(OPERATOR_HAS, { field: PREDICATE_DUE_AT, @@ -66,17 +101,31 @@ class DueCardsComponent extends CardSearchPagedComponent { order: ORDER_ASCENDING, }); - if (Utils.dueCardsView() !== 'all') { - queryParams.addPredicate(OPERATOR_USER, ReactiveCache.getCurrentUser().username); - } + // Note: User filtering is handled server-side based on board membership + // The OPERATOR_USER filter is too restrictive as it only shows cards where + // the user is assigned or a member of the card, not the board + // if (Utils && Utils.dueCardsView && Utils.dueCardsView() !== 'all') { + // const currentUser = ReactiveCache.getCurrentUser(); + // if (currentUser && currentUser.username) { + // queryParams.addPredicate(OPERATOR_USER, currentUser.username); + // } + // } + // Debug: Log the query parameters + if (process.env.DEBUG === 'true') { + console.log('Due cards query params:', queryParams.params); + console.log('Due cards query text:', queryParams.text); + console.log('Due cards has predicates:', queryParams.getPredicates('has')); + console.log('Due cards sort predicates:', queryParams.getPredicates('sort')); + } + this.runGlobalSearch(queryParams); } dueCardsView() { // eslint-disable-next-line no-console //console.log('sort:', Utils.dueCardsView()); - return Utils.dueCardsView(); + return Utils && Utils.dueCardsView ? Utils.dueCardsView() : 'me'; } sortByBoard() { diff --git a/client/lib/cardSearch.js b/client/lib/cardSearch.js index 4803f815b..4b9c14ccd 100644 --- a/client/lib/cardSearch.js +++ b/client/lib/cardSearch.js @@ -29,21 +29,86 @@ export class CardSearchPagedComponent extends BlazeComponent { const that = this; this.subscriptionCallbacks = { onReady() { - that.getResults(); - that.searching.set(false); - that.hasResults.set(true); - that.serverError.set(false); + if (process.env.DEBUG === 'true') { + console.log('Subscription ready, getting results...'); + console.log('Subscription ready - sessionId:', that.sessionId); + } + + // Wait for session data to be available (with timeout) + let waitCount = 0; + const maxWaitCount = 50; // 10 seconds max wait + + const waitForSessionData = () => { + waitCount++; + const sessionData = that.getSessionData(); + if (process.env.DEBUG === 'true') { + console.log('waitForSessionData - attempt', waitCount, 'session data:', sessionData); + } + + if (sessionData) { + const results = that.getResults(); + if (process.env.DEBUG === 'true') { + console.log('Search results count:', results ? results.length : 0); + } + + // If no results and this is a due cards search, try to retry + if ((!results || results.length === 0) && that.searchRetryCount !== undefined && that.searchRetryCount < that.maxRetries) { + if (process.env.DEBUG === 'true') { + console.log('No results found, retrying search...'); + } + that.searchRetryCount++; + Meteor.setTimeout(() => { + if (that.performSearch) { + that.performSearch(); + } + }, 500); + return; + } + + that.searching.set(false); + that.hasResults.set(true); + that.serverError.set(false); + } else if (waitCount < maxWaitCount) { + // Session data not available yet, wait a bit more + if (process.env.DEBUG === 'true') { + console.log('Session data not available yet, waiting... (attempt', waitCount, 'of', maxWaitCount, ')'); + } + Meteor.setTimeout(waitForSessionData, 200); + } else { + // Timeout reached, try fallback search + if (process.env.DEBUG === 'true') { + console.log('Timeout reached waiting for session data, trying fallback search'); + } + const results = that.getResults(); + if (process.env.DEBUG === 'true') { + console.log('Fallback search results count:', results ? results.length : 0); + } + + if (results && results.length > 0) { + that.searching.set(false); + that.hasResults.set(true); + that.serverError.set(false); + } else { + that.searching.set(false); + that.hasResults.set(false); + that.serverError.set(true); + } + } + }; + + // Start waiting for session data + Meteor.setTimeout(waitForSessionData, 100); }, onError(error) { + if (process.env.DEBUG === 'true') { + console.log('Subscription error:', error); + console.log('Error.reason:', error.reason); + console.log('Error.message:', error.message); + console.log('Error.stack:', error.stack); + } that.searching.set(false); that.hasResults.set(false); that.serverError.set(true); - // eslint-disable-next-line no-console - //console.log('Error.reason:', error.reason); - // eslint-disable-next-line no-console - //console.log('Error.message:', error.message); - // eslint-disable-next-line no-console - //console.log('Error.stack:', error.stack); }, }; } @@ -62,9 +127,28 @@ export class CardSearchPagedComponent extends BlazeComponent { } getSessionData(sessionId) { - return ReactiveCache.getSessionData({ - sessionId: sessionId || SessionData.getSessionId(), + const sessionIdToUse = sessionId || SessionData.getSessionId(); + if (process.env.DEBUG === 'true') { + console.log('getSessionData - looking for sessionId:', sessionIdToUse); + } + + // Try using the raw SessionData collection instead of ReactiveCache + const sessionData = SessionData.findOne({ + sessionId: sessionIdToUse, }); + if (process.env.DEBUG === 'true') { + console.log('getSessionData - found session data (raw):', sessionData); + } + + // Also try ReactiveCache for comparison + const reactiveSessionData = ReactiveCache.getSessionData({ + sessionId: sessionIdToUse, + }); + if (process.env.DEBUG === 'true') { + console.log('getSessionData - found session data (reactive):', reactiveSessionData); + } + + return sessionData || reactiveSessionData; } getResults() { @@ -72,33 +156,85 @@ export class CardSearchPagedComponent extends BlazeComponent { // console.log('getting results'); this.sessionData = this.getSessionData(); // eslint-disable-next-line no-console - console.log('session data:', this.sessionData); + if (process.env.DEBUG === 'true') { + console.log('getResults - sessionId:', this.sessionId); + console.log('getResults - session data:', this.sessionData); + } const cards = []; - this.sessionData.cards.forEach(cardId => { - cards.push(ReactiveCache.getCard(cardId)); - }); - this.queryErrors = this.sessionData.errors; + + if (this.sessionData && this.sessionData.cards) { + if (process.env.DEBUG === 'true') { + console.log('getResults - cards array length:', this.sessionData.cards.length); + } + this.sessionData.cards.forEach(cardId => { + const card = ReactiveCache.getCard(cardId); + if (process.env.DEBUG === 'true') { + console.log('getResults - card:', cardId, card); + } + cards.push(card); + }); + this.queryErrors = this.sessionData.errors || []; + } else { + if (process.env.DEBUG === 'true') { + console.log('getResults - no sessionData or no cards array, trying direct card search'); + } + // Fallback: try to get cards directly from the client-side collection + const selector = { + type: 'cardType-card', + dueAt: { $exists: true, $nin: [null, ''] } + }; + const allCards = Cards.find(selector).fetch(); + if (process.env.DEBUG === 'true') { + console.log('getResults - direct card search found:', allCards ? allCards.length : 0, 'cards'); + } + + if (allCards && allCards.length > 0) { + allCards.forEach(card => { + if (card && card._id) { + if (process.env.DEBUG === 'true') { + console.log('getResults - direct card:', card._id, card.title); + } + cards.push(card); + } + }); + } + + this.queryErrors = []; + } if (this.queryErrors.length) { // console.log('queryErrors:', this.queryErrorMessages()); this.hasQueryErrors.set(true); // return null; } - this.debug.set(new QueryDebug(this.sessionData.debug)); - console.log('debug:', this.debug.get().get()); - console.log('debug.show():', this.debug.get().show()); - console.log('debug.showSelector():', this.debug.get().showSelector()); + this.debug.set(new QueryDebug(this.sessionData ? this.sessionData.debug : null)); + if (process.env.DEBUG === 'true') { + console.log('debug:', this.debug.get().get()); + console.log('debug.show():', this.debug.get().show()); + console.log('debug.showSelector():', this.debug.get().showSelector()); + } if (cards) { - this.totalHits = this.sessionData.totalHits; - this.resultsCount = cards.length; - this.resultsStart = this.sessionData.lastHit - this.resultsCount + 1; - this.resultsEnd = this.sessionData.lastHit; - this.resultsHeading.set(this.getResultsHeading()); - this.results.set(cards); - this.hasNextPage.set(this.sessionData.lastHit < this.sessionData.totalHits); - this.hasPreviousPage.set( - this.sessionData.lastHit - this.sessionData.resultsCount > 0, - ); + if (this.sessionData) { + this.totalHits = this.sessionData.totalHits || 0; + this.resultsCount = cards.length; + this.resultsStart = this.sessionData.lastHit - this.resultsCount + 1; + this.resultsEnd = this.sessionData.lastHit; + this.resultsHeading.set(this.getResultsHeading()); + this.results.set(cards); + this.hasNextPage.set(this.sessionData.lastHit < this.sessionData.totalHits); + this.hasPreviousPage.set( + this.sessionData.lastHit - this.sessionData.resultsCount > 0, + ); + } else { + this.totalHits = cards.length; + this.resultsCount = cards.length; + this.resultsStart = 1; + this.resultsEnd = cards.length; + this.resultsHeading.set(this.getResultsHeading()); + this.results.set(cards); + this.hasNextPage.set(false); + this.hasPreviousPage.set(false); + } return cards; } @@ -113,13 +249,29 @@ export class CardSearchPagedComponent extends BlazeComponent { } getSubscription(queryParams) { - return Meteor.subscribe( + if (process.env.DEBUG === 'true') { + console.log('Subscribing to globalSearch with:', { + sessionId: this.sessionId, + params: queryParams.params, + text: queryParams.text + }); + } + + // Subscribe to both globalSearch and sessionData + const globalSearchHandle = Meteor.subscribe( 'globalSearch', this.sessionId, queryParams.params, queryParams.text, this.subscriptionCallbacks, ); + + const sessionDataHandle = Meteor.subscribe('sessionData', this.sessionId); + if (process.env.DEBUG === 'true') { + console.log('Subscribed to sessionData with sessionId:', this.sessionId); + } + + return globalSearchHandle; } runGlobalSearch(queryParams) { diff --git a/server/publications/cards.js b/server/publications/cards.js index 8db1a542b..a47ec5f63 100644 --- a/server/publications/cards.js +++ b/server/publications/cards.js @@ -147,13 +147,31 @@ Meteor.publish('globalSearch', function(sessionId, params, text) { check(params, Object); check(text, String); - // eslint-disable-next-line no-console - // console.log('queryParams:', params); + if (process.env.DEBUG === 'true') { + console.log('globalSearch publication called with:', { sessionId, params, text }); + } const ret = findCards(sessionId, buildQuery(new QueryParams(params, text))); + if (process.env.DEBUG === 'true') { + console.log('globalSearch publication returning:', ret); + } return ret; }); +Meteor.publish('sessionData', function(sessionId) { + check(sessionId, String); + const userId = Meteor.userId(); + if (process.env.DEBUG === 'true') { + console.log('sessionData publication called with:', { sessionId, userId }); + } + + const cursor = SessionData.find({ userId, sessionId }); + if (process.env.DEBUG === 'true') { + console.log('sessionData publication returning cursor with count:', cursor.count()); + } + return cursor; +}); + function buildSelector(queryParams) { const userId = Meteor.userId(); @@ -261,8 +279,12 @@ function buildSelector(queryParams) { selector.archived = false; } } else { + const userBoardIds = Boards.userBoardIds(userId, null, boardsSelector); + if (process.env.DEBUG === 'true') { + console.log('buildSelector - userBoardIds:', userBoardIds); + } selector.boardId = { - $in: Boards.userBoardIds(userId, null, boardsSelector), + $in: userBoardIds, }; } if (endAt !== null) { @@ -537,8 +559,9 @@ function buildSelector(queryParams) { } } - // eslint-disable-next-line no-console - // console.log('cards selector:', JSON.stringify(selector, null, 2)); + if (process.env.DEBUG === 'true') { + console.log('buildSelector - final selector:', JSON.stringify(selector, null, 2)); + } const query = new Query(); query.selector = selector; @@ -702,14 +725,17 @@ function findCards(sessionId, query) { const userId = Meteor.userId(); // eslint-disable-next-line no-console - // console.log('selector:', query.selector); - // console.log('selector.$and:', query.selector.$and); - // eslint-disable-next-line no-console - // console.log('projection:', query.projection); + if (process.env.DEBUG === 'true') { + console.log('findCards - userId:', userId); + console.log('findCards - selector:', JSON.stringify(query.selector, null, 2)); + console.log('findCards - selector.$and:', query.selector.$and); + console.log('findCards - projection:', query.projection); + } const cards = ReactiveCache.getCards(query.selector, query.projection, true); - // eslint-disable-next-line no-console - // console.log('count:', cards.count()); + if (process.env.DEBUG === 'true') { + console.log('findCards - cards count:', cards ? cards.count() : 0); + } const update = { $set: { @@ -720,7 +746,8 @@ function findCards(sessionId, query) { selector: SessionData.pickle(query.selector), projection: SessionData.pickle(query.projection), errors: query.errors(), - debug: query.getQueryParams().getPredicate(OPERATOR_DEBUG) + debug: query.getQueryParams().getPredicate(OPERATOR_DEBUG), + modifiedAt: new Date() }, }; @@ -736,13 +763,22 @@ function findCards(sessionId, query) { update.$set.resultsCount = update.$set.cards.length; } - // eslint-disable-next-line no-console - // console.log('sessionId:', sessionId); - // eslint-disable-next-line no-console - // console.log('userId:', userId); - // eslint-disable-next-line no-console - // console.log('update:', update); - SessionData.upsert({ userId, sessionId }, update); + if (process.env.DEBUG === 'true') { + console.log('findCards - sessionId:', sessionId); + console.log('findCards - userId:', userId); + console.log('findCards - update:', JSON.stringify(update, null, 2)); + } + const upsertResult = SessionData.upsert({ userId, sessionId }, update); + if (process.env.DEBUG === 'true') { + console.log('findCards - upsertResult:', upsertResult); + } + + // Check if the session data was actually stored + const storedSessionData = SessionData.findOne({ userId, sessionId }); + if (process.env.DEBUG === 'true') { + console.log('findCards - stored session data:', storedSessionData); + console.log('findCards - stored session data count:', storedSessionData ? 1 : 0); + } // remove old session data SessionData.remove({ @@ -793,6 +829,21 @@ function findCards(sessionId, query) { type: 1, }; + // Add a small delay to ensure the session data is committed to the database + Meteor.setTimeout(() => { + const sessionDataCursor = SessionData.find({ userId, sessionId }); + if (process.env.DEBUG === 'true') { + console.log('findCards - publishing session data cursor (after delay):', sessionDataCursor); + console.log('findCards - session data count (after delay):', sessionDataCursor.count()); + } + }, 100); + + const sessionDataCursor = SessionData.find({ userId, sessionId }); + if (process.env.DEBUG === 'true') { + console.log('findCards - publishing session data cursor:', sessionDataCursor); + console.log('findCards - session data count:', sessionDataCursor.count()); + } + return [ cards, ReactiveCache.getBoards( @@ -812,9 +863,14 @@ function findCards(sessionId, query) { ReactiveCache.getChecklistItems({ cardId: { $in: cards.map(c => c._id) } }, {}, true), ReactiveCache.getAttachments({ 'meta.cardId': { $in: cards.map(c => c._id) } }, {}, true).cursor, ReactiveCache.getCardComments({ cardId: { $in: cards.map(c => c._id) } }, {}, true), - SessionData.find({ userId, sessionId }), + sessionDataCursor, ]; } - return [SessionData.find({ userId, sessionId })]; + const sessionDataCursor = SessionData.find({ userId, sessionId }); + if (process.env.DEBUG === 'true') { + console.log('findCards - publishing session data cursor (no cards):', sessionDataCursor); + console.log('findCards - session data count (no cards):', sessionDataCursor.count()); + } + return [sessionDataCursor]; } From 32571106730e572770fbb6737c5592fdc5b4bdf8 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 19 Oct 2025 20:07:39 +0300 Subject: [PATCH 012/183] Updated ChangeLog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcee98f39..2e527f067 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,8 @@ This release fixes the following bugs: [Part 1](https://github.com/wekan/wekan/commit/d965faa3174dc81636106e6f81435b2750b0625f), [Part 2](https://github.com/wekan/wekan/commit/101048339bdd1e45f876aeb1aa5ec32ceda28139). Thanks to xet7. +- [Fix unable to see My Due Cards](https://github.com/wekan/wekan/commit/66b444e2b0c9b2ed5f98cd1ff0cd9222b2d0c624). + Thanks to xet7. Thanks to above GitHub users for their contributions and translators for their translations. From 324f3f7794aace800022a24deb5fd5fb36ebd384 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 19 Oct 2025 21:38:55 +0300 Subject: [PATCH 013/183] Fix drag drop lists. Part 1. Thanks to xet7 ! Related #5951 --- client/components/boards/boardBody.js | 28 ++- client/components/boards/boardsList.jade | 16 +- client/components/cards/checklists.jade | 3 +- client/components/cards/minicard.jade | 9 +- client/components/lists/list.css | 5 + client/components/lists/list.js | 25 +-- client/components/lists/listHeader.jade | 6 +- .../components/swimlanes/swimlaneHeader.jade | 5 +- client/components/swimlanes/swimlanes.js | 194 +++++++++++++++--- client/lib/utils.js | 24 +-- .../fullcalendar/fullcalendar.js | 4 +- 11 files changed, 210 insertions(+), 109 deletions(-) diff --git a/client/components/boards/boardBody.js b/client/components/boards/boardBody.js index cb13abebd..56c38f590 100644 --- a/client/components/boards/boardBody.js +++ b/client/components/boards/boardBody.js @@ -550,22 +550,20 @@ BlazeComponent.extendComponent({ // Always reset dragscroll on view switch dragscroll.reset(); - if (Utils.isTouchScreenOrShowDesktopDragHandles()) { - $swimlanesDom.sortable({ - handle: '.js-swimlane-header-handle', - }); - } else { - $swimlanesDom.sortable({ - handle: '.swimlane-header', - }); - } + if ($swimlanesDom.data('uiSortable') || $swimlanesDom.data('sortable')) { + if (Utils.isTouchScreenOrShowDesktopDragHandles()) { + $swimlanesDom.sortable('option', 'handle', '.js-swimlane-header-handle'); + } else { + $swimlanesDom.sortable('option', 'handle', '.swimlane-header'); + } - // Disable drag-dropping if the current user is not a board member - $swimlanesDom.sortable( - 'option', - 'disabled', - !ReactiveCache.getCurrentUser()?.isBoardAdmin(), - ); + // Disable drag-dropping if the current user is not a board member + $swimlanesDom.sortable( + 'option', + 'disabled', + !ReactiveCache.getCurrentUser()?.isBoardAdmin(), + ); + } }); // If there is no data in the board (ie, no lists) we autofocus the list diff --git a/client/components/boards/boardsList.jade b/client/components/boards/boardsList.jade index b612c4019..3d01c19c9 100644 --- a/client/components/boards/boardsList.jade +++ b/client/components/boards/boardsList.jade @@ -64,11 +64,9 @@ template(name="boardList") i.fa.js-has-spenttime-cards( class="fa-circle{{#if hasOvertimeCards}} has-overtime-card-active{{else}} no-overtime-card-active{{/if}}" title="{{#if hasOvertimeCards}}{{_ 'has-overtime-cards'}}{{else}}{{_ 'has-spenttime-cards'}}{{/if}}") - if isTouchScreenOrShowDesktopDragHandles - i.fa.board-handle( - class="fa-arrows" - title="{{_ 'drag-board'}}") - else + i.fa.board-handle( + class="fa-arrows" + title="{{_ 'drag-board'}}") if isSandstorm i.fa.js-clone-board( class="fa-clone" @@ -119,11 +117,9 @@ template(name="boardList") i.fa.js-has-spenttime-cards( class="fa-circle{{#if hasOvertimeCards}} has-overtime-card-active{{else}} no-overtime-card-active{{/if}}" title="{{#if hasOvertimeCards}}{{_ 'has-overtime-cards'}}{{else}}{{_ 'has-spenttime-cards'}}{{/if}}") - if isTouchScreenOrShowDesktopDragHandles - i.fa.board-handle( - class="fa-arrows" - title="{{_ 'drag-board'}}") - else + i.fa.board-handle( + class="fa-arrows" + title="{{_ 'drag-board'}}") if isSandstorm a.js-clone-board( class="fa-clone" diff --git a/client/components/cards/checklists.jade b/client/components/cards/checklists.jade index 3adccaf43..82e4a1a6e 100644 --- a/client/components/cards/checklists.jade +++ b/client/components/cards/checklists.jade @@ -125,8 +125,7 @@ template(name='checklistItemDetail') if canModifyCard .check-box-container .check-box.materialCheckBox(class="{{#if item.isFinished }}is-checked{{/if}}") - if isTouchScreenOrShowDesktopDragHandles - span.fa.checklistitem-handle(class="fa-arrows" title="{{_ 'dragChecklistItem'}}") + span.fa.checklistitem-handle(class="fa-arrows" title="{{_ 'dragChecklistItem'}}") .item-title.js-open-inlined-form.is-editable(class="{{#if item.isFinished }}is-checked{{/if}}") +viewer = item.title diff --git a/client/components/cards/minicard.jade b/client/components/cards/minicard.jade index 402b651c5..9721bc484 100644 --- a/client/components/cards/minicard.jade +++ b/client/components/cards/minicard.jade @@ -4,12 +4,9 @@ template(name="minicard") class="{{#if isLinkedBoard}}linked-board{{/if}}" class="{{#if colorClass}}minicard-{{colorClass}}{{/if}}") if canModifyCard - if isTouchScreenOrShowDesktopDragHandles - a.minicard-details-menu-with-handle.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}") | ☰ - .handle - | ↔️ - else - a.minicard-details-menu.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}") | ☰ + a.minicard-details-menu-with-handle.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}") | ☰ + .handle + | ↕️ .dates if getReceived .date diff --git a/client/components/lists/list.css b/client/components/lists/list.css index 8b76046ad..68fd62270 100644 --- a/client/components/lists/list.css +++ b/client/components/lists/list.css @@ -191,6 +191,11 @@ body.list-resizing-active * { margin-right: 0 !important; /* Ensure proper display */ display: inline-block !important; + /* Ensure it's clickable and shows proper cursor */ + cursor: move !important; + pointer-events: auto !important; + /* Add some padding for better clickability */ + padding: 4px !important; } /* Ensure buttons maintain original positioning */ diff --git a/client/components/lists/list.js b/client/components/lists/list.js index da7c16e16..b80ecff84 100644 --- a/client/components/lists/list.js +++ b/client/components/lists/list.js @@ -150,26 +150,13 @@ BlazeComponent.extendComponent({ }); this.autorun(() => { - if (Utils.isTouchScreenOrShowDesktopDragHandles()) { - $cards.sortable({ - handle: '.handle', - }); - } else { - $cards.sortable({ - handle: '.minicard', - // Prevent sortable from interfering with file drag and drop - start: function(event, ui) { - // Check if this is a file drag operation - const dataTransfer = event.originalEvent?.dataTransfer; - if (dataTransfer && dataTransfer.types && dataTransfer.types.includes('Files')) { - // Cancel sortable for file drags - return false; - } - }, - }); - } - if ($cards.data('uiSortable') || $cards.data('sortable')) { + if (Utils.isTouchScreenOrShowDesktopDragHandles()) { + $cards.sortable('option', 'handle', '.handle'); + } else { + $cards.sortable('option', 'handle', '.minicard'); + } + $cards.sortable( 'option', 'disabled', diff --git a/client/components/lists/listHeader.jade b/client/components/lists/listHeader.jade index db5f86c2f..1aa7f2820 100644 --- a/client/components/lists/listHeader.jade +++ b/client/components/lists/listHeader.jade @@ -70,9 +70,9 @@ template(name="listHeader") | ⬅️ | ➡️ a.js-open-list-menu(title="{{_ 'listActionPopup-title'}}") | ☰ - if currentUser.isBoardAdmin - if isTouchScreenOrShowDesktopDragHandles - a.list-header-handle.handle.js-list-handle | ↔️ + if currentUser.isBoardMember + unless currentUser.isCommentOnly + a.list-header-handle.handle.js-list-handle | ↕️ template(name="editListTitleForm") .list-composer diff --git a/client/components/swimlanes/swimlaneHeader.jade b/client/components/swimlanes/swimlaneHeader.jade index bd9245e05..78fc9d4af 100644 --- a/client/components/swimlanes/swimlaneHeader.jade +++ b/client/components/swimlanes/swimlaneHeader.jade @@ -38,9 +38,8 @@ template(name="swimlaneFixedHeader") // ⬆️.swimlane-header-collapse-up // i.fa.fa-arrow-down.swimlane-header-collapse-down unless isTouchScreen - if isShowDesktopDragHandles - a.swimlane-header-handle.handle.js-swimlane-header-handle - | ↕️ + a.swimlane-header-handle.handle.js-swimlane-header-handle + | ↕️ if isTouchScreen a.swimlane-header-miniscreen-handle.handle.js-swimlane-header-handle | ↕️ diff --git a/client/components/swimlanes/swimlanes.js b/client/components/swimlanes/swimlanes.js index dc149c48c..115138f76 100644 --- a/client/components/swimlanes/swimlanes.js +++ b/client/components/swimlanes/swimlanes.js @@ -1,4 +1,5 @@ import { ReactiveCache } from '/imports/reactiveCache'; +import dragscroll from '@wekanteam/dragscroll'; const { calculateIndex } = Utils; function currentListIsInThisSwimlane(swimlaneId) { @@ -43,6 +44,20 @@ function currentCardIsInThisList(listId, swimlaneId) { } function initSortable(boardComponent, $listsDom) { + // Safety check: ensure we have valid DOM elements + if (!$listsDom || $listsDom.length === 0) { + console.error('initSortable: No valid DOM elements provided'); + return; + } + + // Check if sortable is already initialized + if ($listsDom.data('uiSortable') || $listsDom.data('sortable')) { + console.log('initSortable: Sortable already initialized, skipping'); + return; + } + + console.log('initSortable: Initializing sortable for', $listsDom.length, 'elements'); + // We want to animate the card details window closing. We rely on CSS // transition for the actual animation. $listsDom._uihooks = { @@ -62,20 +77,74 @@ function initSortable(boardComponent, $listsDom) { }, }; - $listsDom.sortable({ - connectWith: '.js-swimlane, .js-lists', - tolerance: 'pointer', - helper: 'clone', - items: '.js-list:not(.js-list-composer)', - placeholder: 'js-list placeholder', - distance: 7, + console.log('Initializing list sortable with', $listsDom.length, 'elements'); + console.log('Connected elements:', $('.js-swimlane, .js-lists').length); + console.log('Available swimlanes:', $('.js-swimlane').map(function() { return $(this).attr('id'); }).get()); + + // Add click debugging for drag handles + $listsDom.on('mousedown', '.js-list-handle', function(e) { + console.log('List drag handle clicked!', e.target); + e.stopPropagation(); + }); + + $listsDom.on('mousedown', '.js-list-header', function(e) { + console.log('List header clicked!', e.target); + }); + + // Add debugging for sortable events + $listsDom.on('sortstart', function(e, ui) { + console.log('Sortable start event fired!', ui.item); + }); + + $listsDom.on('sortbeforestop', function(e, ui) { + console.log('Sortable beforeStop event fired!', ui.item); + }); + + $listsDom.on('sortstop', function(e, ui) { + console.log('Sortable stop event fired!', ui.item); + }); + + try { + $listsDom.sortable({ + connectWith: '.js-swimlane, .js-lists', + tolerance: 'pointer', + appendTo: '.board-canvas', + helper(evt, item) { + const helper = item.clone(); + helper.css('z-index', 1000); + return helper; + }, + items: '.js-list:not(.js-list-composer)', + placeholder: 'list placeholder', + distance: 3, + forcePlaceholderSize: true, + cursor: 'move', start(evt, ui) { + console.log('List drag started'); + ui.helper.css('z-index', 1000); ui.placeholder.height(ui.helper.height()); ui.placeholder.width(ui.helper.width()); EscapeActions.executeUpTo('popup-close'); boardComponent.setIsDragging(true); + + // Add visual feedback for list being dragged + ui.item.addClass('ui-sortable-helper'); + + // Disable dragscroll during list dragging to prevent interference + console.log('Disabling dragscroll for list dragging'); + dragscroll.reset(); + + // Also disable dragscroll on all swimlanes during list dragging + $('.js-swimlane').each(function() { + $(this).removeClass('dragscroll'); + }); + }, + beforeStop(evt, ui) { + // Clean up visual feedback + ui.item.removeClass('ui-sortable-helper'); }, stop(evt, ui) { + console.log('List drag stopped'); // To attribute the new index number, we need to get the DOM element // of the previous and the following card -- if any. const prevListDom = ui.item.prev('.js-list').get(0); @@ -83,15 +152,39 @@ function initSortable(boardComponent, $listsDom) { const sortIndex = calculateIndex(prevListDom, nextListDom, 1); const listDomElement = ui.item.get(0); - const list = Blaze.getData(listDomElement); + if (!listDomElement) { + console.error('List DOM element not found during drag stop'); + return; + } + + let list; + try { + list = Blaze.getData(listDomElement); + } catch (error) { + console.error('Error getting list data:', error); + return; + } + + if (!list) { + console.error('List data not found for element:', listDomElement); + return; + } // Detect if the list was dropped in a different swimlane const targetSwimlaneDom = ui.item.closest('.js-swimlane'); let targetSwimlaneId = null; + console.log('Target swimlane DOM:', targetSwimlaneDom.length, targetSwimlaneDom.attr('id')); + if (targetSwimlaneDom.length > 0) { // List was dropped in a swimlane - targetSwimlaneId = targetSwimlaneDom.attr('id').replace('swimlane-', ''); + try { + targetSwimlaneId = targetSwimlaneDom.attr('id').replace('swimlane-', ''); + console.log('List dropped in swimlane:', targetSwimlaneId); + } catch (error) { + console.error('Error getting target swimlane ID:', error); + return; + } } else { // List was dropped in lists view (not swimlanes view) // In this case, assign to the default swimlane @@ -100,6 +193,7 @@ function initSortable(boardComponent, $listsDom) { const defaultSwimlane = currentBoard.getDefaultSwimline(); if (defaultSwimlane) { targetSwimlaneId = defaultSwimlane._id; + console.log('List dropped in lists view, using default swimlane:', targetSwimlaneId); } } } @@ -123,6 +217,11 @@ function initSortable(boardComponent, $listsDom) { // Check if the list was dropped in a different swimlane const isDifferentSwimlane = targetSwimlaneId && targetSwimlaneId !== originalSwimlaneId; + console.log('Cross-swimlane check:', { + originalSwimlaneId, + targetSwimlaneId, + isDifferentSwimlane + }); // If the list was dropped in a different swimlane, update the swimlaneId if (isDifferentSwimlane) { @@ -152,34 +251,63 @@ function initSortable(boardComponent, $listsDom) { $listsDom.sortable('cancel'); } - Lists.update(list._id, { - $set: updateData, - }); + try { + Lists.update(list._id, { + $set: updateData, + }); + console.log('List updated successfully:', list._id, updateData); + } catch (error) { + console.error('Error updating list:', error); + return; + } boardComponent.setIsDragging(false); + + // Re-enable dragscroll after list dragging is complete + console.log('Re-enabling dragscroll after list dragging'); + dragscroll.reset(); + + // Re-enable dragscroll on all swimlanes + $('.js-swimlane').each(function() { + $(this).addClass('dragscroll'); + }); }, }); + } catch (error) { + console.error('Error initializing list sortable:', error); + return; + } + + console.log('List sortable initialization completed successfully'); + console.log('Sortable data after init:', $listsDom.data('uiSortable') || $listsDom.data('sortable')); boardComponent.autorun(() => { - if (Utils.isTouchScreenOrShowDesktopDragHandles()) { - $listsDom.sortable({ - handle: '.js-list-handle', - connectWith: '.js-swimlane, .js-lists', - }); - } else { - $listsDom.sortable({ - handle: '.js-list-header', - connectWith: '.js-swimlane, .js-lists', - }); - } - const $listDom = $listsDom; + console.log('Autorun running, checking sortable status...'); + console.log('Has uiSortable:', $listDom.data('uiSortable')); + console.log('Has sortable:', $listDom.data('sortable')); if ($listDom.data('uiSortable') || $listDom.data('sortable')) { - $listsDom.sortable( - 'option', - 'disabled', - !ReactiveCache.getCurrentUser()?.isBoardAdmin(), - ); + if (Utils.isTouchScreenOrShowDesktopDragHandles()) { + $listsDom.sortable('option', 'handle', '.js-list-handle'); + console.log('List sortable: Using .js-list-handle as handle'); + console.log('Found drag handles:', $listsDom.find('.js-list-handle').length); + } else { + $listsDom.sortable('option', 'handle', '.js-list-header'); + console.log('List sortable: Using .js-list-header as handle'); + console.log('Found list headers:', $listsDom.find('.js-list-header').length); + } + + const currentUser = ReactiveCache.getCurrentUser(); + const canModify = Utils.canModifyBoard(); + const isDisabled = !canModify; + $listsDom.sortable('option', 'disabled', isDisabled); + console.log('List sortable disabled:', isDisabled); + console.log('Can modify board:', canModify); + console.log('Current user:', currentUser ? currentUser.username : 'null'); + console.log('Is board member:', currentUser ? currentUser.isBoardMember() : 'null'); + console.log('Is comment only:', currentUser ? currentUser.isCommentOnly() : 'null'); + } else { + console.log('List sortable not initialized yet'); } }); } @@ -188,11 +316,15 @@ BlazeComponent.extendComponent({ onRendered() { const boardComponent = this.parentComponent(); const $listsDom = this.$('.js-lists'); + + console.log('Swimlane onRendered - DOM elements found:', $listsDom.length); + console.log('Swimlane onRendered - DOM element:', $listsDom[0]); if (!Utils.getCurrentCardId()) { boardComponent.scrollLeft(); } + console.log('Calling initSortable...'); initSortable(boardComponent, $listsDom); }, onCreated() { @@ -538,11 +670,15 @@ BlazeComponent.extendComponent({ onRendered() { const boardComponent = this.parentComponent(); const $listsDom = this.$('.js-lists'); + + console.log('ListsGroup onRendered - DOM elements found:', $listsDom.length); + console.log('ListsGroup onRendered - DOM element:', $listsDom[0]); if (!Utils.getCurrentCardId()) { boardComponent.scrollLeft(); } + console.log('ListsGroup calling initSortable...'); initSortable(boardComponent, $listsDom); }, }).register('listsGroup'); diff --git a/client/lib/utils.js b/client/lib/utils.js index 503e998fc..c979c1773 100644 --- a/client/lib/utils.js +++ b/client/lib/utils.js @@ -496,30 +496,14 @@ Utils = { // returns if desktop drag handles are enabled isShowDesktopDragHandles() { - if (this.isTouchScreen()) { - return true; - /* - const currentUser = ReactiveCache.getCurrentUser(); - if (currentUser) { - return (currentUser.profile || {}).showDesktopDragHandles; - } else if (window.localStorage.getItem('showDesktopDragHandles')) { - // - if (window.localStorage.getItem('showDesktopDragHandles')) { - return true; - } else { - return false; - */ - } else { - return false; - } + // Always show drag handles on all displays + return true; }, // returns if mini screen or desktop drag handles isTouchScreenOrShowDesktopDragHandles() { - // Always enable drag handles for mobile screens (touch devices) - return this.isTouchScreen() || this.isMiniScreen(); - //return this.isTouchScreen() || this.isShowDesktopDragHandles(); - //return this.isShowDesktopDragHandles(); + // Always enable drag handles for all displays + return true; }, calculateIndexData(prevData, nextData, nItems = 1) { diff --git a/packages/wekan-fullcalendar/fullcalendar/fullcalendar.js b/packages/wekan-fullcalendar/fullcalendar/fullcalendar.js index a06cae5de..6caf88b04 100644 --- a/packages/wekan-fullcalendar/fullcalendar/fullcalendar.js +++ b/packages/wekan-fullcalendar/fullcalendar/fullcalendar.js @@ -2509,14 +2509,14 @@ var GlobalEmitter = /** @class */ (function () { // http://stackoverflow.com/a/32954565/96342 window.addEventListener('scroll', this.handleScrollProxy = function (ev) { _this.handleScroll($.Event(ev)); - }, true // useCapture + }, { passive: true, capture: true } // useCapture with passive for better performance ); }; GlobalEmitter.prototype.unbind = function () { this.stopListeningTo($(document)); window.removeEventListener('touchmove', this.handleTouchMoveProxy, { passive: false } // use same options as addEventListener ); - window.removeEventListener('scroll', this.handleScrollProxy, true // useCapture + window.removeEventListener('scroll', this.handleScrollProxy, { passive: true, capture: true } // use same options as addEventListener ); }; // Touch Handlers From cd3576b9950d62bd6a80e6ba4f46961d8f386ae3 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 19 Oct 2025 21:41:44 +0300 Subject: [PATCH 014/183] Updated ChangeLog. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e527f067..644c28dd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,9 @@ This release fixes the following bugs: Thanks to xet7. - [Fix unable to see My Due Cards](https://github.com/wekan/wekan/commit/66b444e2b0c9b2ed5f98cd1ff0cd9222b2d0c624). Thanks to xet7. +- Fix drag drop lists. + [Part 1](https://github.com/wekan/wekan/commit/324f3f7794aace800022a24deb5fd5fb36ebd384). + Thanks to xet7. Thanks to above GitHub users for their contributions and translators for their translations. From caa6e615ff3c3681bf2b470a625eb39c6009b825 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 19 Oct 2025 21:46:14 +0300 Subject: [PATCH 015/183] Removed extra pipe characters. Thanks to xet7 ! --- client/components/cards/minicard.jade | 2 +- client/components/lists/listHeader.jade | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/components/cards/minicard.jade b/client/components/cards/minicard.jade index 9721bc484..43c8db9bd 100644 --- a/client/components/cards/minicard.jade +++ b/client/components/cards/minicard.jade @@ -4,7 +4,7 @@ template(name="minicard") class="{{#if isLinkedBoard}}linked-board{{/if}}" class="{{#if colorClass}}minicard-{{colorClass}}{{/if}}") if canModifyCard - a.minicard-details-menu-with-handle.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}") | ☰ + a.minicard-details-menu-with-handle.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}") ☰ .handle | ↕️ .dates diff --git a/client/components/lists/listHeader.jade b/client/components/lists/listHeader.jade index 1aa7f2820..8f0c0d244 100644 --- a/client/components/lists/listHeader.jade +++ b/client/components/lists/listHeader.jade @@ -65,14 +65,14 @@ template(name="listHeader") //if isBoardAdmin // a.fa.js-list-star.list-header-plus-top(class="fa-star{{#unless starred}}-o{{/unless}}") if canSeeAddCard - a.js-add-card.list-header-plus-top(title="{{_ 'add-card-to-top-of-list'}}") | ➕ + a.js-add-card.list-header-plus-top(title="{{_ 'add-card-to-top-of-list'}}") ➕ a.js-collapse(title="{{_ 'collapse'}}") | ⬅️ | ➡️ - a.js-open-list-menu(title="{{_ 'listActionPopup-title'}}") | ☰ + a.js-open-list-menu(title="{{_ 'listActionPopup-title'}}") ☰ if currentUser.isBoardMember unless currentUser.isCommentOnly - a.list-header-handle.handle.js-list-handle | ↕️ + a.list-header-handle.handle.js-list-handle ↕️ template(name="editListTitleForm") .list-composer From 0d36abee4edad36b53b067ac9f40b812c9d070c0 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 19 Oct 2025 21:47:51 +0300 Subject: [PATCH 016/183] Updated ChangeLog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 644c28dd9..4bb3b7eba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,8 @@ This release fixes the following bugs: - Fix drag drop lists. [Part 1](https://github.com/wekan/wekan/commit/324f3f7794aace800022a24deb5fd5fb36ebd384). Thanks to xet7. +- [Removed extra pipe characters](https://github.com/wekan/wekan/commit/caa6e615ff3c3681bf2b470a625eb39c6009b825). + Thanks to xet7. Thanks to above GitHub users for their contributions and translators for their translations. From 55bec31a3f7189500ecb7926933e566ae84c978a Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 19 Oct 2025 22:20:52 +0300 Subject: [PATCH 017/183] Fix drag drop lists. Part 2. Thanks to xet7 ! Fixes #5951 --- client/components/swimlanes/swimlanes.js | 284 +++++++++++++++++------ 1 file changed, 212 insertions(+), 72 deletions(-) diff --git a/client/components/swimlanes/swimlanes.js b/client/components/swimlanes/swimlanes.js index 115138f76..398243683 100644 --- a/client/components/swimlanes/swimlanes.js +++ b/client/components/swimlanes/swimlanes.js @@ -2,6 +2,8 @@ import { ReactiveCache } from '/imports/reactiveCache'; import dragscroll from '@wekanteam/dragscroll'; const { calculateIndex } = Utils; + + function currentListIsInThisSwimlane(swimlaneId) { const currentList = Utils.getCurrentList(); return ( @@ -52,11 +54,9 @@ function initSortable(boardComponent, $listsDom) { // Check if sortable is already initialized if ($listsDom.data('uiSortable') || $listsDom.data('sortable')) { - console.log('initSortable: Sortable already initialized, skipping'); - return; + $listsDom.sortable('destroy'); } - console.log('initSortable: Initializing sortable for', $listsDom.length, 'elements'); // We want to animate the card details window closing. We rely on CSS // transition for the actual animation. @@ -77,31 +77,27 @@ function initSortable(boardComponent, $listsDom) { }, }; - console.log('Initializing list sortable with', $listsDom.length, 'elements'); - console.log('Connected elements:', $('.js-swimlane, .js-lists').length); - console.log('Available swimlanes:', $('.js-swimlane').map(function() { return $(this).attr('id'); }).get()); // Add click debugging for drag handles $listsDom.on('mousedown', '.js-list-handle', function(e) { - console.log('List drag handle clicked!', e.target); e.stopPropagation(); }); $listsDom.on('mousedown', '.js-list-header', function(e) { - console.log('List header clicked!', e.target); + }); + + // Add debugging for any mousedown on lists + $listsDom.on('mousedown', '.js-list', function(e) { }); // Add debugging for sortable events $listsDom.on('sortstart', function(e, ui) { - console.log('Sortable start event fired!', ui.item); }); $listsDom.on('sortbeforestop', function(e, ui) { - console.log('Sortable beforeStop event fired!', ui.item); }); $listsDom.on('sortstop', function(e, ui) { - console.log('Sortable stop event fired!', ui.item); }); try { @@ -120,7 +116,6 @@ function initSortable(boardComponent, $listsDom) { forcePlaceholderSize: true, cursor: 'move', start(evt, ui) { - console.log('List drag started'); ui.helper.css('z-index', 1000); ui.placeholder.height(ui.helper.height()); ui.placeholder.width(ui.helper.width()); @@ -131,8 +126,10 @@ function initSortable(boardComponent, $listsDom) { ui.item.addClass('ui-sortable-helper'); // Disable dragscroll during list dragging to prevent interference - console.log('Disabling dragscroll for list dragging'); - dragscroll.reset(); + try { + dragscroll.reset(); + } catch (e) { + } // Also disable dragscroll on all swimlanes during list dragging $('.js-swimlane').each(function() { @@ -144,7 +141,6 @@ function initSortable(boardComponent, $listsDom) { ui.item.removeClass('ui-sortable-helper'); }, stop(evt, ui) { - console.log('List drag stopped'); // To attribute the new index number, we need to get the DOM element // of the previous and the following card -- if any. const prevListDom = ui.item.prev('.js-list').get(0); @@ -174,13 +170,11 @@ function initSortable(boardComponent, $listsDom) { const targetSwimlaneDom = ui.item.closest('.js-swimlane'); let targetSwimlaneId = null; - console.log('Target swimlane DOM:', targetSwimlaneDom.length, targetSwimlaneDom.attr('id')); if (targetSwimlaneDom.length > 0) { // List was dropped in a swimlane try { targetSwimlaneId = targetSwimlaneDom.attr('id').replace('swimlane-', ''); - console.log('List dropped in swimlane:', targetSwimlaneId); } catch (error) { console.error('Error getting target swimlane ID:', error); return; @@ -193,7 +187,6 @@ function initSortable(boardComponent, $listsDom) { const defaultSwimlane = currentBoard.getDefaultSwimline(); if (defaultSwimlane) { targetSwimlaneId = defaultSwimlane._id; - console.log('List dropped in lists view, using default swimlane:', targetSwimlaneId); } } } @@ -217,18 +210,10 @@ function initSortable(boardComponent, $listsDom) { // Check if the list was dropped in a different swimlane const isDifferentSwimlane = targetSwimlaneId && targetSwimlaneId !== originalSwimlaneId; - console.log('Cross-swimlane check:', { - originalSwimlaneId, - targetSwimlaneId, - isDifferentSwimlane - }); // If the list was dropped in a different swimlane, update the swimlaneId if (isDifferentSwimlane) { updateData.swimlaneId = targetSwimlaneId; - if (process.env.DEBUG === 'true') { - console.log(`Moving list "${list.title}" from swimlane ${originalSwimlaneId} to swimlane ${targetSwimlaneId}`); - } // Move all cards in the list to the new swimlane const cardsInList = ReactiveCache.getCards({ @@ -240,9 +225,6 @@ function initSortable(boardComponent, $listsDom) { card.move(list.boardId, targetSwimlaneId, list._id); }); - if (process.env.DEBUG === 'true') { - console.log(`Moved ${cardsInList.length} cards to swimlane ${targetSwimlaneId}`); - } // Don't cancel the sortable when moving to a different swimlane // The DOM move should be allowed to complete @@ -255,7 +237,6 @@ function initSortable(boardComponent, $listsDom) { Lists.update(list._id, { $set: updateData, }); - console.log('List updated successfully:', list._id, updateData); } catch (error) { console.error('Error updating list:', error); return; @@ -264,8 +245,10 @@ function initSortable(boardComponent, $listsDom) { boardComponent.setIsDragging(false); // Re-enable dragscroll after list dragging is complete - console.log('Re-enabling dragscroll after list dragging'); - dragscroll.reset(); + try { + dragscroll.reset(); + } catch (e) { + } // Re-enable dragscroll on all swimlanes $('.js-swimlane').each(function() { @@ -278,38 +261,14 @@ function initSortable(boardComponent, $listsDom) { return; } - console.log('List sortable initialization completed successfully'); - console.log('Sortable data after init:', $listsDom.data('uiSortable') || $listsDom.data('sortable')); + + // Check if drag handles exist + const dragHandles = $listsDom.find('.js-list-handle'); + + // Check if lists exist + const lists = $listsDom.find('.js-list'); - boardComponent.autorun(() => { - const $listDom = $listsDom; - console.log('Autorun running, checking sortable status...'); - console.log('Has uiSortable:', $listDom.data('uiSortable')); - console.log('Has sortable:', $listDom.data('sortable')); - if ($listDom.data('uiSortable') || $listDom.data('sortable')) { - if (Utils.isTouchScreenOrShowDesktopDragHandles()) { - $listsDom.sortable('option', 'handle', '.js-list-handle'); - console.log('List sortable: Using .js-list-handle as handle'); - console.log('Found drag handles:', $listsDom.find('.js-list-handle').length); - } else { - $listsDom.sortable('option', 'handle', '.js-list-header'); - console.log('List sortable: Using .js-list-header as handle'); - console.log('Found list headers:', $listsDom.find('.js-list-header').length); - } - - const currentUser = ReactiveCache.getCurrentUser(); - const canModify = Utils.canModifyBoard(); - const isDisabled = !canModify; - $listsDom.sortable('option', 'disabled', isDisabled); - console.log('List sortable disabled:', isDisabled); - console.log('Can modify board:', canModify); - console.log('Current user:', currentUser ? currentUser.username : 'null'); - console.log('Is board member:', currentUser ? currentUser.isBoardMember() : 'null'); - console.log('Is comment only:', currentUser ? currentUser.isCommentOnly() : 'null'); - } else { - console.log('List sortable not initialized yet'); - } - }); + // Skip the complex autorun and options for now } BlazeComponent.extendComponent({ @@ -317,15 +276,54 @@ BlazeComponent.extendComponent({ const boardComponent = this.parentComponent(); const $listsDom = this.$('.js-lists'); - console.log('Swimlane onRendered - DOM elements found:', $listsDom.length); - console.log('Swimlane onRendered - DOM element:', $listsDom[0]); if (!Utils.getCurrentCardId()) { boardComponent.scrollLeft(); } - console.log('Calling initSortable...'); - initSortable(boardComponent, $listsDom); + // Try a simpler approach - initialize sortable directly like cards do + + // Wait for DOM to be ready + setTimeout(() => { + const $lists = this.$('.js-list'); + + const $parent = $lists.parent(); + + if ($lists.length > 0) { + + // Check for drag handles + const $handles = $parent.find('.js-list-handle'); + + // Test if drag handles are clickable + $handles.on('click', function(e) { + e.preventDefault(); + e.stopPropagation(); + }); + + $parent.sortable({ + connectWith: '.js-swimlane, .js-lists', + tolerance: 'pointer', + appendTo: '.board-canvas', + helper: 'clone', + items: '.js-list:not(.js-list-composer)', + placeholder: 'list placeholder', + distance: 7, + handle: '.js-list-handle', + disabled: !Utils.canModifyBoard(), + start(evt, ui) { + ui.helper.css('z-index', 1000); + ui.placeholder.height(ui.helper.height()); + ui.placeholder.width(ui.helper.width()); + EscapeActions.executeUpTo('popup-close'); + boardComponent.setIsDragging(true); + }, + stop(evt, ui) { + boardComponent.setIsDragging(false); + } + }); + } else { + } + }, 100); }, onCreated() { this.draggingActive = new ReactiveVar(false); @@ -388,7 +386,6 @@ BlazeComponent.extendComponent({ const isInNoDragArea = $(evt.target).closest(noDragInside.join(',')).length > 0; if (isResizeHandle) { - console.log('Board drag prevented - resize handle clicked'); return; } @@ -572,6 +569,7 @@ BlazeComponent.extendComponent({ }, }).register('swimlane'); + BlazeComponent.extendComponent({ onCreated() { this.currentBoard = Utils.getCurrentBoard(); @@ -641,6 +639,108 @@ Template.swimlane.helpers({ }, }); +// Initialize sortable on DOM elements +setTimeout(() => { + const $swimlaneElements = $('.swimlane'); + const $listsGroupElements = $('.list-group'); + + // Initialize sortable on ALL swimlane elements (even empty ones) + $swimlaneElements.each(function(index) { + const $swimlane = $(this); + const $lists = $swimlane.find('.js-list'); + + // Only initialize on swimlanes that have the .js-lists class (the container for lists) + if ($swimlane.hasClass('js-lists')) { + $swimlane.sortable({ + connectWith: '.js-swimlane, .js-lists', + tolerance: 'pointer', + appendTo: '.board-canvas', + helper: 'clone', + items: '.js-list:not(.js-list-composer)', + placeholder: 'list placeholder', + distance: 7, + handle: '.js-list-handle', + disabled: !Utils.canModifyBoard(), + start(evt, ui) { + ui.helper.css('z-index', 1000); + ui.placeholder.height(ui.helper.height()); + ui.placeholder.width(ui.helper.width()); + EscapeActions.executeUpTo('popup-close'); + // Try to get board component + try { + const boardComponent = BlazeComponent.getComponentForElement(ui.item[0]); + if (boardComponent && boardComponent.setIsDragging) { + boardComponent.setIsDragging(true); + } + } catch (e) { + // Silent fail + } + }, + stop(evt, ui) { + // Try to get board component + try { + const boardComponent = BlazeComponent.getComponentForElement(ui.item[0]); + if (boardComponent && boardComponent.setIsDragging) { + boardComponent.setIsDragging(false); + } + } catch (e) { + // Silent fail + } + } + }); + } + }); + + // Initialize sortable on ALL listsGroup elements (even empty ones) + $listsGroupElements.each(function(index) { + const $listsGroup = $(this); + const $lists = $listsGroup.find('.js-list'); + + // Only initialize on listsGroup elements that have the .js-lists class + if ($listsGroup.hasClass('js-lists')) { + $listsGroup.sortable({ + connectWith: '.js-swimlane, .js-lists', + tolerance: 'pointer', + appendTo: '.board-canvas', + helper: 'clone', + items: '.js-list:not(.js-list-composer)', + placeholder: 'list placeholder', + distance: 7, + handle: '.js-list-handle', + disabled: !Utils.canModifyBoard(), + start(evt, ui) { + ui.helper.css('z-index', 1000); + ui.placeholder.height(ui.helper.height()); + ui.placeholder.width(ui.helper.width()); + EscapeActions.executeUpTo('popup-close'); + // Try to get board component + try { + const boardComponent = BlazeComponent.getComponentForElement(ui.item[0]); + if (boardComponent && boardComponent.setIsDragging) { + boardComponent.setIsDragging(true); + } + } catch (e) { + // Silent fail + } + }, + stop(evt, ui) { + // Try to get board component + try { + const boardComponent = BlazeComponent.getComponentForElement(ui.item[0]); + if (boardComponent && boardComponent.setIsDragging) { + boardComponent.setIsDragging(false); + } + } catch (e) { + // Silent fail + } + } + }); + } + }); +}, 1000); + + + BlazeComponent.extendComponent({ currentCardIsInThisList(listId, swimlaneId) { return currentCardIsInThisList(listId, swimlaneId); @@ -671,18 +771,58 @@ BlazeComponent.extendComponent({ const boardComponent = this.parentComponent(); const $listsDom = this.$('.js-lists'); - console.log('ListsGroup onRendered - DOM elements found:', $listsDom.length); - console.log('ListsGroup onRendered - DOM element:', $listsDom[0]); if (!Utils.getCurrentCardId()) { boardComponent.scrollLeft(); } - console.log('ListsGroup calling initSortable...'); - initSortable(boardComponent, $listsDom); + // Try a simpler approach for listsGroup too + + // Wait for DOM to be ready + setTimeout(() => { + const $lists = this.$('.js-list'); + + const $parent = $lists.parent(); + + if ($lists.length > 0) { + + // Check for drag handles + const $handles = $parent.find('.js-list-handle'); + + // Test if drag handles are clickable + $handles.on('click', function(e) { + e.preventDefault(); + e.stopPropagation(); + }); + + $parent.sortable({ + connectWith: '.js-swimlane, .js-lists', + tolerance: 'pointer', + appendTo: '.board-canvas', + helper: 'clone', + items: '.js-list:not(.js-list-composer)', + placeholder: 'list placeholder', + distance: 7, + handle: '.js-list-handle', + disabled: !Utils.canModifyBoard(), + start(evt, ui) { + ui.helper.css('z-index', 1000); + ui.placeholder.height(ui.helper.height()); + ui.placeholder.width(ui.helper.width()); + EscapeActions.executeUpTo('popup-close'); + boardComponent.setIsDragging(true); + }, + stop(evt, ui) { + boardComponent.setIsDragging(false); + } + }); + } else { + } + }, 100); }, }).register('listsGroup'); + class MoveSwimlaneComponent extends BlazeComponent { serverMethod = 'moveSwimlane'; From 351433524708e9a7ccb4795d9ca31a78904943ea Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 19 Oct 2025 23:15:55 +0300 Subject: [PATCH 018/183] At Public Board, drag resize list width and swimlane height. For logged in users, fix adding labels. Thanks to xet7 ! Fixes #5922 --- CHANGELOG.md | 3 +- client/components/cards/cardCustomFields.js | 7 +- client/components/cards/cardDate.js | 14 ++- client/components/cards/labels.js | 30 ++++- client/components/lists/list.js | 93 ++++++++++++-- client/components/main/header.css | 1 + client/components/sidebar/sidebar.jade | 12 +- client/components/sidebar/sidebar.js | 2 + .../components/swimlanes/swimlaneHeader.jade | 115 +++++++++--------- client/components/swimlanes/swimlanes.jade | 30 ++--- client/components/swimlanes/swimlanes.js | 59 +++++++-- client/lib/popup.js | 4 + models/users.js | 12 +- 13 files changed, 279 insertions(+), 103 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bb3b7eba..ba046b6f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,8 @@ This release fixes the following bugs: - [Fix unable to see My Due Cards](https://github.com/wekan/wekan/commit/66b444e2b0c9b2ed5f98cd1ff0cd9222b2d0c624). Thanks to xet7. - Fix drag drop lists. - [Part 1](https://github.com/wekan/wekan/commit/324f3f7794aace800022a24deb5fd5fb36ebd384). + [Part 1](https://github.com/wekan/wekan/commit/324f3f7794aace800022a24deb5fd5fb36ebd384), + [Part 2](https://github.com/wekan/wekan/commit/ff516ec696ef499f11b04b30053eeb9d3f96d8d1). Thanks to xet7. - [Removed extra pipe characters](https://github.com/wekan/wekan/commit/caa6e615ff3c3681bf2b470a625eb39c6009b825). Thanks to xet7. diff --git a/client/components/cards/cardCustomFields.js b/client/components/cards/cardCustomFields.js index 5d02091ca..333d5e5cd 100644 --- a/client/components/cards/cardCustomFields.js +++ b/client/components/cards/cardCustomFields.js @@ -168,7 +168,12 @@ CardCustomField.register('cardCustomField'); } showWeekOfYear() { - return ReactiveCache.getCurrentUser().isShowWeekOfYear(); + const user = ReactiveCache.getCurrentUser(); + if (!user) { + // For non-logged-in users, week of year is not shown + return false; + } + return user.isShowWeekOfYear(); } showDate() { diff --git a/client/components/cards/cardDate.js b/client/components/cards/cardDate.js index 8b87b0cf9..7c8ef242d 100644 --- a/client/components/cards/cardDate.js +++ b/client/components/cards/cardDate.js @@ -131,7 +131,12 @@ const CardDate = BlazeComponent.extendComponent({ }, showWeekOfYear() { - return ReactiveCache.getCurrentUser().isShowWeekOfYear(); + const user = ReactiveCache.getCurrentUser(); + if (!user) { + // For non-logged-in users, week of year is not shown + return false; + } + return user.isShowWeekOfYear(); }, showDate() { @@ -301,7 +306,12 @@ class CardCustomFieldDate extends CardDate { } showWeekOfYear() { - return ReactiveCache.getCurrentUser().isShowWeekOfYear(); + const user = ReactiveCache.getCurrentUser(); + if (!user) { + // For non-logged-in users, week of year is not shown + return false; + } + return user.isShowWeekOfYear(); } showDate() { diff --git a/client/components/cards/labels.js b/client/components/cards/labels.js index e09598189..2962cae77 100644 --- a/client/components/cards/labels.js +++ b/client/components/cards/labels.js @@ -125,8 +125,19 @@ Template.createLabelPopup.events({ .$('#labelName') .val() .trim(); - const color = Blaze.getData(templateInstance.find('.fa-check')).color; - board.addLabel(name, color); + + // Find the selected color by looking for the palette color that contains the checkmark + let selectedColor = null; + templateInstance.$('.js-palette-color').each(function() { + if ($(this).text().includes('✅')) { + selectedColor = Blaze.getData(this).color; + return false; // break out of loop + } + }); + + if (selectedColor) { + board.addLabel(name, selectedColor); + } Popup.back(); }, }); @@ -144,8 +155,19 @@ Template.editLabelPopup.events({ .$('#labelName') .val() .trim(); - const color = Blaze.getData(templateInstance.find('.fa-check')).color; - board.editLabel(this._id, name, color); + + // Find the selected color by looking for the palette color that contains the checkmark + let selectedColor = null; + templateInstance.$('.js-palette-color').each(function() { + if ($(this).text().includes('✅')) { + selectedColor = Blaze.getData(this).color; + return false; // break out of loop + } + }); + + if (selectedColor) { + board.editLabel(this._id, name, selectedColor); + } Popup.back(); }, }); diff --git a/client/components/lists/list.js b/client/components/lists/list.js index b80ecff84..7501886ae 100644 --- a/client/components/lists/list.js +++ b/client/components/lists/list.js @@ -197,20 +197,60 @@ BlazeComponent.extendComponent({ listWidth() { const user = ReactiveCache.getCurrentUser(); const list = Template.currentData(); - if (!user || !list) return 270; // Return default width if user or list is not available - return user.getListWidthFromStorage(list.boardId, list._id); + if (!list) return 270; // Return default width if list is not available + + if (user) { + // For logged-in users, get from user profile + return user.getListWidthFromStorage(list.boardId, list._id); + } else { + // For non-logged-in users, get from localStorage + try { + const stored = localStorage.getItem('wekan-list-widths'); + if (stored) { + const widths = JSON.parse(stored); + if (widths[list.boardId] && widths[list.boardId][list._id]) { + return widths[list.boardId][list._id]; + } + } + } catch (e) { + console.warn('Error reading list width from localStorage:', e); + } + return 270; // Return default width if not found + } }, listConstraint() { const user = ReactiveCache.getCurrentUser(); const list = Template.currentData(); - if (!user || !list) return 550; // Return default constraint if user or list is not available - return user.getListConstraintFromStorage(list.boardId, list._id); + if (!list) return 550; // Return default constraint if list is not available + + if (user) { + // For logged-in users, get from user profile + return user.getListConstraintFromStorage(list.boardId, list._id); + } else { + // For non-logged-in users, get from localStorage + try { + const stored = localStorage.getItem('wekan-list-constraints'); + if (stored) { + const constraints = JSON.parse(stored); + if (constraints[list.boardId] && constraints[list.boardId][list._id]) { + return constraints[list.boardId][list._id]; + } + } + } catch (e) { + console.warn('Error reading list constraint from localStorage:', e); + } + return 550; // Return default constraint if not found + } }, autoWidth() { const user = ReactiveCache.getCurrentUser(); const list = Template.currentData(); + if (!user) { + // For non-logged-in users, auto-width is disabled + return false; + } return user.isAutoWidth(list.boardId); }, @@ -329,14 +369,49 @@ BlazeComponent.extendComponent({ // Use the new storage method that handles both logged-in and non-logged-in users if (process.env.DEBUG === 'true') { } - Meteor.call('applyListWidthToStorage', boardId, listId, finalWidth, listConstraint, (error, result) => { - if (error) { - console.error('Error saving list width:', error); - } else { + + const currentUser = ReactiveCache.getCurrentUser(); + if (currentUser) { + // For logged-in users, use server method + Meteor.call('applyListWidthToStorage', boardId, listId, finalWidth, listConstraint, (error, result) => { + if (error) { + console.error('Error saving list width:', error); + } else { + if (process.env.DEBUG === 'true') { + } + } + }); + } else { + // For non-logged-in users, save to localStorage directly + try { + // Save list width + const storedWidths = localStorage.getItem('wekan-list-widths'); + let widths = storedWidths ? JSON.parse(storedWidths) : {}; + + if (!widths[boardId]) { + widths[boardId] = {}; + } + widths[boardId][listId] = finalWidth; + + localStorage.setItem('wekan-list-widths', JSON.stringify(widths)); + + // Save list constraint + const storedConstraints = localStorage.getItem('wekan-list-constraints'); + let constraints = storedConstraints ? JSON.parse(storedConstraints) : {}; + + if (!constraints[boardId]) { + constraints[boardId] = {}; + } + constraints[boardId][listId] = listConstraint; + + localStorage.setItem('wekan-list-constraints', JSON.stringify(constraints)); + if (process.env.DEBUG === 'true') { } + } catch (e) { + console.warn('Error saving list width/constraint to localStorage:', e); } - }); + } e.preventDefault(); }; diff --git a/client/components/main/header.css b/client/components/main/header.css index 6bb1043f3..f0d002b7a 100644 --- a/client/components/main/header.css +++ b/client/components/main/header.css @@ -220,6 +220,7 @@ margin: 0; margin-top: 1px; } + #header-quick-access #header-user-bar .header-user-bar-name, #header-quick-access #header-help { margin: 4px 8px 0 0; diff --git a/client/components/sidebar/sidebar.jade b/client/components/sidebar/sidebar.jade index 138d4b8bf..d63ea9d98 100644 --- a/client/components/sidebar/sidebar.jade +++ b/client/components/sidebar/sidebar.jade @@ -28,7 +28,6 @@ template(name="sidebar") +Template.dynamic(template=getViewTemplate) template(name='homeSidebar') - hr +membersWidget hr +labelsWidget @@ -38,11 +37,12 @@ template(name='homeSidebar') span {{_ 'hide-minicard-label-text'}} b   .materialCheckBox(class="{{#if hiddenMinicardLabelText}}is-checked{{/if}}") - ul#cards.vertical-scrollbars-toggle - a.flex.js-vertical-scrollbars-toggle(title="{{_ 'enable-vertical-scrollbars'}}") - span {{_ 'enable-vertical-scrollbars'}} - b   - .materialCheckBox(class="{{#if isVerticalScrollbars}}is-checked{{/if}}") + if currentUser + ul#cards.vertical-scrollbars-toggle + a.flex.js-vertical-scrollbars-toggle(title="{{_ 'enable-vertical-scrollbars'}}") + span {{_ 'enable-vertical-scrollbars'}} + b   + .materialCheckBox(class="{{#if isVerticalScrollbars}}is-checked{{/if}}") ul#cards.show-week-of-year-toggle a.flex.js-show-week-of-year-toggle(title="{{_ 'show-week-of-year'}}") span {{_ 'show-week-of-year'}} diff --git a/client/components/sidebar/sidebar.js b/client/components/sidebar/sidebar.js index a6de901d3..9f1d9c5c1 100644 --- a/client/components/sidebar/sidebar.js +++ b/client/components/sidebar/sidebar.js @@ -203,6 +203,8 @@ BlazeComponent.extendComponent({ }, }).register('homeSidebar'); + + Template.boardInfoOnMyBoardsPopup.helpers({ hideCardCounterList() { return Utils.isMiniScreen() && Session.get('currentBoard'); diff --git a/client/components/swimlanes/swimlaneHeader.jade b/client/components/swimlanes/swimlaneHeader.jade index 78fc9d4af..04548ffa6 100644 --- a/client/components/swimlanes/swimlaneHeader.jade +++ b/client/components/swimlanes/swimlaneHeader.jade @@ -23,26 +23,27 @@ template(name="swimlaneFixedHeader") +viewer | {{isTitleDefault title}} .swimlane-header-menu - unless currentUser.isCommentOnly - a.js-open-add-swimlane-menu.swimlane-header-plus-icon(title="{{_ 'add-swimlane'}}") - | ➕ - a.js-open-swimlane-menu(title="{{_ 'swimlaneActionPopup-title'}}") - | ☰ - //// TODO: Collapse Swimlane: make button working, etc. - //unless collapsed - // a.js-collapse-swimlane(title="{{_ 'collapse'}}") - // i.fa.fa-arrow-down.swimlane-header-collapse-down - // ⬆️.swimlane-header-collapse-up - //if collapsed - // a.js-collapse-swimlane(title="{{_ 'uncollapse'}}") - // ⬆️.swimlane-header-collapse-up - // i.fa.fa-arrow-down.swimlane-header-collapse-down - unless isTouchScreen - a.swimlane-header-handle.handle.js-swimlane-header-handle - | ↕️ - if isTouchScreen - a.swimlane-header-miniscreen-handle.handle.js-swimlane-header-handle - | ↕️ + if currentUser + unless currentUser.isCommentOnly + a.js-open-add-swimlane-menu.swimlane-header-plus-icon(title="{{_ 'add-swimlane'}}") + | ➕ + a.js-open-swimlane-menu(title="{{_ 'swimlaneActionPopup-title'}}") + | ☰ + //// TODO: Collapse Swimlane: make button working, etc. + //unless collapsed + // a.js-collapse-swimlane(title="{{_ 'collapse'}}") + // i.fa.fa-arrow-down.swimlane-header-collapse-down + // ⬆️.swimlane-header-collapse-up + //if collapsed + // a.js-collapse-swimlane(title="{{_ 'uncollapse'}}") + // ⬆️.swimlane-header-collapse-up + // i.fa.fa-arrow-down.swimlane-header-collapse-down + unless isTouchScreen + a.swimlane-header-handle.handle.js-swimlane-header-handle + | ↕️ + if isTouchScreen + a.swimlane-header-miniscreen-handle.handle.js-swimlane-header-handle + | ↕️ template(name="editSwimlaneTitleForm") .list-composer @@ -53,44 +54,46 @@ template(name="editSwimlaneTitleForm") | ❌ template(name="swimlaneActionPopup") - unless currentUser.isCommentOnly - ul.pop-over-list - if currentUser.isBoardAdmin - li: a.js-set-swimlane-color - | 🎨 - | {{_ 'select-color'}} - li: a.js-set-swimlane-height - | ↕️ - | {{_ 'set-swimlane-height'}} - if currentUser.isBoardAdmin - unless this.isTemplateContainer - hr - ul.pop-over-list - li: a.js-close-swimlane - | ▶️ - | 📦 - | {{_ 'archive-swimlane'}} - ul.pop-over-list - li: a.js-copy-swimlane - | 📋 - | {{_ 'copy-swimlane'}} - ul.pop-over-list - li: a.js-move-swimlane - | ⬆️ - | {{_ 'move-swimlane'}} + if currentUser + unless currentUser.isCommentOnly + ul.pop-over-list + if currentUser.isBoardAdmin + li: a.js-set-swimlane-color + | 🎨 + | {{_ 'select-color'}} + li: a.js-set-swimlane-height + | ↕️ + | {{_ 'set-swimlane-height'}} + if currentUser.isBoardAdmin + unless this.isTemplateContainer + hr + ul.pop-over-list + li: a.js-close-swimlane + | ▶️ + | 📦 + | {{_ 'archive-swimlane'}} + ul.pop-over-list + li: a.js-copy-swimlane + | 📋 + | {{_ 'copy-swimlane'}} + ul.pop-over-list + li: a.js-move-swimlane + | ⬆️ + | {{_ 'move-swimlane'}} template(name="swimlaneAddPopup") - unless currentUser.isCommentOnly - form - input.swimlane-name-input.full-line(type="text" placeholder="{{_ 'add-swimlane'}}" - autocomplete="off" autofocus) - .edit-controls.clearfix - button.primary.confirm(type="submit") {{_ 'add'}} - unless currentBoard.isTemplatesBoard - unless currentBoard.isTemplateBoard - span.quiet - | {{_ 'or'}} - a.js-swimlane-template {{_ 'template'}} + if currentUser + unless currentUser.isCommentOnly + form + input.swimlane-name-input.full-line(type="text" placeholder="{{_ 'add-swimlane'}}" + autocomplete="off" autofocus) + .edit-controls.clearfix + button.primary.confirm(type="submit") {{_ 'add'}} + unless currentBoard.isTemplatesBoard + unless currentBoard.isTemplateBoard + span.quiet + | {{_ 'or'}} + a.js-swimlane-template {{_ 'template'}} template(name="setSwimlaneColorPopup") form.edit-label.swimlane-color-popup diff --git a/client/components/swimlanes/swimlanes.jade b/client/components/swimlanes/swimlanes.jade index 29d8fb62d..25e634573 100644 --- a/client/components/swimlanes/swimlanes.jade +++ b/client/components/swimlanes/swimlanes.jade @@ -72,21 +72,23 @@ template(name="addListForm") | ➕ template(name="moveSwimlanePopup") - unless currentUser.isWorker - label {{_ 'boards'}}: - select.js-select-boards(autofocus) - each toBoard in toBoards - option(value="{{toBoard._id}}") {{toBoard.title}} + if currentUser + unless currentUser.isWorker + label {{_ 'boards'}}: + select.js-select-boards(autofocus) + each toBoard in toBoards + option(value="{{toBoard._id}}") {{toBoard.title}} - .edit-controls.clearfix - button.primary.confirm.js-done {{_ 'done'}} + .edit-controls.clearfix + button.primary.confirm.js-done {{_ 'done'}} template(name="copySwimlanePopup") - unless currentUser.isWorker - label {{_ 'boards'}}: - select.js-select-boards(autofocus) - each toBoard in toBoards - option(value="{{toBoard._id}}" selected="{{#if $eq toBoard.title board.title}}1{{/if}}") {{toBoard.title}} + if currentUser + unless currentUser.isWorker + label {{_ 'boards'}}: + select.js-select-boards(autofocus) + each toBoard in toBoards + option(value="{{toBoard._id}}" selected="{{#if $eq toBoard.title board.title}}1{{/if}}") {{toBoard.title}} - .edit-controls.clearfix - button.primary.confirm.js-done {{_ 'done'}} + .edit-controls.clearfix + button.primary.confirm.js-done {{_ 'done'}} diff --git a/client/components/swimlanes/swimlanes.js b/client/components/swimlanes/swimlanes.js index 398243683..cb0eb4c9d 100644 --- a/client/components/swimlanes/swimlanes.js +++ b/client/components/swimlanes/swimlanes.js @@ -423,7 +423,31 @@ BlazeComponent.extendComponent({ swimlaneHeight() { const user = ReactiveCache.getCurrentUser(); const swimlane = Template.currentData(); - const height = user.getSwimlaneHeightFromStorage(swimlane.boardId, swimlane._id); + + let height; + if (user) { + // For logged-in users, get from user profile + height = user.getSwimlaneHeightFromStorage(swimlane.boardId, swimlane._id); + } else { + // For non-logged-in users, get from localStorage + try { + const stored = localStorage.getItem('wekan-swimlane-heights'); + if (stored) { + const heights = JSON.parse(stored); + if (heights[swimlane.boardId] && heights[swimlane.boardId][swimlane._id]) { + height = heights[swimlane.boardId][swimlane._id]; + } else { + height = -1; + } + } else { + height = -1; + } + } catch (e) { + console.warn('Error reading swimlane height from localStorage:', e); + height = -1; + } + } + return height == -1 ? "auto" : (height + 5 + "px"); }, @@ -537,15 +561,36 @@ BlazeComponent.extendComponent({ if (process.env.DEBUG === 'true') { } - // Use the new storage method that handles both logged-in and non-logged-in users - Meteor.call('applySwimlaneHeightToStorage', boardId, swimlaneId, finalHeight, (error, result) => { - if (error) { - console.error('Error saving swimlane height:', error); - } else { + const currentUser = ReactiveCache.getCurrentUser(); + if (currentUser) { + // For logged-in users, use server method + Meteor.call('applySwimlaneHeightToStorage', boardId, swimlaneId, finalHeight, (error, result) => { + if (error) { + console.error('Error saving swimlane height:', error); + } else { + if (process.env.DEBUG === 'true') { + } + } + }); + } else { + // For non-logged-in users, save to localStorage directly + try { + const stored = localStorage.getItem('wekan-swimlane-heights'); + let heights = stored ? JSON.parse(stored) : {}; + + if (!heights[boardId]) { + heights[boardId] = {}; + } + heights[boardId][swimlaneId] = finalHeight; + + localStorage.setItem('wekan-swimlane-heights', JSON.stringify(heights)); + if (process.env.DEBUG === 'true') { } + } catch (e) { + console.warn('Error saving swimlane height to localStorage:', e); } - }); + } e.preventDefault(); }; diff --git a/client/lib/popup.js b/client/lib/popup.js index 5cc5ec27c..5c0c7a6c9 100644 --- a/client/lib/popup.js +++ b/client/lib/popup.js @@ -101,6 +101,10 @@ window.Popup = new (class { // our internal dependency, and since we just changed the top element of // our internal stack, the popup will be updated with the new data. if (!self.isOpen()) { + if (!Template[popupName]) { + console.error('Template not found:', popupName); + return; + } self.current = Blaze.renderWithData( self.template, () => { diff --git a/models/users.js b/models/users.js index 081df38d6..0d49d0579 100644 --- a/models/users.js +++ b/models/users.js @@ -1619,7 +1619,10 @@ Meteor.methods({ check(swimlaneId, String); check(height, Number); const user = ReactiveCache.getCurrentUser(); - user.setSwimlaneHeightToStorage(boardId, swimlaneId, height); + if (user) { + user.setSwimlaneHeightToStorage(boardId, swimlaneId, height); + } + // For non-logged-in users, the client-side code will handle localStorage }, applyListWidthToStorage(boardId, listId, width, constraint) { @@ -1628,8 +1631,11 @@ Meteor.methods({ check(width, Number); check(constraint, Number); const user = ReactiveCache.getCurrentUser(); - user.setListWidthToStorage(boardId, listId, width); - user.setListConstraintToStorage(boardId, listId, constraint); + if (user) { + user.setListWidthToStorage(boardId, listId, width); + user.setListConstraintToStorage(boardId, listId, constraint); + } + // For non-logged-in users, the client-side code will handle localStorage }, setZoomLevel(level) { check(level, Number); From 1658883b78175cb9f5615fe29f7a5c7770aaf732 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 19 Oct 2025 23:19:54 +0300 Subject: [PATCH 019/183] Updated ChangeLog. --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba046b6f2..0efb53b60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,12 @@ Fixing other platforms In Progress. # Upcoming WeKan ® release -This release fixes the following bugs: +This release adds the following new features: + +- [At Public Board, drag resize list width and swimlane height. For logged in users, fix adding labels](https://github.com/wekan/wekan/commit/351433524708e9a7ccb4795d9ca31a78904943ea). + Thanks to xet7. + +and fixes the following bugs: - [Fix add and drag drop attachments to minicards and card](https://github.com/wekan/wekan/commit/b06daff4c7e63453643459f7d8798fde97e3200c). Thanks to xet7. From 951d2e49379d5588f2aa11960e8945ab770c78d2 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 19 Oct 2025 23:40:02 +0300 Subject: [PATCH 020/183] Legacy Lists button at one board view to restore missing lists/cards. Thanks to xet7 ! Fixes #5952 --- client/components/boards/boardHeader.jade | 5 + client/components/boards/boardHeader.js | 23 +++ imports/i18n/data/en.i18n.json | 7 +- models/boards.js | 4 + server/cronMigrationManager.js | 208 ++++++++++++++++++++-- 5 files changed, 233 insertions(+), 14 deletions(-) diff --git a/client/components/boards/boardHeader.jade b/client/components/boards/boardHeader.jade index 208753243..372dfb07f 100644 --- a/client/components/boards/boardHeader.jade +++ b/client/components/boards/boardHeader.jade @@ -130,6 +130,11 @@ template(name="boardHeaderBar") a.board-header-btn-close.js-multiselection-reset(title="{{_ 'filter-clear'}}") | ❌ + if currentUser.isBoardAdmin + a.board-header-btn.js-restore-legacy-lists(title="{{_ 'restore-legacy-lists'}}") + | 🔄 + | {{_ 'legacy-lists'}} + .separator a.board-header-btn.js-toggle-sidebar(title="{{_ 'sidebar-open'}} {{_ 'or'}} {{_ 'sidebar-close'}}") | ☰ diff --git a/client/components/boards/boardHeader.js b/client/components/boards/boardHeader.js index d11857f3e..574b45e4d 100644 --- a/client/components/boards/boardHeader.js +++ b/client/components/boards/boardHeader.js @@ -152,9 +152,32 @@ BlazeComponent.extendComponent({ 'click .js-log-in'() { FlowRouter.go('atSignIn'); }, + 'click .js-restore-legacy-lists'() { + this.restoreLegacyLists(); + }, }, ]; }, + + restoreLegacyLists() { + // Show confirmation dialog + if (confirm('Are you sure you want to restore legacy lists to their original shared state? This will make them appear in all swimlanes.')) { + // Call cron method to restore legacy lists + Meteor.call('cron.triggerRestoreLegacyLists', (error, result) => { + if (error) { + console.error('Error restoring legacy lists:', error); + alert(`Error: ${error.message}`); + } else { + console.log('Successfully triggered restore legacy lists migration:', result); + alert(`Migration triggered successfully. Job ID: ${result.jobId}`); + // Refresh the board to show the restored lists + setTimeout(() => { + window.location.reload(); + }, 2000); + } + }); + } + }, }).register('boardHeaderBar'); Template.boardHeaderBar.helpers({ diff --git a/imports/i18n/data/en.i18n.json b/imports/i18n/data/en.i18n.json index 48daee11b..f106b6528 100644 --- a/imports/i18n/data/en.i18n.json +++ b/imports/i18n/data/en.i18n.json @@ -1488,5 +1488,10 @@ "weight": "Weight", "idle": "Idle", "complete": "Complete", - "cron": "Cron" + "cron": "Cron", + "legacy-lists": "Legacy Lists", + "restore-legacy-lists": "Restore Legacy Lists", + "legacy-lists-restore": "Legacy Lists Restore", + "legacy-lists-restore-description": "Restore legacy lists to their original shared state across all swimlanes", + "restoring-legacy-lists": "Restoring legacy lists" } diff --git a/models/boards.js b/models/boards.js index dcb09a3cc..f62016d71 100644 --- a/models/boards.js +++ b/models/boards.js @@ -778,6 +778,10 @@ Boards.helpers({ return this.permission === 'public'; }, + hasLegacyLists() { + return this.hasLegacyLists === true; + }, + cards() { const ret = ReactiveCache.getCards( { boardId: this._id, archived: false }, diff --git a/server/cronMigrationManager.js b/server/cronMigrationManager.js index 97305a49b..a42e9b335 100644 --- a/server/cronMigrationManager.js +++ b/server/cronMigrationManager.js @@ -232,6 +232,17 @@ class CronMigrationManager { cronName: 'migration_lists_per_swimlane', schedule: 'every 1 minute', status: 'stopped' + }, + { + id: 'restore-legacy-lists', + name: 'Restore Legacy Lists', + description: 'Restore legacy lists to their original shared state across all swimlanes', + weight: 3, + completed: false, + progress: 0, + cronName: 'migration_restore_legacy_lists', + schedule: 'every 1 minute', + status: 'stopped' } ]; } @@ -357,9 +368,16 @@ class CronMigrationManager { * Execute a migration job */ async executeMigrationJob(jobId, jobData) { - const { stepId } = jobData; - const step = this.migrationSteps.find(s => s.id === stepId); + if (!jobData) { + throw new Error('Job data is required for migration execution'); + } + const { stepId } = jobData; + if (!stepId) { + throw new Error('Step ID is required in job data'); + } + + const step = this.migrationSteps.find(s => s.id === stepId); if (!step) { throw new Error(`Migration step ${stepId} not found`); } @@ -378,7 +396,7 @@ class CronMigrationManager { }); // Execute step - await this.executeMigrationStep(jobId, i, stepData); + await this.executeMigrationStep(jobId, i, stepData, stepId); // Mark step as completed cronJobStorage.saveJobStep(jobId, i, { @@ -423,6 +441,14 @@ class CronMigrationManager { { name: 'Cleanup old data', duration: 1000 } ); break; + case 'restore-legacy-lists': + steps.push( + { name: 'Identify legacy lists', duration: 1000 }, + { name: 'Restore lists to shared state', duration: 2000 }, + { name: 'Update board settings', duration: 500 }, + { name: 'Verify restoration', duration: 500 } + ); + break; default: steps.push( { name: `Execute ${step.name}`, duration: 2000 }, @@ -436,22 +462,116 @@ class CronMigrationManager { /** * Execute a migration step */ - async executeMigrationStep(jobId, stepIndex, stepData) { + async executeMigrationStep(jobId, stepIndex, stepData, stepId) { const { name, duration } = stepData; - // Simulate step execution with progress updates - const progressSteps = 10; - for (let i = 0; i <= progressSteps; i++) { - const progress = Math.round((i / progressSteps) * 100); + if (stepId === 'restore-legacy-lists') { + await this.executeRestoreLegacyListsMigration(jobId, stepIndex, stepData); + } else { + // Simulate step execution with progress updates for other migrations + const progressSteps = 10; + for (let i = 0; i <= progressSteps; i++) { + const progress = Math.round((i / progressSteps) * 100); + + // Update step progress + cronJobStorage.saveJobStep(jobId, stepIndex, { + progress, + currentAction: `Executing: ${name} (${progress}%)` + }); + + // Simulate work + await new Promise(resolve => setTimeout(resolve, duration / progressSteps)); + } + } + } + + /** + * Execute the restore legacy lists migration + */ + async executeRestoreLegacyListsMigration(jobId, stepIndex, stepData) { + const { name } = stepData; + + try { + // Import collections directly for server-side access + const { default: Boards } = await import('/models/boards'); + const { default: Lists } = await import('/models/lists'); - // Update step progress + // Step 1: Identify legacy lists cronJobStorage.saveJobStep(jobId, stepIndex, { - progress, - currentAction: `Executing: ${name} (${progress}%)` + progress: 25, + currentAction: 'Identifying legacy lists...' }); - // Simulate work - await new Promise(resolve => setTimeout(resolve, duration / progressSteps)); + const boards = Boards.find({}).fetch(); + const migrationDate = new Date('2025-10-10T21:14:44.000Z'); // Date of commit 719ef87efceacfe91461a8eeca7cf74d11f4cc0a + let totalLegacyLists = 0; + + for (const board of boards) { + const allLists = Lists.find({ boardId: board._id }).fetch(); + const legacyLists = allLists.filter(list => { + const listDate = list.createdAt || new Date(0); + return listDate < migrationDate && list.swimlaneId && list.swimlaneId !== ''; + }); + totalLegacyLists += legacyLists.length; + } + + // Step 2: Restore lists to shared state + cronJobStorage.saveJobStep(jobId, stepIndex, { + progress: 50, + currentAction: 'Restoring lists to shared state...' + }); + + let restoredCount = 0; + + for (const board of boards) { + const allLists = Lists.find({ boardId: board._id }).fetch(); + const legacyLists = allLists.filter(list => { + const listDate = list.createdAt || new Date(0); + return listDate < migrationDate && list.swimlaneId && list.swimlaneId !== ''; + }); + + // Restore legacy lists to shared state (empty swimlaneId) + for (const list of legacyLists) { + Lists.direct.update(list._id, { + $set: { + swimlaneId: '' + } + }); + restoredCount++; + } + + // Mark the board as having legacy lists + if (legacyLists.length > 0) { + Boards.direct.update(board._id, { + $set: { + hasLegacyLists: true + } + }); + } + } + + // Step 3: Update board settings + cronJobStorage.saveJobStep(jobId, stepIndex, { + progress: 75, + currentAction: 'Updating board settings...' + }); + + // Step 4: Verify restoration + cronJobStorage.saveJobStep(jobId, stepIndex, { + progress: 100, + currentAction: `Verification complete. Restored ${restoredCount} legacy lists.` + }); + + console.log(`Successfully restored ${restoredCount} legacy lists across ${boards.length} boards`); + + } catch (error) { + console.error('Error during restore legacy lists migration:', error); + cronJobStorage.saveJobStep(jobId, stepIndex, { + progress: 0, + currentAction: `Error: ${error.message}`, + status: 'error' + }); + throw error; } } @@ -1299,6 +1419,54 @@ class CronMigrationManager { return stats; } + + /** + * Trigger restore legacy lists migration + */ + async triggerRestoreLegacyListsMigration() { + try { + // Find the restore legacy lists step + const step = this.migrationSteps.find(s => s.id === 'restore-legacy-lists'); + if (!step) { + throw new Error('Restore legacy lists migration step not found'); + } + + // Create a job for this migration + const jobId = `restore_legacy_lists_${Date.now()}`; + cronJobStorage.addToQueue(jobId, 'migration', step.weight, { + stepId: step.id, + stepName: step.name, + stepDescription: step.description + }); + + // Save initial job status + cronJobStorage.saveJobStatus(jobId, { + jobType: 'migration', + status: 'pending', + progress: 0, + stepId: step.id, + stepName: step.name + }); + + // Execute the migration immediately + const jobData = { + stepId: step.id, + stepName: step.name, + stepDescription: step.description + }; + await this.executeMigrationJob(jobId, jobData); + + return { + success: true, + jobId: jobId, + message: 'Restore legacy lists migration triggered successfully' + }; + + } catch (error) { + console.error('Error triggering restore legacy lists migration:', error); + throw new Meteor.Error('migration-trigger-failed', `Failed to trigger migration: ${error.message}`); + } + } } // Export singleton instance @@ -1496,5 +1664,19 @@ Meteor.methods({ // Import the board migration detector const { boardMigrationDetector } = require('./boardMigrationDetector'); return boardMigrationDetector.forceScan(); + }, + + 'cron.triggerRestoreLegacyLists'() { + if (!this.userId) { + throw new Meteor.Error('not-authorized'); + } + + // Check if user is admin (optional - you can remove this if you want any user to trigger it) + const user = ReactiveCache.getCurrentUser(); + if (!user || !user.isAdmin) { + throw new Meteor.Error('not-authorized', 'Only administrators can trigger this migration'); + } + + return cronMigrationManager.triggerRestoreLegacyListsMigration(); } }); From 48b645ee1e49455441917eb4325dd315c4918489 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 19 Oct 2025 23:48:15 +0300 Subject: [PATCH 021/183] Updated ChangeLog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0efb53b60..19981311a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ This release adds the following new features: - [At Public Board, drag resize list width and swimlane height. For logged in users, fix adding labels](https://github.com/wekan/wekan/commit/351433524708e9a7ccb4795d9ca31a78904943ea). Thanks to xet7. +- [Legacy Lists button at one board view to restore missing lists/cards](https://github.com/wekan/wekan/commit/951d2e49379d5588f2aa11960e8945ab770c78d2). + Thanks to xet7. and fixes the following bugs: From 1e6252de7f26f3af14a99fb63b5dac27ba0576f3 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Mon, 20 Oct 2025 00:22:26 +0300 Subject: [PATCH 022/183] When opening board, migrate from Shared Lists to Per-Swimlane Lists. Thanks to xet7 ! Fixes #5952 --- client/components/boards/boardBody.js | 97 ++++++++++++ client/components/boards/boardHeader.jade | 5 - client/components/boards/boardHeader.js | 22 --- imports/i18n/data/en.i18n.json | 7 +- models/boards.js | 5 +- server/00checkStartup.js | 8 +- server/boardMigrationDetector.js | 14 +- server/cronMigrationManager.js | 173 +--------------------- 8 files changed, 112 insertions(+), 219 deletions(-) diff --git a/client/components/boards/boardBody.js b/client/components/boards/boardBody.js index 56c38f590..7f8883d0b 100644 --- a/client/components/boards/boardBody.js +++ b/client/components/boards/boardBody.js @@ -112,6 +112,9 @@ BlazeComponent.extendComponent({ } } + // Convert shared lists to per-swimlane lists if needed + await this.convertSharedListsToPerSwimlane(boardId); + // Start attachment migration in background if needed this.startAttachmentMigrationIfNeeded(boardId); } catch (error) { @@ -139,6 +142,100 @@ BlazeComponent.extendComponent({ } }, + async convertSharedListsToPerSwimlane(boardId) { + try { + const board = ReactiveCache.getBoard(boardId); + if (!board) return; + + // Check if board has already been processed for shared lists conversion + if (board.hasSharedListsConverted) { + if (process.env.DEBUG === 'true') { + console.log(`Board ${boardId} has already been processed for shared lists conversion`); + } + return; + } + + // Get all lists for this board + const allLists = board.lists(); + const swimlanes = board.swimlanes(); + + if (swimlanes.length === 0) { + if (process.env.DEBUG === 'true') { + console.log(`Board ${boardId} has no swimlanes, skipping shared lists conversion`); + } + return; + } + + // Find shared lists (lists with empty swimlaneId or null swimlaneId) + const sharedLists = allLists.filter(list => !list.swimlaneId || list.swimlaneId === ''); + + if (sharedLists.length === 0) { + if (process.env.DEBUG === 'true') { + console.log(`Board ${boardId} has no shared lists to convert`); + } + // Mark as processed even if no shared lists + Meteor.call('boards.update', boardId, { $set: { hasSharedListsConverted: true } }); + return; + } + + if (process.env.DEBUG === 'true') { + console.log(`Converting ${sharedLists.length} shared lists to per-swimlane lists for board ${boardId}`); + } + + // Convert each shared list to per-swimlane lists + for (const sharedList of sharedLists) { + // Create a copy of the list for each swimlane + for (const swimlane of swimlanes) { + // Check if this list already exists in this swimlane + const existingList = Lists.findOne({ + boardId: boardId, + swimlaneId: swimlane._id, + title: sharedList.title + }); + + if (!existingList) { + // Create a new list in this swimlane + const newListData = { + title: sharedList.title, + boardId: boardId, + swimlaneId: swimlane._id, + sort: sharedList.sort || 0, + archived: sharedList.archived || false, + createdAt: new Date(), + modifiedAt: new Date() + }; + + // Copy other properties if they exist + if (sharedList.color) newListData.color = sharedList.color; + if (sharedList.wipLimit) newListData.wipLimit = sharedList.wipLimit; + if (sharedList.wipLimitEnabled) newListData.wipLimitEnabled = sharedList.wipLimitEnabled; + if (sharedList.wipLimitSoft) newListData.wipLimitSoft = sharedList.wipLimitSoft; + + Lists.insert(newListData); + } + } + + // Archive or remove the original shared list + Lists.update(sharedList._id, { + $set: { + archived: true, + modifiedAt: new Date() + } + }); + } + + // Mark board as processed + Meteor.call('boards.update', boardId, { $set: { hasSharedListsConverted: true } }); + + if (process.env.DEBUG === 'true') { + console.log(`Successfully converted ${sharedLists.length} shared lists to per-swimlane lists for board ${boardId}`); + } + + } catch (error) { + console.error('Error converting shared lists to per-swimlane:', error); + } + }, + async startAttachmentMigrationIfNeeded(boardId) { try { // Check if board has already been migrated diff --git a/client/components/boards/boardHeader.jade b/client/components/boards/boardHeader.jade index 372dfb07f..208753243 100644 --- a/client/components/boards/boardHeader.jade +++ b/client/components/boards/boardHeader.jade @@ -130,11 +130,6 @@ template(name="boardHeaderBar") a.board-header-btn-close.js-multiselection-reset(title="{{_ 'filter-clear'}}") | ❌ - if currentUser.isBoardAdmin - a.board-header-btn.js-restore-legacy-lists(title="{{_ 'restore-legacy-lists'}}") - | 🔄 - | {{_ 'legacy-lists'}} - .separator a.board-header-btn.js-toggle-sidebar(title="{{_ 'sidebar-open'}} {{_ 'or'}} {{_ 'sidebar-close'}}") | ☰ diff --git a/client/components/boards/boardHeader.js b/client/components/boards/boardHeader.js index 574b45e4d..72271f44a 100644 --- a/client/components/boards/boardHeader.js +++ b/client/components/boards/boardHeader.js @@ -152,32 +152,10 @@ BlazeComponent.extendComponent({ 'click .js-log-in'() { FlowRouter.go('atSignIn'); }, - 'click .js-restore-legacy-lists'() { - this.restoreLegacyLists(); - }, }, ]; }, - restoreLegacyLists() { - // Show confirmation dialog - if (confirm('Are you sure you want to restore legacy lists to their original shared state? This will make them appear in all swimlanes.')) { - // Call cron method to restore legacy lists - Meteor.call('cron.triggerRestoreLegacyLists', (error, result) => { - if (error) { - console.error('Error restoring legacy lists:', error); - alert(`Error: ${error.message}`); - } else { - console.log('Successfully triggered restore legacy lists migration:', result); - alert(`Migration triggered successfully. Job ID: ${result.jobId}`); - // Refresh the board to show the restored lists - setTimeout(() => { - window.location.reload(); - }, 2000); - } - }); - } - }, }).register('boardHeaderBar'); Template.boardHeaderBar.helpers({ diff --git a/imports/i18n/data/en.i18n.json b/imports/i18n/data/en.i18n.json index f106b6528..48daee11b 100644 --- a/imports/i18n/data/en.i18n.json +++ b/imports/i18n/data/en.i18n.json @@ -1488,10 +1488,5 @@ "weight": "Weight", "idle": "Idle", "complete": "Complete", - "cron": "Cron", - "legacy-lists": "Legacy Lists", - "restore-legacy-lists": "Restore Legacy Lists", - "legacy-lists-restore": "Legacy Lists Restore", - "legacy-lists-restore-description": "Restore legacy lists to their original shared state across all swimlanes", - "restoring-legacy-lists": "Restoring legacy lists" + "cron": "Cron" } diff --git a/models/boards.js b/models/boards.js index f62016d71..d08e7fe9e 100644 --- a/models/boards.js +++ b/models/boards.js @@ -778,10 +778,11 @@ Boards.helpers({ return this.permission === 'public'; }, - hasLegacyLists() { - return this.hasLegacyLists === true; + hasSharedListsConverted() { + return this.hasSharedListsConverted === true; }, + cards() { const ret = ReactiveCache.getCards( { boardId: this._id, archived: false }, diff --git a/server/00checkStartup.js b/server/00checkStartup.js index d4d152a1e..2ff0f2bcc 100644 --- a/server/00checkStartup.js +++ b/server/00checkStartup.js @@ -40,8 +40,6 @@ if (errors.length > 0) { // Import cron job storage for persistent job tracking import './cronJobStorage'; -// Import board migration detector for automatic board migrations -import './boardMigrationDetector'; - -// Import cron migration manager for cron-based migrations -import './cronMigrationManager'; +// Note: Automatic migrations are disabled - migrations only run when opening boards +// import './boardMigrationDetector'; +// import './cronMigrationManager'; diff --git a/server/boardMigrationDetector.js b/server/boardMigrationDetector.js index cd553ac47..b1dfb2f1e 100644 --- a/server/boardMigrationDetector.js +++ b/server/boardMigrationDetector.js @@ -337,13 +337,13 @@ class BoardMigrationDetector { // Export singleton instance export const boardMigrationDetector = new BoardMigrationDetector(); -// Start the detector on server startup -Meteor.startup(() => { - // Wait a bit for the system to initialize - Meteor.setTimeout(() => { - boardMigrationDetector.start(); - }, 10000); // Start after 10 seconds -}); +// Note: Automatic migration detector is disabled - migrations only run when opening boards +// Meteor.startup(() => { +// // Wait a bit for the system to initialize +// Meteor.setTimeout(() => { +// boardMigrationDetector.start(); +// }, 10000); // Start after 10 seconds +// }); // Meteor methods for client access Meteor.methods({ diff --git a/server/cronMigrationManager.js b/server/cronMigrationManager.js index a42e9b335..413f214ba 100644 --- a/server/cronMigrationManager.js +++ b/server/cronMigrationManager.js @@ -233,17 +233,6 @@ class CronMigrationManager { schedule: 'every 1 minute', status: 'stopped' }, - { - id: 'restore-legacy-lists', - name: 'Restore Legacy Lists', - description: 'Restore legacy lists to their original shared state across all swimlanes', - weight: 3, - completed: false, - progress: 0, - cronName: 'migration_restore_legacy_lists', - schedule: 'every 1 minute', - status: 'stopped' - } ]; } @@ -441,14 +430,6 @@ class CronMigrationManager { { name: 'Cleanup old data', duration: 1000 } ); break; - case 'restore-legacy-lists': - steps.push( - { name: 'Identify legacy lists', duration: 1000 }, - { name: 'Restore lists to shared state', duration: 2000 }, - { name: 'Update board settings', duration: 500 }, - { name: 'Verify restoration', duration: 500 } - ); - break; default: steps.push( { name: `Execute ${step.name}`, duration: 2000 }, @@ -465,10 +446,7 @@ class CronMigrationManager { async executeMigrationStep(jobId, stepIndex, stepData, stepId) { const { name, duration } = stepData; - if (stepId === 'restore-legacy-lists') { - await this.executeRestoreLegacyListsMigration(jobId, stepIndex, stepData); - } else { - // Simulate step execution with progress updates for other migrations + // Simulate step execution with progress updates for other migrations const progressSteps = 10; for (let i = 0; i <= progressSteps; i++) { const progress = Math.round((i / progressSteps) * 100); @@ -485,95 +463,6 @@ class CronMigrationManager { } } - /** - * Execute the restore legacy lists migration - */ - async executeRestoreLegacyListsMigration(jobId, stepIndex, stepData) { - const { name } = stepData; - - try { - // Import collections directly for server-side access - const { default: Boards } = await import('/models/boards'); - const { default: Lists } = await import('/models/lists'); - - // Step 1: Identify legacy lists - cronJobStorage.saveJobStep(jobId, stepIndex, { - progress: 25, - currentAction: 'Identifying legacy lists...' - }); - - const boards = Boards.find({}).fetch(); - const migrationDate = new Date('2025-10-10T21:14:44.000Z'); // Date of commit 719ef87efceacfe91461a8eeca7cf74d11f4cc0a - let totalLegacyLists = 0; - - for (const board of boards) { - const allLists = Lists.find({ boardId: board._id }).fetch(); - const legacyLists = allLists.filter(list => { - const listDate = list.createdAt || new Date(0); - return listDate < migrationDate && list.swimlaneId && list.swimlaneId !== ''; - }); - totalLegacyLists += legacyLists.length; - } - - // Step 2: Restore lists to shared state - cronJobStorage.saveJobStep(jobId, stepIndex, { - progress: 50, - currentAction: 'Restoring lists to shared state...' - }); - - let restoredCount = 0; - - for (const board of boards) { - const allLists = Lists.find({ boardId: board._id }).fetch(); - const legacyLists = allLists.filter(list => { - const listDate = list.createdAt || new Date(0); - return listDate < migrationDate && list.swimlaneId && list.swimlaneId !== ''; - }); - - // Restore legacy lists to shared state (empty swimlaneId) - for (const list of legacyLists) { - Lists.direct.update(list._id, { - $set: { - swimlaneId: '' - } - }); - restoredCount++; - } - - // Mark the board as having legacy lists - if (legacyLists.length > 0) { - Boards.direct.update(board._id, { - $set: { - hasLegacyLists: true - } - }); - } - } - - // Step 3: Update board settings - cronJobStorage.saveJobStep(jobId, stepIndex, { - progress: 75, - currentAction: 'Updating board settings...' - }); - - // Step 4: Verify restoration - cronJobStorage.saveJobStep(jobId, stepIndex, { - progress: 100, - currentAction: `Verification complete. Restored ${restoredCount} legacy lists.` - }); - - console.log(`Successfully restored ${restoredCount} legacy lists across ${boards.length} boards`); - - } catch (error) { - console.error('Error during restore legacy lists migration:', error); - cronJobStorage.saveJobStep(jobId, stepIndex, { - progress: 0, - currentAction: `Error: ${error.message}`, - status: 'error' - }); - throw error; - } - } /** * Execute a board operation job @@ -1420,53 +1309,6 @@ class CronMigrationManager { return stats; } - /** - * Trigger restore legacy lists migration - */ - async triggerRestoreLegacyListsMigration() { - try { - // Find the restore legacy lists step - const step = this.migrationSteps.find(s => s.id === 'restore-legacy-lists'); - if (!step) { - throw new Error('Restore legacy lists migration step not found'); - } - - // Create a job for this migration - const jobId = `restore_legacy_lists_${Date.now()}`; - cronJobStorage.addToQueue(jobId, 'migration', step.weight, { - stepId: step.id, - stepName: step.name, - stepDescription: step.description - }); - - // Save initial job status - cronJobStorage.saveJobStatus(jobId, { - jobType: 'migration', - status: 'pending', - progress: 0, - stepId: step.id, - stepName: step.name - }); - - // Execute the migration immediately - const jobData = { - stepId: step.id, - stepName: step.name, - stepDescription: step.description - }; - await this.executeMigrationJob(jobId, jobData); - - return { - success: true, - jobId: jobId, - message: 'Restore legacy lists migration triggered successfully' - }; - - } catch (error) { - console.error('Error triggering restore legacy lists migration:', error); - throw new Meteor.Error('migration-trigger-failed', `Failed to trigger migration: ${error.message}`); - } - } } // Export singleton instance @@ -1666,17 +1508,4 @@ Meteor.methods({ return boardMigrationDetector.forceScan(); }, - 'cron.triggerRestoreLegacyLists'() { - if (!this.userId) { - throw new Meteor.Error('not-authorized'); - } - - // Check if user is admin (optional - you can remove this if you want any user to trigger it) - const user = ReactiveCache.getCurrentUser(); - if (!user || !user.isAdmin) { - throw new Meteor.Error('not-authorized', 'Only administrators can trigger this migration'); - } - - return cronMigrationManager.triggerRestoreLegacyListsMigration(); - } }); From 679d210667de117a58212dc173a7ca95d5f57316 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Mon, 20 Oct 2025 00:24:46 +0300 Subject: [PATCH 023/183] Updated ChangeLog. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19981311a..60c6807cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,7 @@ This release adds the following new features: - [At Public Board, drag resize list width and swimlane height. For logged in users, fix adding labels](https://github.com/wekan/wekan/commit/351433524708e9a7ccb4795d9ca31a78904943ea). Thanks to xet7. -- [Legacy Lists button at one board view to restore missing lists/cards](https://github.com/wekan/wekan/commit/951d2e49379d5588f2aa11960e8945ab770c78d2). +- [When opening board, migrate from Shared Lists to Per-Swimlane Lists](https://github.com/wekan/wekan/commit/1e6252de7f26f3af14a99fb63b5dac27ba0576f3). Thanks to xet7. and fixes the following bugs: From eb6b42c4c9f99894fd93e62c9b3fceda3429c96c Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Mon, 20 Oct 2025 00:28:19 +0300 Subject: [PATCH 024/183] Fix syntax error at migrations. Thanks to xet7 ! --- server/cronMigrationManager.js | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/server/cronMigrationManager.js b/server/cronMigrationManager.js index 413f214ba..dd85c3743 100644 --- a/server/cronMigrationManager.js +++ b/server/cronMigrationManager.js @@ -447,19 +447,18 @@ class CronMigrationManager { const { name, duration } = stepData; // Simulate step execution with progress updates for other migrations - const progressSteps = 10; - for (let i = 0; i <= progressSteps; i++) { - const progress = Math.round((i / progressSteps) * 100); - - // Update step progress - cronJobStorage.saveJobStep(jobId, stepIndex, { - progress, - currentAction: `Executing: ${name} (${progress}%)` - }); - - // Simulate work - await new Promise(resolve => setTimeout(resolve, duration / progressSteps)); - } + const progressSteps = 10; + for (let i = 0; i <= progressSteps; i++) { + const progress = Math.round((i / progressSteps) * 100); + + // Update step progress + cronJobStorage.saveJobStep(jobId, stepIndex, { + progress, + currentAction: `Executing: ${name} (${progress}%)` + }); + + // Simulate work + await new Promise(resolve => setTimeout(resolve, duration / progressSteps)); } } From 91fb7d9e7063a46e8bec28c59a87b72e4389c0e1 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Mon, 20 Oct 2025 00:29:56 +0300 Subject: [PATCH 025/183] Updated ChangeLog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60c6807cb..eead036fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,8 @@ and fixes the following bugs: Thanks to xet7. - [Removed extra pipe characters](https://github.com/wekan/wekan/commit/caa6e615ff3c3681bf2b470a625eb39c6009b825). Thanks to xet7. +- [Fix syntax error at migrations](https://github.com/wekan/wekan/commit/eb6b42c4c9f99894fd93e62c9b3fceda3429c96c). + Thanks to xet7. Thanks to above GitHub users for their contributions and translators for their translations. From 1e53125499ef563ca3c65f786ac3525e5f50274c Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Mon, 20 Oct 2025 00:33:02 +0300 Subject: [PATCH 026/183] Fix opened card attachments button text to be at tooltip, not at opened card. Thanks to xet7 ! --- client/components/cards/attachments.jade | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/client/components/cards/attachments.jade b/client/components/cards/attachments.jade index efa66f513..34a9b2496 100644 --- a/client/components/cards/attachments.jade +++ b/client/components/cards/attachments.jade @@ -86,17 +86,17 @@ template(name="attachmentGallery") = name span.file-size ({{fileSize size}}) .attachment-actions - a.js-download(href="{{link}}?download=true", download="{{name}}") - | ⬇️(title="{{_ 'download'}}") + a.js-download(href="{{link}}?download=true", download="{{name}}", title="{{_ 'download'}}") + | ⬇️ if currentUser.isBoardMember unless currentUser.isCommentOnly unless currentUser.isWorker - a.js-rename - | ✏️(title="{{_ 'rename'}}") - a.js-confirm-delete - | 🗑️(title="{{_ 'delete'}}") - a.js-open-attachment-menu - | ☰(data-attachment-link="{{link}}" title="{{_ 'attachmentActionsPopup-title'}}") + a.js-rename(title="{{_ 'rename'}}") + | ✏️ + a.js-confirm-delete(title="{{_ 'delete'}}") + | 🗑️ + a.js-open-attachment-menu(data-attachment-link="{{link}}", title="{{_ 'attachmentActionsPopup-title'}}") + | ☰ // Migration spinner overlay if isAttachmentMigrating _id From e1902d58c129d26757ed3daa7c782244077c8775 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Mon, 20 Oct 2025 00:35:33 +0300 Subject: [PATCH 027/183] Updated ChangeLog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eead036fa..87aa150dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,8 @@ and fixes the following bugs: Thanks to xet7. - [Fix syntax error at migrations](https://github.com/wekan/wekan/commit/eb6b42c4c9f99894fd93e62c9b3fceda3429c96c). Thanks to xet7. +- [Fix opened card attachments button text to be at tooltip, not at opened card](https://github.com/wekan/wekan/commit/1e53125499ef563ca3c65f786ac3525e5f50274c). + Thanks to xet7. Thanks to above GitHub users for their contributions and translators for their translations. From 973a49526fdf22c143468d3d9db64269b1defa7d Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Mon, 20 Oct 2025 01:01:55 +0300 Subject: [PATCH 028/183] Fix Broken Hyperlinks in Markdown to HTML conversion. Thanks to xet7 ! Fixes #5932 --- client/lib/secureDOMPurify.js | 92 +++++++++++-------- imports/lib/secureDOMPurify.js | 59 ++++++------ packages/markdown/src/secureDOMPurify.js | 59 ++++++------ packages/markdown/src/template-integration.js | 6 +- 4 files changed, 113 insertions(+), 103 deletions(-) diff --git a/client/lib/secureDOMPurify.js b/client/lib/secureDOMPurify.js index c4e352e87..898687dad 100644 --- a/client/lib/secureDOMPurify.js +++ b/client/lib/secureDOMPurify.js @@ -3,43 +3,25 @@ import DOMPurify from 'dompurify'; // Centralized secure DOMPurify configuration to prevent XSS and CSS injection attacks export function getSecureDOMPurifyConfig() { return { - // Block dangerous elements that can cause XSS and CSS injection - FORBID_TAGS: [ - 'svg', 'defs', 'use', 'g', 'symbol', 'marker', 'pattern', 'mask', 'clipPath', - 'linearGradient', 'radialGradient', 'stop', 'animate', 'animateTransform', - 'animateMotion', 'set', 'switch', 'foreignObject', 'script', 'style', 'link', - 'meta', 'iframe', 'object', 'embed', 'applet', 'form', 'input', 'textarea', - 'select', 'option', 'button', 'label', 'fieldset', 'legend', 'frameset', - 'frame', 'noframes', 'base', 'basefont', 'isindex', 'dir', 'menu', 'menuitem' - ], - // Block dangerous attributes that can cause XSS and CSS injection - FORBID_ATTR: [ - 'xlink:href', 'href', 'onload', 'onerror', 'onclick', 'onmouseover', - 'onfocus', 'onblur', 'onchange', 'onsubmit', 'onreset', 'onselect', - 'onunload', 'onresize', 'onscroll', 'onkeydown', 'onkeyup', 'onkeypress', - 'onmousedown', 'onmouseup', 'onmouseover', 'onmouseout', 'onmousemove', - 'ondblclick', 'oncontextmenu', 'onwheel', 'ontouchstart', 'ontouchend', - 'ontouchmove', 'ontouchcancel', 'onabort', 'oncanplay', 'oncanplaythrough', - 'ondurationchange', 'onemptied', 'onended', 'onerror', 'onloadeddata', - 'onloadedmetadata', 'onloadstart', 'onpause', 'onplay', 'onplaying', - 'onprogress', 'onratechange', 'onseeked', 'onseeking', 'onstalled', - 'onsuspend', 'ontimeupdate', 'onvolumechange', 'onwaiting', 'onbeforeunload', - 'onhashchange', 'onpagehide', 'onpageshow', 'onpopstate', 'onstorage', - 'onunload', 'style', 'class', 'id', 'data-*', 'aria-*' - ], - // Allow only safe image formats and protocols + // Allow common markdown elements including anchor tags + ALLOWED_TAGS: ['a', 'p', 'br', 'strong', 'em', 'u', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li', 'blockquote', 'pre', 'code', 'img', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'hr', 'div', 'span'], + // Allow safe attributes including href for anchor tags + ALLOWED_ATTR: ['href', 'title', 'alt', 'src', 'width', 'height', 'target', 'rel'], + // Allow safe protocols for links ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i, - // Remove dangerous protocols + // Allow unknown protocols but be cautious ALLOW_UNKNOWN_PROTOCOLS: false, - // Sanitize URLs to prevent malicious content loading + // Sanitize DOM for security SANITIZE_DOM: true, - // Remove dangerous elements completely - KEEP_CONTENT: false, - // Additional security measures - ADD_ATTR: [], + // Keep content but sanitize it + KEEP_CONTENT: true, + // Block dangerous elements that can cause XSS + FORBID_TAGS: ['script', 'style', 'iframe', 'object', 'embed', 'applet', 'svg', 'defs', 'use', 'g', 'symbol', 'marker', 'pattern', 'mask', 'clipPath', 'linearGradient', 'radialGradient', 'stop', 'animate', 'animateTransform', 'animateMotion', 'set', 'switch', 'foreignObject', 'link', 'meta', 'form', 'input', 'textarea', 'select', 'option', 'button', 'label', 'fieldset', 'legend', 'frameset', 'frame', 'noframes', 'base', 'basefont', 'isindex', 'dir', 'menu', 'menuitem'], + // Block dangerous attributes but allow safe href + FORBID_ATTR: ['xlink:href', 'onload', 'onerror', 'onclick', 'onmouseover', 'onfocus', 'onblur', 'onchange', 'onsubmit', 'onreset', 'onselect', 'onunload', 'onresize', 'onscroll', 'onkeydown', 'onkeyup', 'onkeypress', 'onmousedown', 'onmouseup', 'onmouseover', 'onmouseout', 'onmousemove', 'ondblclick', 'oncontextmenu', 'onwheel', 'ontouchstart', 'ontouchend', 'ontouchmove', 'ontouchcancel', 'onabort', 'oncanplay', 'oncanplaythrough', 'ondurationchange', 'onemptied', 'onended', 'onerror', 'onloadeddata', 'onloadedmetadata', 'onloadstart', 'onpause', 'onplay', 'onplaying', 'onprogress', 'onratechange', 'onseeked', 'onseeking', 'onstalled', 'onsuspend', 'ontimeupdate', 'onvolumechange', 'onwaiting', 'onbeforeunload', 'onhashchange', 'onpagehide', 'onpageshow', 'onpopstate', 'onstorage', 'onunload', 'style', 'class', 'id', 'data-*', 'aria-*'], // Block data URIs that could contain malicious content ALLOW_DATA_ATTR: false, - // Custom hook to further sanitize content + // Custom hooks for additional security HOOKS: { uponSanitizeElement: function(node, data) { // Block any remaining dangerous elements @@ -51,14 +33,37 @@ export function getSecureDOMPurifyConfig() { return false; } - // Block img tags with SVG data URIs + // Block img tags with SVG data URIs that could contain malicious JavaScript if (node.tagName && node.tagName.toLowerCase() === 'img') { const src = node.getAttribute('src'); - if (src && (src.startsWith('data:image/svg') || src.endsWith('.svg'))) { - if (process.env.DEBUG === 'true') { - console.warn('Blocked potentially malicious SVG image:', src); + if (src) { + // Block all SVG data URIs to prevent XSS via embedded JavaScript + if (src.startsWith('data:image/svg') || src.endsWith('.svg')) { + if (process.env.DEBUG === 'true') { + console.warn('Blocked potentially malicious SVG image:', src); + } + return false; + } + + // Additional check for base64 encoded SVG with script tags + if (src.startsWith('data:image/svg+xml;base64,')) { + try { + const base64Content = src.split(',')[1]; + const decodedContent = atob(base64Content); + if (decodedContent.includes('