mirror of
https://github.com/wekan/wekan.git
synced 2026-11-07 02:52:33 -05:00
Since #3986 a new card's title starting with [Text] followed by more text lost the prefix and got the label Text, created on the board when it had none - for everybody, without a hint. Titles pasted from ticket systems, mails and monitoring tools ([Follower] #123456 - ..., [EXTERNAL] ...) were changed and filled the board with unwanted labels. It is a Member Settings switch now, Turn [label] at the start of a new card title into a label (profile.titleBracketLabel), off by default: a title is kept as typed. Who wants #3986's behaviour turns it on. Both texts are translated in every language; low confidence for the constructed and rarest ones (tlh, vo and some 35 others). Thanks to jullbo and xet7 ! Fixes #6810.
86 lines
4.0 KiB
JavaScript
86 lines
4.0 KiB
JavaScript
'use strict';
|
|
|
|
// Pure helpers for GitHub issue #3986: quick-add-card bracket label syntax.
|
|
// Typing "[Fedora] Do a thing" in the quick-add-card textarea at the bottom of
|
|
// a list creates a card titled "Do a thing" with the "Fedora" label applied,
|
|
// creating the label on the board first if it does not already exist.
|
|
//
|
|
// Only a SINGLE bracket prefix at the very start of the title is parsed - this
|
|
// is deliberately not a general mid-title or multi-label syntax. Extracted so
|
|
// the parsing/resolution logic is unit-testable in plain Node, mirroring the
|
|
// other models/lib pure helpers (labelAutocomplete.js, memberAutocomplete.js).
|
|
|
|
// Parses a leading "[LabelName] " prefix off a quick-add-card title.
|
|
//
|
|
// Returns { title, labelName }:
|
|
// - When the title starts with a well-formed bracket prefix (non-empty
|
|
// content, no nested brackets, followed by at least one non-whitespace
|
|
// character), `labelName` is the trimmed bracket content and `title` is the
|
|
// remainder with the prefix stripped.
|
|
// - Otherwise (no brackets, empty brackets, nested brackets, or nothing left
|
|
// after the bracket) the title is returned unchanged and `labelName` is
|
|
// `null` - the bracket is treated as literal title text, not label syntax.
|
|
function parseQuickAddCardLabel(rawTitle) {
|
|
const title = rawTitle == null ? '' : String(rawTitle);
|
|
const trimmed = title.trim();
|
|
// [^[\]]* forbids a second '[' or ']' inside the brackets, so a title like
|
|
// "[a[b] rest" (nested/unbalanced brackets) never matches and is left as
|
|
// literal text, per the "defensive, not overly clever" scope.
|
|
const match = /^\[([^[\]]*)\](?:\s+(\S.*))?$/s.exec(trimmed);
|
|
if (!match) {
|
|
return { title: rawTitle, labelName: null };
|
|
}
|
|
const labelName = match[1].trim();
|
|
const rest = (match[2] || '').trim();
|
|
if (!labelName || !rest) {
|
|
// Empty bracket ("[] Do a thing") or nothing after the bracket
|
|
// ("[Fedora]" alone) - not the syntax, keep the literal title.
|
|
return { title: rawTitle, labelName: null };
|
|
}
|
|
return { title: rest, labelName };
|
|
}
|
|
|
|
// Finds an existing label on the board whose name matches `labelName`
|
|
// case-insensitively. Returns the label's `_id`, or `null` when no label
|
|
// matches (including when `labels` is empty/absent or `labelName` is falsy).
|
|
function findExistingLabelIdByName(labels, labelName) {
|
|
if (!Array.isArray(labels) || !labelName) {
|
|
return null;
|
|
}
|
|
const needle = String(labelName).trim().toLowerCase();
|
|
const found = labels.find(
|
|
label => String(label?.name == null ? '' : label.name).toLowerCase() === needle,
|
|
);
|
|
return found ? found._id : null;
|
|
}
|
|
|
|
// Picks a default color for a newly created label: the first palette color
|
|
// not already used by one of the board's labels, falling back to the first
|
|
// palette color when every color is already used. Mirrors
|
|
// Template.createLabelPopup.helpers.defaultColor() in
|
|
// client/components/cards/labels.js, so a label auto-created by the quick-add
|
|
// bracket syntax gets the same kind of color a manually created one would.
|
|
function pickDefaultLabelColor(labels, colors) {
|
|
const palette = Array.isArray(colors) && colors.length > 0 ? colors : ['green'];
|
|
const used = (Array.isArray(labels) ? labels : []).map(label => label?.color);
|
|
const available = palette.filter(color => !used.includes(color));
|
|
return available.length > 0 ? available[0] : palette[0];
|
|
}
|
|
|
|
// #6810: the title and the label a new card gets from what was typed. The
|
|
// "[Text] " prefix becomes a label only for a user who turned it on in Member
|
|
// Settings (profile.titleBracketLabel, off by default): otherwise a title
|
|
// pasted from a ticket system or a mail ("[EXTERNAL] ...") is kept exactly as
|
|
// it is, and no label is created.
|
|
function quickAddTitleParts(rawTitle, bracketLabelOn) {
|
|
if (bracketLabelOn) return parseQuickAddCardLabel(rawTitle);
|
|
return { title: rawTitle == null ? '' : String(rawTitle), labelName: null };
|
|
}
|
|
|
|
export {
|
|
quickAddTitleParts,
|
|
parseQuickAddCardLabel,
|
|
findExistingLabelIdByName,
|
|
pickDefaultLabelColor,
|
|
};
|