Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions .github/instructions/php.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ All PHP lives in `inc/` and is loaded via `functions.php`. Each file handles **o
| `analytics-ga4.php` | GA4 registration + output |
| `analytics-leadfeeder.php` | Leadfeeder registration + output |
| `analytics-cookiebot.php` | CookieBot registration + output |
| `analytics-linkedin.php` | LinkedIn Insight Tag registration + output |
| `theme-settings.php` | Settings page + sections |
| `theme-settings-footer.php` | Footer content fields |

Expand Down
8 changes: 8 additions & 0 deletions .vscode/mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"servers": {
"shaders": {
"type": "http",
"url": "https://shaders.com/mcp"
}
}
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Each file handles **one concern**:
| `analytics-ga4.php` | GA4 registration + output |
| `analytics-leadfeeder.php` | Leadfeeder registration + output |
| `analytics-cookiebot.php` | CookieBot registration + output |
| `analytics-linkedin.php` | LinkedIn Insight Tag registration + output |

All functions are **fully typed** (parameter + return types). PHPStan runs at
level 5 in CI to prevent type regressions.
Expand Down
6 changes: 1 addition & 5 deletions blocks/card-contact-form/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,5 @@
"editorScript": "file:../../build/index.js",
"editorStyle": "file:../../build/index.css",
"viewStyle": ["file:card-contact-form.css"],
"example": {
"attributes": {
"formUrl": "https://forms.zohopublic.eu/Observata/form/Contactusforwebsite/formperma/WZY4ixkroGhjj-TNFSI45yFNmi5QFNBVWTdBcPxWMTE"
}
}
"example": {}
}
2 changes: 1 addition & 1 deletion blocks/card-contact-form/card-contact-form.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
}

