Accept full meta HTML in Tags & Badges (name is label only).

Admins paste complete <meta> tags as the value; the name field is only a list label.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-10-03 18:39:20 +03:30
co-authored by Cursor
parent 68b985dee3
commit eb9b8b9fa3
5 changed files with 41 additions and 26 deletions
@@ -6,7 +6,7 @@ import { useT } from '../i18n/useT'
import styles from './WebsiteMetaTagModal.module.css' import styles from './WebsiteMetaTagModal.module.css'
const ANIMATION_MS = 220 const ANIMATION_MS = 220
const META_NAME_RE = /^[a-zA-Z0-9_.:-]{1,80}$/ const META_HTML_RE = /^<meta\b[^>]*>/i
interface WebsiteMetaTagModalProps { interface WebsiteMetaTagModalProps {
open: boolean open: boolean
@@ -65,11 +65,11 @@ export function WebsiteMetaTagModal({
e.preventDefault() e.preventDefault()
const trimmedName = name.trim() const trimmedName = name.trim()
const trimmedContent = content.trim() const trimmedContent = content.trim()
if (!trimmedName || !META_NAME_RE.test(trimmedName)) { if (!trimmedName) {
setFormError(t('website.badges.meta.nameInvalid')) setFormError(t('website.badges.meta.nameInvalid'))
return return
} }
if (!trimmedContent) { if (!trimmedContent || !META_HTML_RE.test(trimmedContent)) {
setFormError(t('website.badges.meta.contentRequired')) setFormError(t('website.badges.meta.contentRequired'))
return return
} }
+16 -14
View File
@@ -2435,7 +2435,7 @@ const en = {
'website.badges.errorLoad': 'Unable to load tags & badges.', 'website.badges.errorLoad': 'Unable to load tags & badges.',
'website.badges.errorSave': 'Unable to save tags & badges.', 'website.badges.errorSave': 'Unable to save tags & badges.',
'website.badges.meta.title': 'Custom meta tags', 'website.badges.meta.title': 'Custom meta tags',
'website.badges.meta.desc': 'Each row is added to the website head as', 'website.badges.meta.desc': 'Name is only a label. Value is the full tag, e.g.',
'website.badges.meta.sample': '<meta name="…" content="…">', 'website.badges.meta.sample': '<meta name="…" content="…">',
'website.badges.meta.add': 'Add meta tag', 'website.badges.meta.add': 'Add meta tag',
'website.badges.meta.modalTitle': 'Add meta tag', 'website.badges.meta.modalTitle': 'Add meta tag',
@@ -2445,12 +2445,13 @@ const en = {
'website.badges.meta.nameLabel': 'Name', 'website.badges.meta.nameLabel': 'Name',
'website.badges.meta.valueLabel': 'Value', 'website.badges.meta.valueLabel': 'Value',
'website.badges.meta.remove': 'Remove meta tag', 'website.badges.meta.remove': 'Remove meta tag',
'website.badges.meta.empty': 'No custom tags yet. Use + to add a name and value.', 'website.badges.meta.empty': 'No custom tags yet. Use + to paste a full meta tag.',
'website.badges.meta.namePlaceholder': 'name (e.g. google-site-verification)', 'website.badges.meta.namePlaceholder': 'Label (e.g. Google verification)',
'website.badges.meta.contentPlaceholder': 'value / content', 'website.badges.meta.contentPlaceholder':
'website.badges.meta.nameInvalid': '<meta name="google-site-verification" content="…">',
'Meta tag names may only use letters, numbers, and _ . : -', 'website.badges.meta.nameInvalid': 'Enter a short label for this meta tag.',
'website.badges.meta.contentRequired': 'Each meta tag needs both a name and a value.', 'website.badges.meta.contentRequired':
'Value must be a full meta tag starting with <meta …>.',
'website.badges.enamad.title': 'eNamad', 'website.badges.enamad.title': 'eNamad',
'website.badges.enamad.desc': 'website.badges.enamad.desc':
'If you have received an eNamad badge for your website, enter its HTML code below to show it on the site.', 'If you have received an eNamad badge for your website, enter its HTML code below to show it on the site.',
@@ -5353,7 +5354,7 @@ const fa: Record<MessageKey, string> = {
'website.badges.errorLoad': 'بارگذاری تگ و نشان‌ها ممکن نشد.', 'website.badges.errorLoad': 'بارگذاری تگ و نشان‌ها ممکن نشد.',
'website.badges.errorSave': 'ذخیره تگ و نشان‌ها ممکن نشد.', 'website.badges.errorSave': 'ذخیره تگ و نشان‌ها ممکن نشد.',
'website.badges.meta.title': 'تگ‌های متا سفارشی', 'website.badges.meta.title': 'تگ‌های متا سفارشی',
'website.badges.meta.desc': 'هر ردیف در هد وب‌سایت قرار می‌گیرد به‌صورت', 'website.badges.meta.desc': 'نام فقط برچسب است. مقدار کل تگ است، مثلاً',
'website.badges.meta.sample': '<meta name="…" content="…">', 'website.badges.meta.sample': '<meta name="…" content="…">',
'website.badges.meta.add': 'افزودن تگ متا', 'website.badges.meta.add': 'افزودن تگ متا',
'website.badges.meta.modalTitle': 'افزودن تگ متا', 'website.badges.meta.modalTitle': 'افزودن تگ متا',
@@ -5363,12 +5364,13 @@ const fa: Record<MessageKey, string> = {
'website.badges.meta.nameLabel': 'نام', 'website.badges.meta.nameLabel': 'نام',
'website.badges.meta.valueLabel': 'مقدار', 'website.badges.meta.valueLabel': 'مقدار',
'website.badges.meta.remove': 'حذف تگ متا', 'website.badges.meta.remove': 'حذف تگ متا',
'website.badges.meta.empty': 'هنوز تگ سفارشی ندارید. با + نام و مقدار اضافه کنید.', 'website.badges.meta.empty': 'هنوز تگ سفارشی ندارید. با + کل تگ متا را بچسبانید.',
'website.badges.meta.namePlaceholder': 'نام (مثلاً google-site-verification)', 'website.badges.meta.namePlaceholder': 'برچسب (مثلاً تأیید گوگل)',
'website.badges.meta.contentPlaceholder': 'مقدار / محتوا', 'website.badges.meta.contentPlaceholder':
'website.badges.meta.nameInvalid': '<meta name="google-site-verification" content="…">',
'نام تگ فقط می‌تواند شامل حروف، اعداد و _ . : - باشد.', 'website.badges.meta.nameInvalid': 'یک برچسب کوتاه برای این تگ متا وارد کنید.',
'website.badges.meta.contentRequired': 'هر تگ متا باید هم نام و هم مقدار داشته باشد.', 'website.badges.meta.contentRequired':
'مقدار باید یک تگ متای کامل باشد که با <meta …> شروع شود.',
'website.badges.enamad.title': 'اینماد', 'website.badges.enamad.title': 'اینماد',
'website.badges.enamad.desc': 'website.badges.enamad.desc':
'در صورتی که نشان اینماد برای وبسایت خود دریافت کرده اید، کد HTML آن را در زیر وارد کنید تا در وبسایت نمایش داده شود.', 'در صورتی که نشان اینماد برای وبسایت خود دریافت کرده اید، کد HTML آن را در زیر وارد کنید تا در وبسایت نمایش داده شود.',
@@ -27,14 +27,20 @@
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
font-size: 12px; font-size: 12px;
line-height: 1.4; line-height: 1.4;
color: var(--text-primary); color: #0f172a;
background: rgba(15, 23, 42, 0.55); background: #f8fafc;
border: 1px solid rgba(148, 163, 184, 0.28); border: 1px solid rgba(148, 163, 184, 0.5);
border-radius: 6px; border-radius: 6px;
unicode-bidi: isolate; unicode-bidi: isolate;
white-space: nowrap; white-space: nowrap;
} }
:global(html[data-theme='dark']) .metaSample {
color: var(--text-primary);
background: rgba(15, 23, 42, 0.55);
border-color: rgba(148, 163, 184, 0.28);
}
.metaList { .metaList {
list-style: none; list-style: none;
margin: 0; margin: 0;
@@ -66,15 +72,21 @@
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
font-size: 12px; font-size: 12px;
font-weight: 600; font-weight: 600;
color: var(--text-primary); color: #0f172a;
background: rgba(15, 23, 42, 0.45); background: #ffffff;
border: 1px solid rgba(148, 163, 184, 0.25); border: 1px solid rgba(148, 163, 184, 0.5);
border-radius: 6px; border-radius: 6px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
:global(html[data-theme='dark']) .metaName {
color: var(--text-primary);
background: rgba(15, 23, 42, 0.45);
border-color: rgba(148, 163, 184, 0.25);
}
.metaValue { .metaValue {
min-width: 0; min-width: 0;
font-size: 12px; font-size: 12px;
@@ -23,7 +23,7 @@ import sharedStyles from './ProductSettingsPage.module.css'
import settingsStyles from './WebsiteSettingsPage.module.css' import settingsStyles from './WebsiteSettingsPage.module.css'
import styles from './WebsiteBadgesPage.module.css' import styles from './WebsiteBadgesPage.module.css'
const META_NAME_RE = /^[a-zA-Z0-9_.:-]{1,80}$/ const META_HTML_RE = /^<meta\b[^>]*>/i
const MAX_META_TAGS = 40 const MAX_META_TAGS = 40
type TrustKind = 'enamad' | 'samandehi' type TrustKind = 'enamad' | 'samandehi'
@@ -152,11 +152,11 @@ export function WebsiteBadgesPage() {
const name = tag.name.trim() const name = tag.name.trim()
const content = tag.content.trim() const content = tag.content.trim()
if (!name && !content) continue if (!name && !content) continue
if (!name || !META_NAME_RE.test(name)) { if (!name) {
setError(t('website.badges.meta.nameInvalid')) setError(t('website.badges.meta.nameInvalid'))
return return
} }
if (!content) { if (!content || !META_HTML_RE.test(content)) {
setError(t('website.badges.meta.contentRequired')) setError(t('website.badges.meta.contentRequired'))
return return
} }
@@ -111,6 +111,7 @@ export interface WebsiteSchemaSettings {
sameAs: string[] sameAs: string[]
} }
/** name = admin label; content = full `<meta …>` HTML */
export interface WebsiteMetaTag { export interface WebsiteMetaTag {
id: string id: string
name: string name: string