<?xml version="1.0"?><artefact xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="artefact.xsd" name="AgilePress Content Block for ACF" slug="agilepress-content-block-for-acf" type="code-package" schemaVersion="2">
  <file path="readme.txt">
    <description>This file contains the readme information for the block. It is used to provide information about the block, its usage, and any other relevant details.</description>
    <content><![CDATA[=== AgilePress Content Block for ACF ===

Contributors:       AgilePress
Donate link:        https://agilepress.net
Tags:               block, acf, custom-fields, dynamic-content, gutenberg
Tested up to:       6.9
Stable tag:         1.1.0
Requires at least:  6.1
Requires PHP:       7.0
License:            GPLv2 or later
License URI:        https://www.gnu.org/licenses/gpl-2.0.html

Display ACF content using custom tags in HTML with support for groups, repeaters, galleries, and conditionals.

== Description ==

AgilePress Content Block for ACF is a versatile tool developed by [AgilePress](https://agilepress.net) that allows you to create dynamic HTML content with Advanced Custom Fields integration. It supports four powerful syntaxes for different ACF field types:

* **Simple fields**: Use `{field_name}` for text, number, and other simple fields
* **Group fields**: Use `{group_name:sub_field_name}` to access fields within ACF groups
* **Repeaters & Galleries**: Use `{{repeater_name}}` HTML content with `{sub_field}` tags `{{/repeater_name}}` to loop through repeater fields and image galleries
* **Conditionals**: Use `{{#if field}}...{{/if}}` for conditional content display with support for `=` and `!=` comparisons

This block is perfect for:

* Displaying custom field data in formatted HTML
* Creating dynamic templates with ACF fields
* Building complex layouts that pull data from custom fields
* Looping through repeater fields and image galleries
* Accessing nested group field data
* Showing/hiding content based on field values
* Integrating ACF data seamlessly into your content

The block provides an intuitive sidebar editor where you can write HTML and insert ACF field tags. On the frontend, these tags are automatically replaced with the actual values from your Advanced Custom Fields.

**Requirements:**
* Advanced Custom Fields (free or PRO version)
* WordPress 6.1 or higher
* PHP 7.0 or higher

== Installation ==

1. Make sure you have **Advanced Custom Fields** (free or PRO) installed and activated
2. Upload the plugin files to the `/wp-content/plugins/agilepress-content-block-for-acf` directory, or install the plugin through the WordPress plugins screen directly
3. Activate the plugin through the 'Plugins' screen in WordPress
4. Add the block to any post or page by searching for "AgilePress Content Block for ACF"
5. Start using ACF field tags in your HTML content via the sidebar settings

== Frequently Asked Questions ==

= How do I use simple ACF field tags? =

Simply wrap your ACF field name in single curly braces. For example, if you have a field named "title", use `{title}` in your HTML content.

= How do I access fields within an ACF group? =

Use the colon syntax: `{group_name:sub_field_name}`. For example, if you have a group called "location" with a field "latitude", use `{location:latitude}`.

= How do I loop through repeater fields? =

Use double curly braces: `{{repeater_name}}` your HTML with `{sub_field}` tags `{{/repeater_name}}`. The content between the opening and closing tags will be repeated for each row.

Example:
```html
{{team_members}}
  <div>{name} - {position}</div>
{{/team_members}}
```

= How do I use conditionals? =

Use the `{{#if}}...{{/if}}` syntax. You can check if a field exists, equals a value, or doesn't equal a value:

```html
{{#if field}}
  Content if field exists and is not empty
{{/if}}

{{#if status = active}}
  Content if status equals "active"
{{/if}}

{{#if status != draft}}
  Content if status is not "draft"
{{/if}}

{{#if featured}}
  Featured content
{{#elseif popular}}
  Popular content
{{#else}}
  Regular content
{{/if}}
```

= How do I display image galleries? =

Gallery fields work the same as repeaters. Use `{{gallery_field}}` with image sub-fields like `{url}`, `{alt}`, `{caption}` to loop through all images.

Example:
```html
{{gallery}}
  <img src="{url}" alt="{alt}" />
{{/gallery}}
```

= What types of ACF fields are supported? =

The block supports:

* **Simple fields**: text, textarea, number, email, url, wysiwyg, etc.
* **Group fields**: access sub-fields using colon notation `{group:subfield}`
* **Repeater fields**: loop through rows with `{{repeater}}...{{/repeater}}`
* **Gallery fields**: loop through images (exposes `{url}`, `{alt}`, `{caption}`, `{width}`, `{height}`, etc.)
* **Image fields**: automatically extracts the URL
* **Link fields**: extracts url, title, and target
* **True/False fields**: use with conditionals for show/hide logic

= Do I need Advanced Custom Fields installed? =

Yes, this block requires **Advanced Custom Fields** (free or PRO version) to be installed and activated. Without ACF, the block will display a message asking you to install it.

= Can I use HTML tags? =

Yes! You can use any HTML markup along with your ACF field tags. The block preserves your HTML structure while replacing the field tags with actual values. All HTML is sanitized using WordPress security functions.

= Can I nest repeaters or conditionals? =

Currently, nested repeaters and nested conditionals are not supported. However, you can use repeaters and conditionals at the same level and combine them with group fields for complex layouts.

= Does this work with ACF PRO features? =

Yes, the block works with both ACF free and PRO versions. It supports all standard field types including those exclusive to PRO like repeaters, flexible content (as repeaters), and gallery fields.

= Where can I get support? =

For support, please visit the plugin's support forum on WordPress.org or contact AgilePress directly.

== Screenshots ==

1. Block editor showing the sidebar settings panel with HTML content and ACF field tags
2. Inspector panel displaying the comprehensive syntax guide including conditionals
3. Frontend display with ACF values automatically replaced
4. Example of a repeater field loop displaying team members
5. Example of group field access for structured data
6. Gallery field loop displaying multiple images
7. Conditional content display based on field values

== Changelog ==

= 1.1.0 =
* Enhanced conditional syntax documentation
* Improved syntax guide clarity in the editor
* Updated documentation for if/elseif/else structure
* Minor bug fixes and improvements

= 1.0.0 =
* Initial public release
* Support for simple ACF field tags using `{field_name}` syntax
* Support for group fields using `{group:subfield}` syntax
* Support for repeater and gallery loops using `{{field}}...{{/field}}` syntax
* Support for conditional blocks using `{{#if}}...{{/if}}` syntax
* Conditional comparisons with `=` and `!=` operators
* Support for `{{#elseif}}` and `{{#else}}` in conditionals
* HTML content editor with comprehensive syntax guide
* Real-time preview in the editor with syntax highlighting
* Security: All output properly escaped following WordPress standards
* Compatibility: Works with ACF free and PRO versions
* Tested with WordPress 6.8 and PHP 8.x

== Usage Examples ==

**Example 1 - Simple field display:**
```html
<div class="location">
  <p>Latitude: {latitude}</p>
  <p>Longitude: {longitude}</p>
</div>
```

**Example 2 - Group field access:**
```html
<div class="contact">
  <h3>{contact_info:name}</h3>
  <p>Email: <a href="mailto:{contact_info:email}">{contact_info:email}</a></p>
  <p>Phone: {contact_info:phone}</p>
</div>
```

**Example 3 - Repeater field loop:**
```html
<ul class="team-members">
  {{team_member}}
    <li>
      <h4>{name}</h4>
      <p>{position}</p>
      <p>{bio}</p>
    </li>
  {{/team_member}}
</ul>
```

**Example 4 - Gallery field loop:**
```html
<div class="gallery">
  {{project_images}}
    <figure>
      <img src="{url}" alt="{alt}" width="{width}" height="{height}" />
      <figcaption>{caption}</figcaption>
    </figure>
  {{/project_images}}
</div>
```

**Example 5 - Conditional display:**
```html
<article class="post">
  {{#if featured}}
    <span class="badge">Featured</span>
  {{/if}}
  
  <h2>{title}</h2>
  
  {{#if status = published}}
    <p class="published-date">Published: {date}</p>
  {{#elseif status = draft}}
    <p class="draft-notice">This is a draft</p>
  {{#else}}
    <p class="pending-notice">Pending review</p>
  {{/if}}
  
  {{#if author_name != Anonymous}}
    <p>By {author_name}</p>
  {{/if}}
</article>
```

**Example 6 - Complex combination:**
```html
<article class="project">
  <header>
    {{#if is_featured}}
      <span class="featured-badge">Featured Project</span>
    {{/if}}
    
    <h2>{project_title}</h2>
    <p class="meta">
      Location: {location:city}, {location:country}<br>
      Date: {project_date}
    </p>
  </header>
  
  <div class="gallery">
    {{project_gallery}}
      <figure>
        <img src="{url}" alt="{alt}" loading="lazy" />
        <figcaption>{caption}</figcaption>
      </figure>
    {{/project_gallery}}
  </div>
  
  {{#if features}}
    <div class="features">
      <h3>Project Features</h3>
      <ul>
        {{features}}
          <li>
            <strong>{feature_name}:</strong> {feature_value}
          </li>
        {{/features}}
      </ul>
    </div>
  {{/if}}
  
  <footer>
    {{#if client_type = commercial}}
      <p><strong>Commercial Project</strong></p>
    {{/if}}
    
    <p>Client: {client_info:company_name}</p>
    
    {{#if client_info:email != none}}
      <p>Contact: {client_info:email}</p>
    {{/if}}
  </footer>
</article>
```

== Additional Information ==

**Security:**
All field values are properly escaped using WordPress security functions (`esc_html()`, `esc_url()`, `wp_kses_post()`) to prevent XSS attacks and ensure safe output.

**Performance:**
The block uses WordPress native functions and ACF's optimized field retrieval methods. Field values are processed server-side during render, ensuring optimal frontend performance.

**Compatibility:**
* WordPress 6.1+ (tested up to 6.9)
* PHP 7.0+ (tested up to 8.3)
* ACF free version 5.0+
* ACF PRO 5.0+
* Classic and Block themes

**Developer Notes:**
The block follows WordPress coding standards and uses proper namespacing (`agilepress_` prefix). All code is well-documented and follows best practices for WordPress plugin development.]]></content>
  </file>
  <file path="src/block.json">
    <description>This file contains metadata about the block including its name, title, category, icon, and other properties.</description>
    <content><![CDATA[{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "agilepress/content-block-for-acf",
	"version": "1.1.0",
	"title": "AgilePress Content Block for ACF",
	"category": "widgets",
	"icon": "editor-code",
	"description": "Display Advanced Custom Fields content using custom tags within HTML.",
	"example": {
		"attributes": {
			"content": "<div class=\"acf-example\">\n  <p>Location: {latitud}, {longitud}</p>\n</div>"
		}
	},
	"attributes": {
		"content": {
			"type": "string",
			"default": ""
		}
	},
	"supports": {
		"html": false,
		"anchor": true,
		"align": true
	},
	"textdomain": "agilepress-content-block-for-acf",
	"editorScript": "file:./index.js",
	"editorStyle": "file:./index.css",
	"style": "file:./style-index.css",
	"render": "file:./render.php"
}]]></content>
  </file>
  <file path="src/index.js">
    <description>This file registers the block, specifies the edit and save functions, and loads the block's metadata</description>
    <content><![CDATA[/**
 * Registers a new block provided a unique name and an object defining its behavior.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
 */
import { registerBlockType } from '@wordpress/blocks';

/**
 * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
 * All files containing `style` keyword are bundled together. The code used
 * gets applied both to the front of your site and to the editor.
 *
 * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
 */
import './style.scss';

/**
 * Internal dependencies
 */
import Edit from './edit';
import metadata from './block.json';

/**
 * Every block starts by registering a new block type definition.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
 */
registerBlockType( metadata.name, {
	/**
	 * @see ./edit.js
	 */
	edit: Edit,
} );
]]></content>
  </file>
  <file path="src/edit.js">
    <description>This file contains the edit function for the block which is responsible for rendering the block in the editor.</description>
    <content><![CDATA[/**
 * Retrieves the translation of text.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
 */
import { __ } from '@wordpress/i18n';

/**
 * React hook that is used to mark the block wrapper element.
 * It provides all the necessary props like the class name.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
 */
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';

/**
 * WordPress dependencies
 */
import { PanelBody, TextareaControl } from '@wordpress/components';

/**
 * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
 * Those files can contain any CSS code that gets applied to the editor.
 *
 * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
 */
import './editor.scss';

/**
 * The edit function describes the structure of your block in the context of the
 * editor. This represents what the editor will render when the block is used.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
 *
 * @param {Object} props Block properties.
 * @return {Element} Element to render.
 */
export default function Edit( { attributes, setAttributes } ) {
	const { content } = attributes;
	const blockProps = useBlockProps();

	const handleContentChange = ( newContent ) => {
		setAttributes( { content: newContent } );
	};

	/**
	 * Basic sanitization to prevent XSS in the editor preview.
	 * Removes script tags and on* attributes.
	 *
	 * @param {string} html The raw HTML.
	 * @return {string} Sanitized HTML.
	 */
	const sanitizePreview = ( html ) => {
		if ( ! html ) return '';
		return html
			.replace( /<script\b[^>]*>([\s\S]*?)<\/script>/gim, '' ) // Remove scripts
			.replace( /<[^>]+>/gim, ( tag ) => {
				// Remove event handlers like onclick, onmouseover, etc. from tags
				return tag.replace( /\son[a-z]+\s*=\s*['"]([^'"]*)['"']'/gim, '' );
			} );
	};

	// Replace ACF tags with placeholder text for preview
	const getPreviewContent = () => {
		if ( ! content ) {
			return '';
		}

		// 1. Sanitize first to prevent XSS in the editor
		let preview = sanitizePreview( content );

		// 2. Highlight conditional blocks
		preview = preview.replace(
			/\{\{#if ([^}]+)\}\}(.*?)(?:\{\{#elseif ([^}]+)\}\}(.*?))*(?:\{\{#else\}\}(.*?))?\{\{\/if\}\}/gs,
			( match ) => {
				return `<div class="acf-conditional-preview"><strong>Conditional:</strong>${ match }</div>`;
			}
		);

		// 3. Highlight repeater loops
		preview = preview.replace(
			/\{\{([^}]+)\}\}(.*?)\{\{\/\1\}\}/gs,
			( match, fieldName ) => {
				return `<div class="acf-loop-preview"><strong>Loop: {{${ fieldName }}}</strong>${ match }<strong>{{/${ fieldName }}}</strong></div>`;
			}
		);

		// 4. Highlight group fields
		preview = preview.replace(
			/\{([^}:]+):([^}]+)\}/g,
			( match, groupName, subField ) => {
				return `<span class="acf-group-preview">{${ groupName }:${ subField }}</span>`;
			}
		);

		// 5. Highlight simple fields
		preview = preview.replace(
			/\{([^}:]+)\}/g,
			( match, fieldName ) => {
				return `<span class="acf-tag-preview">{${ fieldName }}</span>`;
			}
		);

		return preview;
	};

	return (
		<>
			<InspectorControls>
				<PanelBody
					title={ __( 'Content Settings', 'agilepress-content-block-for-acf' ) }
					initialOpen={ true }
				>
					<TextareaControl
						label={ __( 'HTML Content with ACF Tags', 'agilepress-content-block-for-acf' ) }
						help={
							<>
								<p><strong>{ __( 'Syntax Guide:', 'agilepress-content-block-for-acf' ) }</strong></p>

								<p>
									{ __( 'Simple fields:', 'agilepress-content-block-for-acf' ) }
									<br />
									<code>{ '\u007bfield_name\u007d' }</code>
								</p>

								<p>
									{ __( 'Group fields:', 'agilepress-content-block-for-acf' ) }
									<br />
									<code>{ '\u007bgroup_name:sub_field_name\u007d' }</code>
								</p>

								<p>
									{ __( 'Repeaters / Galleries:', 'agilepress-content-block-for-acf' ) }
									<br />
									<code>{ '\u007b\u007brepeater_name\u007d\u007d' }</code>
									<br />
									<code>{ '  \u007bsub_field\u007d' }</code>
									<br />
									<code>{ '\u007b\u007b/repeater_name\u007d\u007d' }</code>
								</p>

								<p>
									{ __( 'Conditionals:', 'agilepress-content-block-for-acf' ) }
									<br />
									<code>{ '\u007b\u007b#if field\u007d\u007d...\u007b\u007b/if\u007d\u007d' }</code>
									<br />
									<code>{ '\u007b\u007b#if field = value\u007d\u007d...\u007b\u007b/if\u007d\u007d' }</code>
									<br />
									<code>{ '\u007b\u007b#if field != value\u007d\u007d...\u007b\u007b/if\u007d\u007d' }</code>
								</p>

								<p>
									{ __( 'With elseif and else (all closed with /if):', 'agilepress-content-block-for-acf' ) }
									<br />
									<code>{ '\u007b\u007b#if field\u007d\u007d...' }</code>
									<br />
									<code>{ '\u007b\u007b#elseif field2\u007d\u007d...' }</code>
									<br />
									<code>{ '\u007b\u007b#else\u007d\u007d...' }</code>
									<br />
									<code>{ '\u007b\u007b/if\u007d\u007d' }</code>
								</p>
							</>
						}
						value={ content }
						onChange={ handleContentChange }
						rows={ 15 }
					/>
				</PanelBody>
			</InspectorControls>

			<div { ...blockProps }>
				<div className="agilepress-content-block-for-acf-editor">
					{ content ? (
						<div
							className="acf-content-preview"
							dangerouslySetInnerHTML={ {
								__html: getPreviewContent(),
							} }
						/>
					) : (
						<div className="acf-content-placeholder">
							{ __(
								'Add HTML content with ACF field tags in the sidebar settings.',
								'agilepress-content-block-for-acf'
							) }
						</div>
					) }
				</div>
			</div>
		</>
	);
}]]></content>
  </file>
  <file path="src/save.js">
    <description>This file contains the save function for the block which is responsible for creating the static result of rendering the block on the client to display the saved result on the front end.</description>
    <content><![CDATA[]]></content>
  </file>
  <file path="src/style.scss">
    <description>This file contains styles for the block in the front end.</description>
    <content><![CDATA[/**
 * The following styles get applied both on the front of your site
 * and in the editor.
 */

.wp-block-agilepress-content-block-for-acf {
	.acf-content-wrapper {
		width: 100%;
	}
}
]]></content>
  </file>
  <file path="src/editor.scss">
    <description>This file contains styles for the block in the editor.</description>
    <content><![CDATA[/**
 * Styles applied inside the editor for AgilePress Content Block for ACF.
 * Focus: Unify structure and preserve original class names.
 */

/* General visual style of the block in the editor */
.wp-block-agilepress-content-block-for-acf {
	border: 1px dashed #ddd; /* Added for visual consistency */
	border-radius: 4px; /* Added for visual consistency */

	/* ACF Block content area (Adjusted for consistency) */
	.agilepress-content-block-for-acf-editor {
		min-height: 100px;
		padding: 1em; /* Unified: 1em (previously 20px) */
		background: #fafafa; /* Unified: #fafafa (previously #f8f9fa) */
		border: 1px solid #e0e0e0;
		border-radius: 4px;
	}

	/* Style for placeholders (Maintained and Adjusted) */
	.acf-content-placeholder {
		color: #757575;
		font-style: italic;
		text-align: left;
		padding: 0.5em 0; /* Unified */
		font-size: 0.9em; /* Unified */
	}

	/* --------------------------------------------------------------------------
	   TAG PREVIEW STYLES (Maintained)
	   -------------------------------------------------------------------------- */
	
	.acf-content-preview {
		.acf-tag-preview { /* Simple Tag */
			background-color: #e3f2fd;
			border: 1px solid #2196f3;
			padding: 2px 6px;
			border-radius: 3px;
			font-family: monospace;
			font-size: 0.9em;
			color: #1565c0;
		}
		
		.acf-group-preview { /* Group Tag */
			background-color: #f3e5f5;
			border: 1px solid #9c27b0;
			padding: 2px 6px;
			border-radius: 3px;
			font-family: monospace;
			font-size: 0.9em;
			color: #6a1b9a;
		}
		
		.acf-loop-preview { /* Loop/Repeater Tag */
			background-color: #fff3e0;
			border: 2px solid #ff9800;
			padding: 1em; /* Unified: 1em (previously 10px) */
			border-radius: 4px;
			margin: 1em 0;
			
			strong {
				color: #e65100;
				font-family: monospace;
				display: block;
				margin: 5px 0;
			}
		}
		
		.acf-conditional-preview { /* Conditional Tag */
			background-color: #e8f5e9;
			border: 2px solid #4caf50;
			padding: 1em;
			border-radius: 4px;
			margin: 1em 0;
			
			strong {
				color: #2e7d32;
				font-family: monospace;
				display: block;
				margin: 5px 0;
			}
		}
	}
}

/* Native component style corrections (Common) */
.components-panel__body {
	.components-notice {
		margin-top: 16px;
		
		ul {
			list-style-type: disc;
			margin-bottom: 0;
		}
		
		p {
			margin-bottom: 4px;
		}
	}
}]]></content>
  </file>
  <file path="src/view.js">
    <description>This file contains the view function for the block which is responsible for rendering interactive behaviors of the block on the front end.</description>
    <content><![CDATA[/**
 * Frontend JavaScript for AgilePress Content Block for ACF
 */

/* eslint-disable no-console */
console.log("AgilePress Content Block for ACF loaded");
/* eslint-enable no-console */
]]></content>
  </file>
  <file path="src/render.php">
    <description>This file contains the render callback function for the block, which is responsible for rendering the block content on the front end.</description>
    <content><![CDATA[<?php
/**
 * Render callback for AgilePress Content Block for ACF
 *
 * @see https://github.com/WordPress/gutenberg/blob/trunk/docs/reference-guides/block-api/block-metadata.md#render
 *
 * @param array    $attributes Block attributes.
 * @param string   $content    Block default content.
 * @param WP_Block $block      Block instance.
 * @return string Returns the filtered content with ACF fields.
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound
$agilepress_acf_block_content = isset( $attributes['content'] ) ? $attributes['content'] : '';

if ( empty( $agilepress_acf_block_content ) ) {
	return '';
}

// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound
$agilepress_acf_block_post_id = get_the_ID();

// Check if ACF is active.
if ( ! function_exists( 'get_field' ) ) {
	return sprintf(
		'<div %1$s><p>%2$s</p></div>',
		get_block_wrapper_attributes(),
		esc_html__( 'Advanced Custom Fields plugin is required.', 'agilepress-content-block-for-acf' )
	);
}

/**
 * Process ACF conditional blocks with {{#if}}...{{/if}} syntax
 * Supports:
 * - {{#if field}} - checks if field exists and is truthy
 * - {{#if field = value}} - checks if field equals value
 * - {{#if field != value}} - checks if field does not equal value
 * - {{#elseif field}} - alternative condition
 * - {{#else}} - fallback
 */
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- Template variable, not a global
$agilepress_acf_block_processed = preg_replace_callback(
	'/\{\{#if ([^}]+)\}\}(.*?)(?:\{\{#elseif ([^}]+)\}\}(.*?))*(?:\{\{#else\}\}(.*?))?\{\{\/if\}\}/s',
	function( $matches ) use ( $agilepress_acf_block_post_id ) {
		$condition = trim( $matches[1] );
		$if_content = $matches[2];
		$else_content = isset( $matches[5] ) ? $matches[5] : '';
		
		// Parse condition: field, field = value, or field != value
		$field_name = '';
		$operator = 'exists';
		$compare_value = '';
		
		if ( strpos( $condition, '!=' ) !== false ) {
			$parts = array_map( 'trim', explode( '!=', $condition, 2 ) );
			$field_name = $parts[0];
			$operator = '!=';
			$compare_value = isset( $parts[1] ) ? $parts[1] : '';
		} elseif ( strpos( $condition, '=' ) !== false ) {
			$parts = array_map( 'trim', explode( '=', $condition, 2 ) );
			$field_name = $parts[0];
			$operator = '=';
			$compare_value = isset( $parts[1] ) ? $parts[1] : '';
		} else {
			$field_name = $condition;
		}
		
		// Get field value
		$field_value = get_field( $field_name, $agilepress_acf_block_post_id );
		
		// Evaluate condition
		$condition_met = false;
		
		if ( 'exists' === $operator ) {
			$condition_met = ! empty( $field_value );
		} elseif ( '=' === $operator ) {
			$condition_met = ( (string) $field_value === (string) $compare_value );
		} elseif ( '!=' === $operator ) {
			$condition_met = ( (string) $field_value !== (string) $compare_value );
		}
		
		return $condition_met ? $if_content : $else_content;
	},
	$agilepress_acf_block_content
);

/**
 * Process ACF repeater/gallery loops with {{field_name}}...{{/field_name}} syntax
 */
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- Template variable, not a global
$agilepress_acf_block_processed = preg_replace_callback(
	'/\{\{([^}]+)\}\}(.*?)\{\{\/\1\}\}/s',
	function( $matches ) use ( $agilepress_acf_block_post_id ) {
		$field_name = trim( $matches[1] );
		$template   = $matches[2];

		$field_value = get_field( $field_name, $agilepress_acf_block_post_id );

		// Handle repeater fields.
		if ( is_array( $field_value ) && ! empty( $field_value ) ) {
			$output = '';

			foreach ( $field_value as $row ) {
				$row_output = $template;

				// Determine if it's a gallery (image array) or a normal repeater.
				$is_gallery = ( isset( $row['url'] ) && isset( $row['alt'] ) );

				$row_output = preg_replace_callback(
					'/\{([^}:]+)\}/',
					function( $sub_matches ) use ( $row, $is_gallery ) {
						$sub_field = trim( $sub_matches[1] );
						$value     = '';

						if ( $is_gallery ) {
							// Gallery logic: field names are keys of the image array.
							if ( isset( $row[ $sub_field ] ) ) {
								$value = $row[ $sub_field ];
							}
						} else {
							// Repeater logic.
							if ( isset( $row[ $sub_field ] ) ) {
								$value = $row[ $sub_field ];
							}
						}

						// Handle URL explicitly for security inside tags.
						if ( 'url' === $sub_field || 'link' === $sub_field || ( is_array( $value ) && isset( $value['url'] ) ) ) {
							$url_val = is_array( $value ) ? $value['url'] : $value;
							return esc_url( $url_val );
						}

						// Return raw value (can be HTML). It will be sanitized by wp_kses_post at the end.
						// If value is array/object and not handled above, return empty to avoid errors.
						if ( is_array( $value ) || is_object( $value ) ) {
							return '';
						}

						return $value;
					},
					$row_output
				);

				$output .= $row_output;
			}

			return $output;
		}

		return '';
	},
	$agilepress_acf_block_processed
);

/**
 * Process ACF group fields with {group_name:sub_field_name} syntax
 */
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- Template variable, not a global
$agilepress_acf_block_processed = preg_replace_callback(
	'/\{([^}:]+):([^}]+)\}/',
	function( $matches ) use ( $agilepress_acf_block_post_id ) {
		$group_name     = trim( $matches[1] );
		$sub_field_name = trim( $matches[2] );

		$group_value = get_field( $group_name, $agilepress_acf_block_post_id );

		if ( is_array( $group_value ) && isset( $group_value[ $sub_field_name ] ) ) {
			$field_value = $group_value[ $sub_field_name ];

			if ( is_array( $field_value ) && isset( $field_value['url'] ) ) {
				return esc_url( $field_value['url'] );
			}

			if ( is_string( $field_value ) || is_numeric( $field_value ) ) {
				return $field_value; // Raw return, sanitized later.
			}
		}

		// If field not found, return original string to avoid breaking CSS/JS that uses braces.
		return $matches[0];
	},
	$agilepress_acf_block_processed
);

/**
 * Replace simple ACF field tags with actual field values
 */
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- Template variable, not a global
$agilepress_acf_block_processed = preg_replace_callback(
	'/\{([^}:]+)\}/',
	function( $matches ) use ( $agilepress_acf_block_post_id ) {
		$field_name = trim( $matches[1] );

		// Get the field value.
		$field_value = get_field( $field_name, $agilepress_acf_block_post_id );

		// If get_field returns null, the field does not exist in ACF.
		// We return the original match to avoid breaking CSS/JS {curly braces}.
		if ( null === $field_value ) {
			return $matches[0];
		}

		// Handle array returns (like Image or Link objects).
		if ( is_array( $field_value ) ) {
			if ( isset( $field_value['url'] ) ) {
				return esc_url( $field_value['url'] );
			} elseif ( isset( $field_value['value'] ) ) {
				return $field_value['value'];
			}
			return '';
		}

		if ( false !== $field_value ) {
			return $field_value; // Raw return, sanitized later.
		}

		return '';
	},
	$agilepress_acf_block_processed
);

// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound
$agilepress_acf_block_wrapper_attributes = get_block_wrapper_attributes();
?>
<div <?php echo $agilepress_acf_block_wrapper_attributes; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
	<div class="acf-content-wrapper">
		<?php
		// Final Output Sanitization.
		// This allows HTML tags (bold, links, etc) but strips malicious scripts.
		// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- Template variable, not a global
		echo wp_kses_post( $agilepress_acf_block_processed );
		?>
	</div>
</div>]]></content>
  </file>
  <file path="agilepress-content-block-for-acf.php">
    <description>This file contains the block registration code in the form of a single block plugin.</description>
    <content><![CDATA[<?php
/**
 * Plugin Name:       AgilePress Content Block for ACF
 * Description:       Display Advanced Custom Fields content using custom tags within HTML, including support for groups, repeaters, and galleries.
 * Version:           1.1.0
 * Requires at least: 6.1
 * Requires PHP:      7.0
 * Author:            AgilePress
 * License:           GPLv2 or later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       agilepress-content-block-for-acf
 *
 * @package AgilePress
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Registers the block using the metadata loaded from the `block.json` file.
 * Behind the scenes, it registers also all assets so they can be enqueued
 * through the block editor in the corresponding context.
 *
 * @see https://developer.wordpress.org/reference/functions/register_block_type/
 */
function agilepress_content_block_for_acf_block_init() {
	register_block_type( __DIR__ . '/build/' );
}
add_action( 'init', 'agilepress_content_block_for_acf_block_init' );]]></content>
  </file>
</artefact>