Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 27 additions & 14 deletions config/visual-editor.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,27 +84,40 @@
'core/details',
'core/search',
'core/latest-posts',
'artisanpack/callout',
],

'disabled_blocks' => [
// `core/navigation` is enabled by D4 — its editor experience is
// backed by the `wp_navigation` shim entity (B1) plus the C4
// REST surface, so the block's link-control picker and inner
// blocks both round-trip cleanly. The JS-side mirror in
// site-editor-app.tsx is updated alongside this entry.
'core/query',
'core/query-loop',
'core/post-content',
// E4 — re-enabled on the back of B1's expanded core-data shim
// and the C1–C5 REST surface. Each block has a renderer in
// every renderer package (Blade / React / Vue) and round-trips
// against the empty-state shim without crashing. See
// docs/block-library-audit.md for the per-block notes.
'core/template-part',
'core/post-title',
'core/post-content',
'core/post-excerpt',
'core/post-date',
'core/post-author',
'core/post-featured-image',
'core/site-logo',
'core/site-title',
'core/site-tagline',
'core/template-part',
'core/site-logo',
'core/navigation',
'artisanpack/callout',
],

'disabled_blocks' => [
// `core/navigation` was enabled by D4 and stays enabled in E4.
// `core/template-part`, `core/post-*`, and `core/site-*` are
// promoted to the allow-list above by E4 (#381). The blocks
// listed here remain deliberately deferred:
//
// - core/query / core/query-loop need a real loop runtime
// (V2 — `artisanpack-ui/cms-framework`).
// - The taxonomy/feed widgets need term + comment endpoints
// that the shim does not implement (V1.1+).
//
// The JS-side mirror in site-editor-app.tsx is updated
// alongside this entry.
'core/query',
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
'core/query-loop',
'core/latest-comments',
'core/archives',
'core/categories',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@php
use ArtisanPackUI\VisualEditorRendererBlade\Support\UrlSanitizer;

$label = isset( $attributes['label'] ) && is_string( $attributes['label'] ) ? $attributes['label'] : '';
$url = UrlSanitizer::safe( isset( $attributes['url'] ) && is_string( $attributes['url'] ) ? $attributes['url'] : '' );
$opensInNew = ! empty( $attributes['opensInNewTab'] );
$rel = isset( $attributes['rel'] ) && is_string( $attributes['rel'] ) ? $attributes['rel'] : '';
$title = isset( $attributes['title'] ) && is_string( $attributes['title'] ) ? $attributes['title'] : '';
$description = isset( $attributes['description'] ) && is_string( $attributes['description'] ) ? $attributes['description'] : '';

$classes = [ 'wp-block-navigation-item', 'wp-block-navigation-link' ];

if ( ! empty( $attributes['className'] ) && is_string( $attributes['className'] ) ) {
$classes[] = $attributes['className'];
}

$linkAttrs = '' !== $url ? sprintf( ' href="%s"', e( $url ) ) : '';

if ( $opensInNew ) {
$linkAttrs .= sprintf( ' target="_blank" rel="%s"', e( trim( 'noopener noreferrer ' . $rel ) ) );
} elseif ( '' !== $rel ) {
$linkAttrs .= sprintf( ' rel="%s"', e( $rel ) );
}

if ( '' !== $title ) {
$linkAttrs .= sprintf( ' title="%s"', e( $title ) );
}
@endphp
<li class="{{ implode( ' ', array_map( 'trim', $classes ) ) }}">
<a class="wp-block-navigation-item__content"{!! $linkAttrs !!}><span class="wp-block-navigation-item__label">{{ $label }}</span>@if ( '' !== $description )<span class="wp-block-navigation-item__description">{{ $description }}</span>@endif</a>
</li>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
@php
use ArtisanPackUI\VisualEditorRendererBlade\Support\UrlSanitizer;

$label = isset( $attributes['label'] ) && is_string( $attributes['label'] ) ? $attributes['label'] : '';
$url = UrlSanitizer::safe( isset( $attributes['url'] ) && is_string( $attributes['url'] ) ? $attributes['url'] : '' );
$opensInNew = ! empty( $attributes['opensInNewTab'] );
$rel = isset( $attributes['rel'] ) && is_string( $attributes['rel'] ) ? $attributes['rel'] : '';

$classes = [ 'wp-block-navigation-item', 'wp-block-navigation-submenu', 'has-child' ];

if ( ! empty( $attributes['className'] ) && is_string( $attributes['className'] ) ) {
$classes[] = $attributes['className'];
}

$linkAttrs = '' !== $url ? sprintf( ' href="%s"', e( $url ) ) : '';

if ( $opensInNew ) {
$linkAttrs .= sprintf( ' target="_blank" rel="%s"', e( trim( 'noopener noreferrer ' . $rel ) ) );
} elseif ( '' !== $rel ) {
$linkAttrs .= sprintf( ' rel="%s"', e( $rel ) );
}
@endphp
<li class="{{ implode( ' ', array_map( 'trim', $classes ) ) }}">
<a class="wp-block-navigation-item__content"{!! $linkAttrs !!}><span class="wp-block-navigation-item__label">{{ $label }}</span></a>
<ul class="wp-block-navigation__submenu-container">{!! $innerBlocksHtml !!}</ul>
</li>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
@php
$overlayMenu = isset( $attributes['overlayMenu'] ) && is_string( $attributes['overlayMenu'] ) ? $attributes['overlayMenu'] : 'mobile';
$orientation = isset( $attributes['orientation'] ) && is_string( $attributes['orientation'] ) ? $attributes['orientation'] : 'horizontal';
$itemsJustify = isset( $attributes['itemsJustification'] ) && is_string( $attributes['itemsJustification'] ) ? $attributes['itemsJustification'] : '';

$ariaLabel = isset( $attributes['ariaLabel'] ) && is_string( $attributes['ariaLabel'] ) ? $attributes['ariaLabel'] : '';

$classes = [ 'wp-block-navigation' ];

if ( 'horizontal' === $orientation ) {
$classes[] = 'is-horizontal';
} elseif ( 'vertical' === $orientation ) {
$classes[] = 'is-vertical';
}

if ( '' !== $itemsJustify ) {
$classes[] = 'items-justified-' . $itemsJustify;
}

if ( 'never' !== $overlayMenu ) {
$classes[] = 'is-responsive';
}

if ( ! empty( $attributes['className'] ) && is_string( $attributes['className'] ) ) {
$classes[] = $attributes['className'];
}

$navAttrs = '';

if ( '' !== $ariaLabel ) {
$navAttrs .= sprintf( ' aria-label="%s"', e( $ariaLabel ) );
}
@endphp
<nav class="{{ implode( ' ', array_map( 'trim', $classes ) ) }}"{!! $navAttrs !!}>
<ul class="wp-block-navigation__container">{!! $innerBlocksHtml !!}</ul>
</nav>
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
@php
use ArtisanPackUI\VisualEditorRendererBlade\Support\UrlSanitizer;

$align = isset( $attributes['textAlign'] ) && is_string( $attributes['textAlign'] ) ? $attributes['textAlign'] : '';
$showAvatar = ! isset( $attributes['showAvatar'] ) || ! empty( $attributes['showAvatar'] );
$showBio = ! empty( $attributes['showBio'] );
$avatarSize = isset( $attributes['avatarSize'] ) ? max( 1, (int) $attributes['avatarSize'] ) : 24;
$byline = isset( $attributes['byline'] ) && is_string( $attributes['byline'] ) ? $attributes['byline'] : '';
$isLink = ! empty( $attributes['isLink'] );

$name = isset( $attributes['_resolvedAuthorName'] ) && is_string( $attributes['_resolvedAuthorName'] ) ? $attributes['_resolvedAuthorName'] : '';
$bio = isset( $attributes['_resolvedAuthorBio'] ) && is_string( $attributes['_resolvedAuthorBio'] ) ? $attributes['_resolvedAuthorBio'] : '';
$authorUrl = UrlSanitizer::safe( isset( $attributes['_resolvedAuthorUrl'] ) && is_string( $attributes['_resolvedAuthorUrl'] ) ? $attributes['_resolvedAuthorUrl'] : '' );
$avatarUrl = UrlSanitizer::safe( isset( $attributes['_resolvedAuthorAvatar'] ) && is_string( $attributes['_resolvedAuthorAvatar'] ) ? $attributes['_resolvedAuthorAvatar'] : '' );

$classes = [ 'wp-block-post-author' ];

if ( '' !== $align ) {
$classes[] = 'has-text-align-' . $align;
}

if ( ! empty( $attributes['className'] ) && is_string( $attributes['className'] ) ) {
$classes[] = $attributes['className'];
}
@endphp
<div class="{{ implode( ' ', array_map( 'trim', $classes ) ) }}">
@if ( $showAvatar && '' !== $avatarUrl )
<div class="wp-block-post-author__avatar"><img alt="{{ $name }}" width="{{ $avatarSize }}" height="{{ $avatarSize }}" src="{{ $avatarUrl }}"/></div>
@endif
<div class="wp-block-post-author__content">
@if ( '' !== $byline )
<p class="wp-block-post-author__byline">{{ $byline }}</p>
@endif
@if ( $isLink && '' !== $authorUrl )
<p class="wp-block-post-author__name"><a href="{{ $authorUrl }}">{{ $name }}</a></p>
@else
<p class="wp-block-post-author__name">{{ $name }}</p>
@endif
@if ( $showBio && '' !== $bio )
<p class="wp-block-post-author__bio">{!! $bio !!}</p>
@endif
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@php
$align = isset( $attributes['textAlign'] ) && is_string( $attributes['textAlign'] ) ? $attributes['textAlign'] : '';

$classes = [ 'entry-content', 'wp-block-post-content' ];

if ( '' !== $align ) {
$classes[] = 'has-text-align-' . $align;
}

if ( ! empty( $attributes['className'] ) && is_string( $attributes['className'] ) ) {
$classes[] = $attributes['className'];
}

$content = isset( $attributes['_resolvedContent'] ) && is_string( $attributes['_resolvedContent'] ) ? $attributes['_resolvedContent'] : '';
@endphp
<div class="{{ implode( ' ', array_map( 'trim', $classes ) ) }}">{!! $content !!}</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@php
use ArtisanPackUI\VisualEditorRendererBlade\Support\UrlSanitizer;

$align = isset( $attributes['textAlign'] ) && is_string( $attributes['textAlign'] ) ? $attributes['textAlign'] : '';
$displayType = isset( $attributes['displayType'] ) && 'modified' === $attributes['displayType'] ? 'modified' : 'date';
$isLink = ! empty( $attributes['isLink'] );

$resolvedKey = 'modified' === $displayType ? '_resolvedModifiedDate' : '_resolvedDate';
$resolvedFormatKey = 'modified' === $displayType ? '_resolvedModifiedDateFormatted' : '_resolvedDateFormatted';

$datetime = isset( $attributes[ $resolvedKey ] ) && is_string( $attributes[ $resolvedKey ] ) ? $attributes[ $resolvedKey ] : '';
$formatted = isset( $attributes[ $resolvedFormatKey ] ) && is_string( $attributes[ $resolvedFormatKey ] ) ? $attributes[ $resolvedFormatKey ] : $datetime;
$permalink = UrlSanitizer::safe( isset( $attributes['_resolvedPermalink'] ) && is_string( $attributes['_resolvedPermalink'] ) ? $attributes['_resolvedPermalink'] : '' );

$classes = [ 'wp-block-post-date' ];

if ( '' !== $align ) {
$classes[] = 'has-text-align-' . $align;
}

if ( ! empty( $attributes['className'] ) && is_string( $attributes['className'] ) ) {
$classes[] = $attributes['className'];
}

$timeAttrs = '' !== $datetime ? sprintf( ' datetime="%s"', e( $datetime ) ) : '';
@endphp
<div class="{{ implode( ' ', array_map( 'trim', $classes ) ) }}">@if ( $isLink && '' !== $permalink )<a href="{{ $permalink }}"><time{!! $timeAttrs !!}>{{ $formatted }}</time></a>@else<time{!! $timeAttrs !!}>{{ $formatted }}</time>@endif</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
@php
use ArtisanPackUI\VisualEditorRendererBlade\Support\UrlSanitizer;

$align = isset( $attributes['textAlign'] ) && is_string( $attributes['textAlign'] ) ? $attributes['textAlign'] : '';
$moreText = isset( $attributes['moreText'] ) && is_string( $attributes['moreText'] ) ? $attributes['moreText'] : '';
$showMoreOnNewLine = ! isset( $attributes['showMoreOnNewLine'] ) || ! empty( $attributes['showMoreOnNewLine'] );

$excerpt = isset( $attributes['_resolvedExcerpt'] ) && is_string( $attributes['_resolvedExcerpt'] ) ? $attributes['_resolvedExcerpt'] : '';
$permalink = UrlSanitizer::safe( isset( $attributes['_resolvedPermalink'] ) && is_string( $attributes['_resolvedPermalink'] ) ? $attributes['_resolvedPermalink'] : '' );

$classes = [ 'wp-block-post-excerpt' ];

if ( '' !== $align ) {
$classes[] = 'has-text-align-' . $align;
}

if ( ! empty( $attributes['className'] ) && is_string( $attributes['className'] ) ) {
$classes[] = $attributes['className'];
}

$moreLink = '';

if ( '' !== $moreText && '' !== $permalink ) {
$moreLink = sprintf(
'<a class="wp-block-post-excerpt__more-link" href="%s">%s</a>',
e( $permalink ),
e( $moreText )
);
} elseif ( '' !== $moreText ) {
$moreLink = sprintf(
'<span class="wp-block-post-excerpt__more-text">%s</span>',
e( $moreText )
);
}
@endphp
<div class="{{ implode( ' ', array_map( 'trim', $classes ) ) }}">
<p class="wp-block-post-excerpt__excerpt">{!! $excerpt !!}@if ( '' !== $moreLink && ! $showMoreOnNewLine ) {!! $moreLink !!}@endif</p>
@if ( '' !== $moreLink && $showMoreOnNewLine )
<p class="wp-block-post-excerpt__more-text">{!! $moreLink !!}</p>
@endif
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
@php
use ArtisanPackUI\VisualEditorRendererBlade\Support\UrlSanitizer;

$isLink = ! empty( $attributes['isLink'] );
$linkTarget = isset( $attributes['linkTarget'] ) && is_string( $attributes['linkTarget'] ) ? $attributes['linkTarget'] : '';
$rel = isset( $attributes['rel'] ) && is_string( $attributes['rel'] ) ? $attributes['rel'] : '';
$aspect = isset( $attributes['aspectRatio'] ) && is_string( $attributes['aspectRatio'] ) ? $attributes['aspectRatio'] : '';
$scale = isset( $attributes['scale'] ) && is_string( $attributes['scale'] ) ? $attributes['scale'] : '';
$sizeSlug = isset( $attributes['sizeSlug'] ) && is_string( $attributes['sizeSlug'] ) ? $attributes['sizeSlug'] : '';

$imageUrl = UrlSanitizer::safe( isset( $attributes['_resolvedImageUrl'] ) && is_string( $attributes['_resolvedImageUrl'] ) ? $attributes['_resolvedImageUrl'] : '' );
$alt = isset( $attributes['_resolvedImageAlt'] ) && is_string( $attributes['_resolvedImageAlt'] ) ? $attributes['_resolvedImageAlt'] : '';
$width = isset( $attributes['_resolvedImageWidth'] ) ? (int) $attributes['_resolvedImageWidth'] : 0;
$height = isset( $attributes['_resolvedImageHeight'] ) ? (int) $attributes['_resolvedImageHeight'] : 0;
$permalink = UrlSanitizer::safe( isset( $attributes['_resolvedPermalink'] ) && is_string( $attributes['_resolvedPermalink'] ) ? $attributes['_resolvedPermalink'] : '' );

$classes = [ 'wp-block-post-featured-image' ];

if ( '' !== $sizeSlug ) {
$classes[] = 'size-' . $sizeSlug;
}

if ( ! empty( $attributes['className'] ) && is_string( $attributes['className'] ) ) {
$classes[] = $attributes['className'];
}

$styleParts = [];

if ( '' !== $aspect ) {
$styleParts[] = 'aspect-ratio:' . $aspect;
}

if ( '' !== $scale ) {
$styleParts[] = 'object-fit:' . $scale;
}

$style = '' !== implode( '', $styleParts ) ? sprintf( ' style="%s"', e( implode( ';', $styleParts ) ) ) : '';

$imgAttrs = sprintf( ' src="%s" alt="%s"', e( $imageUrl ), e( $alt ) );

if ( $width > 0 ) {
$imgAttrs .= sprintf( ' width="%d"', $width );
}

if ( $height > 0 ) {
$imgAttrs .= sprintf( ' height="%d"', $height );
}

$imgAttrs .= $style;
@endphp
<figure class="{{ implode( ' ', array_map( 'trim', $classes ) ) }}">
@if ( '' !== $imageUrl )
@if ( $isLink && '' !== $permalink )
@php
$linkAttrs = sprintf( ' href="%s"', e( $permalink ) );
if ( '_blank' === $linkTarget ) {
$linkAttrs .= sprintf( ' target="_blank" rel="%s"', e( trim( 'noopener noreferrer ' . $rel ) ) );
} elseif ( '' !== $rel ) {
$linkAttrs .= sprintf( ' rel="%s"', e( $rel ) );
}
@endphp
<a{!! $linkAttrs !!}><img{!! $imgAttrs !!}/></a>
@else
<img{!! $imgAttrs !!}/>
@endif
@endif
</figure>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
@php
use ArtisanPackUI\VisualEditorRendererBlade\Support\UrlSanitizer;

$rawLevel = isset( $attributes['level'] ) ? (int) $attributes['level'] : 2;
$level = max( 1, min( 6, $rawLevel ) );
$tag = 'h' . $level;
$align = isset( $attributes['textAlign'] ) && is_string( $attributes['textAlign'] ) ? $attributes['textAlign'] : '';
$isLink = ! empty( $attributes['isLink'] );
$linkTarget = isset( $attributes['linkTarget'] ) && is_string( $attributes['linkTarget'] ) ? $attributes['linkTarget'] : '';
$rel = isset( $attributes['rel'] ) && is_string( $attributes['rel'] ) ? $attributes['rel'] : '';
$linkClass = isset( $attributes['linkClass'] ) && is_string( $attributes['linkClass'] ) ? $attributes['linkClass'] : '';

$title = isset( $attributes['_resolvedTitle'] ) && is_string( $attributes['_resolvedTitle'] ) ? $attributes['_resolvedTitle'] : '';
$permalink = UrlSanitizer::safe( isset( $attributes['_resolvedPermalink'] ) && is_string( $attributes['_resolvedPermalink'] ) ? $attributes['_resolvedPermalink'] : '' );

$classes = [ 'wp-block-post-title' ];

if ( '' !== $align ) {
$classes[] = 'has-text-align-' . $align;
}

if ( ! empty( $attributes['className'] ) && is_string( $attributes['className'] ) ) {
$classes[] = $attributes['className'];
}

$linkAttrs = '';

if ( $isLink && '' !== $permalink ) {
$linkAttrs .= sprintf( ' href="%s"', e( $permalink ) );

if ( '' !== $linkClass ) {
$linkAttrs .= sprintf( ' class="%s"', e( $linkClass ) );
}

if ( '_blank' === $linkTarget ) {
$linkAttrs .= sprintf( ' target="_blank" rel="%s"', e( trim( 'noopener noreferrer ' . $rel ) ) );
} elseif ( '' !== $rel ) {
$linkAttrs .= sprintf( ' rel="%s"', e( $rel ) );
}
}
@endphp
<{{ $tag }} class="{{ implode( ' ', array_map( 'trim', $classes ) ) }}">@if ( $isLink && '' !== $permalink )<a{!! $linkAttrs !!}>{!! $title !!}</a>@else{!! $title !!}@endif</{{ $tag }}>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Escape post title output instead of rendering raw HTML.

Line 42 outputs $title with {!! $title !!}. That creates a stored-XSS path if _resolvedTitle is ever unsanitized upstream. Use escaped output for title text.

🔧 Proposed fix
-<{{ $tag }} class="{{ implode( ' ', array_map( 'trim', $classes ) ) }}">@if ( $isLink && '' !== $permalink )<a{!! $linkAttrs !!}>{!! $title !!}</a>@else{!! $title !!}@endif</{{ $tag }}>
+<{{ $tag }} class="{{ implode( ' ', array_map( 'trim', $classes ) ) }}">@if ( $isLink && '' !== $permalink )<a{!! $linkAttrs !!}>{{ $title }}</a>@else{{ $title }}@endif</{{ $tag }}>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<{{ $tag }} class="{{ implode( ' ', array_map( 'trim', $classes ) ) }}">@if ( $isLink && '' !== $permalink )<a{!! $linkAttrs !!}>{!! $title !!}</a>@else{!! $title !!}@endif</{{ $tag }}>
<{{ $tag }} class="{{ implode( ' ', array_map( 'trim', $classes ) ) }}">@if ( $isLink && '' !== $permalink )<a{!! $linkAttrs !!}>{{ $title }}</a>@else{{ $title }}@endif</{{ $tag }}>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/visual-editor-renderer-blade/resources/views/blocks/core/post-title.blade.php`
at line 42, The post title is being rendered as raw HTML using {!! $title !!},
creating a stored-XSS risk if upstream _resolvedTitle is unsanitized; update the
template to output an escaped title instead (use Blade's escaped output for the
$title variable) while keeping existing link logic ($isLink, $permalink,
$linkAttrs, $tag, $classes) intact so markup and attributes remain unchanged.

Loading