Enable generated campaign passwords
This commit is contained in:
@@ -124,6 +124,12 @@ action.
|
||||
5. Open **Mail settings** and select an available Mail profile. The campaign
|
||||
stores only `server.mail_profile_id`; it never accepts SMTP/IMAP settings,
|
||||
usernames, passwords, or credential references.
|
||||
If the selected profile permits a campaign-scoped Mail credential, that
|
||||
credential remains Mail-owned even though it is created from this surface.
|
||||
For that credential and for password-valued campaign fields, the shared
|
||||
password generator keeps its candidate separate from the form until **Use
|
||||
password** is explicitly confirmed. Copying a candidate does not save or
|
||||
submit it.
|
||||
6. Save the editable version, validate the relevant sections, and resolve every
|
||||
blocking issue. Warnings remain explicit review decisions.
|
||||
7. Build the exact messages and inspect recipient, addressing, template,
|
||||
|
||||
@@ -844,7 +844,7 @@ manifest = ModuleManifest(
|
||||
id="campaigns.workflow.prepare-validate-and-build",
|
||||
title="Prepare, validate, and build a campaign",
|
||||
summary="Turn governed recipient, template, attachment, and Mail-profile inputs into exact built messages for review.",
|
||||
body="Prepare each input in its owning surface, resolve every blocking validation issue, and build exact recipient messages before review. Recipient data can activate or deactivate every currently opposite-state row as one explicitly confirmed draft change; saving it creates the normal Campaign version evidence and invalidates stale validation, build, and review state. Campaign freezes recipient and attachment evidence for the selected version; later source changes do not silently alter that build. When the Templates module is installed, its single Templates navigation entry owns the reusable library while campaign-specific composition remains in the campaign workspace.",
|
||||
body="Prepare each input in its owning surface, resolve every blocking validation issue, and build exact recipient messages before review. Recipient data can activate or deactivate every currently opposite-state row as one explicitly confirmed draft change; saving it creates the normal Campaign version evidence and invalidates stale validation, build, and review state. New campaign credentials and password-valued fields offer the shared secure generator; its candidate remains separate until Use password is confirmed. Campaign freezes recipient and attachment evidence for the selected version; later source changes do not silently alter that build. When the Templates module is installed, its single Templates navigation entry owns the reusable library while campaign-specific composition remains in the campaign workspace.",
|
||||
layer="configured",
|
||||
documentation_types=("user",),
|
||||
audience=("campaign_manager", "campaign_author"),
|
||||
@@ -901,7 +901,7 @@ manifest = ModuleManifest(
|
||||
"steps": [
|
||||
"Set campaign-wide fields and purpose, then define the recipient fields and templates.",
|
||||
"Import or select recipients, optionally confirm a counted activate-all or deactivate-all draft action, and inspect provenance, exclusions, and review-required rows.",
|
||||
"Select managed attachment versions and an authorized Mail profile when those capabilities are used.",
|
||||
"Select managed attachment versions and an authorized Mail profile when those capabilities are used; generate a new password only when creating a campaign credential or password-valued field, then explicitly confirm the candidate.",
|
||||
"Validate the relevant sections and resolve every blocker without hiding warnings.",
|
||||
"Build the selected version and inspect representative and exceptional rendered messages.",
|
||||
],
|
||||
|
||||
@@ -465,7 +465,7 @@ export default function MailSettingsPage({ settings, campaignId, view = "setting
|
||||
<input value={credentialDraft.username} disabled={credentialSaving} onChange={(event) => setCredentialDraft({ ...credentialDraft, username: event.target.value })} />
|
||||
</FormField>
|
||||
<FormField label="Password">
|
||||
<PasswordField value={credentialDraft.password} onValueChange={(password) => setCredentialDraft({ ...credentialDraft, password })} disabled={credentialSaving} autoComplete="new-password" />
|
||||
<PasswordField value={credentialDraft.password} onValueChange={(password) => setCredentialDraft({ ...credentialDraft, password })} disabled={credentialSaving} generator autoComplete="new-password" />
|
||||
</FormField>
|
||||
</div>
|
||||
<div className="form-grid two">
|
||||
|
||||
@@ -18,6 +18,7 @@ export default function FieldValueInput({ fieldType = "string", value, disabled
|
||||
inputClassName={className}
|
||||
value={valueToInputText(value, normalizedType)}
|
||||
disabled={disabled}
|
||||
generator
|
||||
placeholder={placeholder}
|
||||
autoComplete="new-password"
|
||||
onValueChange={(nextValue) => onChange(inputValueToFieldValue(normalizedType, nextValue))}
|
||||
|
||||
@@ -4,6 +4,8 @@ import { readFileSync } from "node:fs";
|
||||
const workspace = readFileSync("src/features/campaigns/CampaignWorkspace.tsx", "utf8");
|
||||
const overview = readFileSync("src/features/campaigns/CampaignOverviewPage.tsx", "utf8");
|
||||
const recipients = readFileSync("src/features/campaigns/RecipientDataPage.tsx", "utf8");
|
||||
const fieldValueInput = readFileSync("src/features/campaigns/components/FieldValueInput.tsx", "utf8");
|
||||
const mailSettings = readFileSync("src/features/campaigns/MailSettingsPage.tsx", "utf8");
|
||||
const api = readFileSync("src/api/campaigns.ts", "utf8");
|
||||
|
||||
assert.match(workspace, /<CampaignOverviewPage settings=\{settings\} auth=\{auth\}/);
|
||||
@@ -23,5 +25,7 @@ assert.match(recipients, /requestBulkActivation\(false\)/);
|
||||
assert.match(recipients, /const count = inlineEntries\.filter/);
|
||||
assert.match(recipients, /<ConfirmDialog[\s\S]*bulkActivation/);
|
||||
assert.match(recipients, /replaceInlineEntries\(inlineEntries\.map/);
|
||||
assert.match(fieldValueInput, /<PasswordField[\s\S]*generator[\s\S]*autoComplete="new-password"/);
|
||||
assert.match(mailSettings, /disabled=\{credentialSaving\} generator autoComplete="new-password"/);
|
||||
|
||||
console.log("Campaign lifecycle UI structural contract passed.");
|
||||
|
||||
Reference in New Issue
Block a user