.iframe-contact-form {
height: 699px;
height: 762px;

@media (width >=64rem) {
height: 600px;
Expand Down
1 change: 1 addition & 0 deletions functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
require get_template_directory() . '/inc/analytics-ga4.php';
require get_template_directory() . '/inc/analytics-leadfeeder.php';
require get_template_directory() . '/inc/analytics-cookiebot.php';
require get_template_directory() . '/inc/analytics-linkedin.php';
require get_template_directory() . '/inc/theme-settings.php';
require get_template_directory() . '/inc/theme-settings-footer.php';
require get_template_directory() . '/inc/image-optimization.php';
Expand Down
109 changes: 109 additions & 0 deletions inc/analytics-linkedin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php
/**
* LinkedIn Insight Tag integration.
*
* Registers the LinkedIn Partner ID setting, renders the admin field,
* and outputs the consent-gated Insight Tag snippet in <head>.
*/

add_action( 'admin_init', 'observata_linkedin_register_setting' );
add_action( 'wp_head', 'observata_output_linkedin_script', 100 );

// ─────────────────────────────────────────────────────────────────

// Register the LinkedIn Partner ID setting and admin field.
function observata_linkedin_register_setting(): void {
register_setting(
'observata_settings',
'observata_linkedin_id',
array(
'type' => 'string',
'sanitize_callback' => 'observata_sanitize_linkedin_id',
'default' => '',
)
);

add_settings_field(
'observata_linkedin_id',
__( 'LinkedIn Partner ID', 'observata' ),
'observata_linkedin_id_field',
'observata-settings',
'observata_analytics_section'
);
}

// Sanitize and validate the LinkedIn Partner ID format.
function observata_sanitize_linkedin_id( string $value ): string {
$value = sanitize_text_field( $value );

if ( $value && ! preg_match( '/^[0-9]+$/', $value ) ) {
add_settings_error(
'observata_linkedin_id',
'invalid-linkedin-id',
__( 'Invalid LinkedIn Partner ID. Expected digits only.', 'observata' )
);

return '';
}

return $value;
}

// Render the LinkedIn Partner ID input field.
function observata_linkedin_id_field(): void {
$value = get_option( 'observata_linkedin_id', '' );

printf(
'<input type="text" name="observata_linkedin_id" value="%s" class="regular-text" placeholder="10849473">',
esc_attr( $value )
);

printf(
'<p class="description">%s</p>',
esc_html__( 'Enter your LinkedIn Partner ID (Insight Tag). Leave blank to disable.', 'observata' )
);
}

// Output the consent-gated LinkedIn Insight Tag snippet in <head>.
function observata_output_linkedin_script(): void {
if ( ! observata_is_production() ) {
return;
}

$linkedin_id = get_option( 'observata_linkedin_id', '' );

if ( empty( $linkedin_id ) ) {
return;
}

if ( is_admin() || wp_doing_ajax() ) {
return;
}

// phpcs:disable WordPress.WP.EnqueuedResources.NonEnqueuedScript -- consent-gated via CookieBot
printf(
'<!-- LinkedIn Insight Tag (consent-gated via CookieBot) -->
<script type="text/plain" data-cookieconsent="marketing">
_linkedin_partner_id = "%1$s";
window._linkedin_data_partner_ids = window._linkedin_data_partner_ids || [];
window._linkedin_data_partner_ids.push(_linkedin_partner_id);
</script>
<script type="text/plain" data-cookieconsent="marketing">
(function(l) {
if (!l){window.lintrk = function(a,b){window.lintrk.q.push([a,b])};
window.lintrk.q=[]}
var s = document.getElementsByTagName("script")[0];
var b = document.createElement("script");
b.type = "text/javascript";b.async = true;
b.src = "https://snap.licdn.com/li.lms-analytics/insight.min.js";
s.parentNode.insertBefore(b, s);
})(window.lintrk);
</script>
<noscript>
<img height="1" width="1" style="display:none;" alt="" src="https://px.ads.linkedin.com/collect/?pid=%2$s&amp;fmt=gif" />
</noscript>',
esc_js( $linkedin_id ),
esc_attr( $linkedin_id )
);
// phpcs:enable
}
1 change: 1 addition & 0 deletions phpcs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
<exclude-pattern>node_modules/*</exclude-pattern>
<exclude-pattern>build/*</exclude-pattern>
<exclude-pattern>dist/*</exclude-pattern>
<exclude-pattern>cache/*</exclude-pattern>
<exclude-pattern>*.min.js</exclude-pattern>
<exclude-pattern>*.min.css</exclude-pattern>
<exclude-pattern>intelephense-wp.php</exclude-pattern>
Expand Down
49 changes: 38 additions & 11 deletions src/card-contact-form/edit.jsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,47 @@
import { useBlockProps } from '@wordpress/block-editor';
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
import { PanelBody, TextControl } from '@wordpress/components';

import BlockLabel from '../components/block-label';
import { __ } from '@wordpress/i18n';

export default function CardContactFormEdit({ attributes: _attributes }) {
export default function CardContactFormEdit({ attributes, setAttributes }) {
const { formUrl } = attributes;
const blockProps = useBlockProps({ className: 'contact-form-editor' });

const formUrlControl = (
<TextControl
label={__('Form URL', 'observata')}
type="url"
value={formUrl}
onChange={(value) => setAttributes({ formUrl: value })}
help={__('Zoho form embed URL. Leave blank to hide the form.', 'observata')}
/>
);

return (
<div {...blockProps}>
<BlockLabel name="Card Contact Form" />
<div className="contact-form">
<div className="contact-form__placeholder">
<span className="contact-form__placeholder-icon">📝</span>
<p className="contact-form__placeholder-text">
Zoho Contact Form — preview not available in editor
</p>
<>
<InspectorControls>
<PanelBody title={__('Contact Form Settings', 'observata')}>{formUrlControl}</PanelBody>
</InspectorControls>

<div {...blockProps}>
<BlockLabel name="Card Contact Form">{formUrlControl}</BlockLabel>
<div className="contact-form">
<div className="contact-form__placeholder">
<span className="contact-form__placeholder-icon">📝</span>
<p className="contact-form__placeholder-text">
Zoho Contact Form — preview not available in editor
</p>
{formUrl && (
<p className="contact-form__placeholder-url">
<a href={formUrl} target="_blank" rel="noreferrer">
{formUrl}
</a>
</p>
)}
</div>
</div>
</div>
</div>
</>
);
}
4 changes: 0 additions & 4 deletions src/card-geo-list/edit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import BlockLabel from '../components/block-label';
import ControlsLayout from '../components/controls-layout';
import GeoIcon from '../components/geo-icon';
import GEO_ICON_OPTIONS from '../components/geo-icon-options';
import GraphicSelect from '../components/graphic-select';

Expand Down Expand Up @@ -37,9 +36,6 @@ export default function CardGeoListEdit({ attributes, setAttributes }) {
<article {...blockProps}>
<BlockLabel name="Card Geo List" />

<div className="icon-geo">
<GeoIcon number={iconGeo} />
</div>
<div className="intro-card-body">
<RichText
tagName="h3"
Expand Down
4 changes: 0 additions & 4 deletions src/card-geo-shader/edit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { RichText, useBlockProps } from '@wordpress/block-editor';

import { __ } from '@wordpress/i18n';
import BlockLabel from '../components/block-label';
import GeoIcon from '../components/geo-icon';
import GEO_ICON_OPTIONS from '../components/geo-icon-options';
import GraphicSelect from '../components/graphic-select';

Expand All @@ -15,9 +14,6 @@ export default function CardGeoShaderEdit({ attributes, setAttributes }) {
return (
<article {...blockProps}>
<BlockLabel name="Card Geo Shader" />
<div className="icon-geo">
<GeoIcon number={iconGeo} />
</div>
<div className="card-body">
<RichText
tagName="h3"
Expand Down
4 changes: 0 additions & 4 deletions src/card-geo-tech/edit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { RichText, useBlockProps } from '@wordpress/block-editor';
import { SelectControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import BlockLabel from '../components/block-label';
import GeoIcon from '../components/geo-icon';
import GEO_ICON_OPTIONS from '../components/geo-icon-options';
import GraphicSelect from '../components/graphic-select';

Expand Down Expand Up @@ -34,9 +33,6 @@ export default function CardGeoTechEdit({ attributes, setAttributes }) {
<article {...blockProps}>
<BlockLabel name="Card Geo Tech" />

<div className="icon-geo">
<GeoIcon number={iconGeo} />
</div>
<div className="intro-card-body">
<RichText
tagName="h3"
Expand Down
4 changes: 0 additions & 4 deletions src/card-geo/edit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { RichText, useBlockProps } from '@wordpress/block-editor';

import { __ } from '@wordpress/i18n';
import BlockLabel from '../components/block-label';
import GeoIcon from '../components/geo-icon';
import GEO_ICON_OPTIONS from '../components/geo-icon-options';
import GraphicSelect from '../components/graphic-select';

Expand All @@ -14,9 +13,6 @@ export default function CardGeoEdit({ attributes, setAttributes }) {
<article {...blockProps}>
<BlockLabel name="Card Geo" />

<div className="icon-geo">
<GeoIcon number={iconGeo} />
</div>
<div className="card-body">
<RichText
className="intro-card-title"
Expand Down
4 changes: 0 additions & 4 deletions src/card-simple/edit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { SelectControl } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import BlockLabel from '../components/block-label';
import GeoIcon from '../components/geo-icon';
import GEO_ICON_OPTIONS from '../components/geo-icon-options';
import GraphicSelect from '../components/graphic-select';

Expand All @@ -33,9 +32,6 @@ export default function Edit({ attributes, setAttributes }) {
<article {...blockProps}>
<BlockLabel name="Card Simple" />

<div className="icon-geo">
<GeoIcon number={iconGeo} />
</div>
<div className="intro-card-body">
<RichText
tagName="h3"
Expand Down
20 changes: 6 additions & 14 deletions src/card-table-features/edit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import BlockLabel from '../components/block-label';
import ControlsLayout from '../components/controls-layout';
import GeoIcon from '../components/geo-icon';
import GEO_ICON_OPTIONS from '../components/geo-icon-options';
import GraphicSelect from '../components/graphic-select';

Expand Down Expand Up @@ -75,19 +74,12 @@ export default function CardTableFeaturesEdit({ attributes, setAttributes }) {
value={attributes['plan' + i + 'Description']}
onChange={(value) => setAttributes({ ['plan' + i + 'Description']: value })}
/>
<ControlsLayout layout="horizontal" gap="1rem">
<div className="icon-geo">
<GeoIcon
number={attributes['plan' + i + 'Icon'] || String(i).padStart(2, '0')}
/>
</div>
<GraphicSelect
label={__('Plan ' + i + ' Icon', 'observata')}
value={attributes['plan' + i + 'Icon']}
options={GEO_ICON_OPTIONS}
onChange={(val) => setAttributes({ ['plan' + i + 'Icon']: val })}
/>
</ControlsLayout>
<GraphicSelect
label={__('Plan ' + i + ' Icon', 'observata')}
value={attributes['plan' + i + 'Icon']}
options={GEO_ICON_OPTIONS}
onChange={(val) => setAttributes({ ['plan' + i + 'Icon']: val })}
/>
<ToggleControl
label={__('Show Read More Link', 'observata')}
checked={attributes['plan' + i + 'ShowReadMore']}
Expand Down
21 changes: 18 additions & 3 deletions src/components/graphic-select.css
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,24 @@
overflow-wrap: anywhere;
}

/* Hide icon when option is cloned into selectedcontent (button display) */
selectedcontent .graphic-select-option-icon {
display: none;
/* Selected option shown in the select button — compact icon beside the label */
.graphic-select-button {
display: flex;
flex: 1;
align-items: center;
gap: var(--spacing-sm, 8px);
min-width: 0;

.graphic-select-option-icon {
width: 32px;
height: 32px;
}

.graphic-select-option-label {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}

/* Help text */
Expand Down
Loading
Loading