Skip to content

Commit 1fa4f7c

Browse files
committed
Merge branch 'release/0.8.75'
2 parents 8c2aab2 + 1b04ba1 commit 1fa4f7c

4 files changed

Lines changed: 57 additions & 6 deletions

File tree

.version.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
"strategy": "semver",
33
"major": 0,
44
"minor": 8,
5-
"patch": 74,
5+
"patch": 75,
66
"build": 0
77
}

src/Cms/Services/Widget/ContactFormWidget.php

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ private function renderField( array $field, string $idSuffix ): string
154154
{
155155
return '<div class="form-check mb-3">'
156156
. '<input type="checkbox" class="form-check-input" id="' . $this->esc( $id ) . '" name="' . $this->esc( $name ) . '" value="1"' . $reqAttr . '>'
157-
. '<label class="form-check-label" for="' . $this->esc( $id ) . '">' . $this->esc( $label ) . $reqMark . '</label>'
157+
. '<label class="form-check-label" for="' . $this->esc( $id ) . '">' . $this->labelWithLink( $label, $field ) . $reqMark . '</label>'
158158
. '</div>';
159159
}
160160

@@ -169,7 +169,7 @@ private function renderField( array $field, string $idSuffix ): string
169169
}
170170

171171
$control = '<div class="mb-3">';
172-
$control .= '<label class="form-label" for="' . $this->esc( $id ) . '">' . $this->esc( $label ) . $reqMark . '</label>';
172+
$control .= '<label class="form-label" for="' . $this->esc( $id ) . '">' . $this->labelWithLink( $label, $field ) . $reqMark . '</label>';
173173

174174
switch( $type )
175175
{
@@ -227,7 +227,7 @@ private function renderCheckboxes( array $field, string $idSuffix, string $name,
227227
$groups = FieldOptions::groups( $field );
228228

229229
$html = '<fieldset class="mb-3 contact-checkboxes">';
230-
$html .= '<legend class="form-label fs-6">' . $this->esc( $label ) . $reqMark . '</legend>';
230+
$html .= '<legend class="form-label fs-6">' . $this->labelWithLink( $label, $field ) . $reqMark . '</legend>';
231231

232232
foreach( $groups as $group )
233233
{
@@ -274,7 +274,7 @@ private function renderRadios( array $field, string $idSuffix, string $name, str
274274
$reqAttr = $required ? ' required' : '';
275275

276276
$html = '<fieldset class="mb-3 contact-radios">';
277-
$html .= '<legend class="form-label fs-6">' . $this->esc( $label ) . $reqMark . '</legend>';
277+
$html .= '<legend class="form-label fs-6">' . $this->labelWithLink( $label, $field ) . $reqMark . '</legend>';
278278

279279
foreach( FieldOptions::groups( $field ) as $group )
280280
{
@@ -382,6 +382,43 @@ private function session(): ?SessionManager
382382
return $this->_sessionManager;
383383
}
384384

385+
/**
386+
* Build a field's (escaped) label, optionally embedding a safe hyperlink.
387+
*
388+
* A field may declare a link via config:
389+
*
390+
* link: { text: "Volunteer Agreement", url: "/pages/release-form" }
391+
*
392+
* Use the literal token "{link}" in the label to control placement; if the
393+
* token is absent the link is appended. Label text, link text and URL are
394+
* all escaped, so only this controlled anchor is ever injected as HTML.
395+
*
396+
* @param string $label
397+
* @param array $field
398+
* @return string
399+
*/
400+
private function labelWithLink( string $label, array $field ): string
401+
{
402+
$escaped = $this->esc( $label );
403+
$link = $field['link'] ?? null;
404+
405+
if( !is_array( $link ) || empty( $link['url'] ) )
406+
{
407+
return $escaped;
408+
}
409+
410+
$url = $this->esc( (string) $link['url'] );
411+
$text = $this->esc( (string) ( $link['text'] ?? $link['url'] ) );
412+
$anchor = '<a href="' . $url . '" target="_blank" rel="noopener noreferrer">' . $text . '</a>';
413+
414+
if( str_contains( $escaped, '{link}' ) )
415+
{
416+
return str_replace( '{link}', $anchor, $escaped );
417+
}
418+
419+
return $escaped . ' ' . $anchor;
420+
}
421+
385422
/**
386423
* HTML-escape helper.
387424
*

tests/Unit/Cms/Services/Widget/ContactFormWidgetTest.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ private function widget(): ContactFormWidget
3737
]
3838
],
3939
[ 'name' => 'contact_pref', 'label' => 'Preferred Contact', 'type' => 'radio', 'required' => true, 'options' => [ 'Email', 'Phone' ] ],
40-
[ 'name' => 'start_date', 'label' => 'Start Date', 'type' => 'date', 'required' => false ]
40+
[ 'name' => 'start_date', 'label' => 'Start Date', 'type' => 'date', 'required' => false ],
41+
[ 'name' => 'agree', 'label' => 'I agree to the {link}', 'type' => 'checkbox', 'required' => true, 'link' => [ 'text' => 'Release Form', 'url' => '/pages/release-form' ] ]
4142
]
4243
]
4344
]
@@ -110,6 +111,17 @@ public function testRadioAndDateFieldsRender(): void
110111
$this->assertStringContainsString( 'name="start_date"', $html );
111112
}
112113

114+
public function testCheckboxLabelEmbedsConfiguredLinkAtToken(): void
115+
{
116+
$html = $this->widget()->render( [] );
117+
118+
$this->assertStringContainsString( 'href="/pages/release-form"', $html );
119+
$this->assertStringContainsString( 'target="_blank"', $html );
120+
$this->assertStringContainsString( '>Release Form</a>', $html );
121+
// The {link} token is replaced, not left as literal text.
122+
$this->assertStringNotContainsString( '{link}', $html );
123+
}
124+
113125
public function testTitleAndButtonCanBeOverriddenByAttributes(): void
114126
{
115127
$html = $this->widget()->render( [ 'title' => 'Custom Heading', 'button' => 'Go' ] );

versionlog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
## 0.8.75 2026-06-22
2+
13
## 0.8.74 2026-06-22
24

35
* Added multi select fields to forms.

0 commit comments

Comments
 (0)