Skip to content
Open
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
4 changes: 4 additions & 0 deletions includes/Core/PostType.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ public function get_localize_scripts() {
'admin' => NOTIFICATIONX_ADMIN_URL,
'public' => NOTIFICATIONX_PUBLIC_URL,
'common' => NOTIFICATIONX_COMMON_URL,
// Used by the PressBar "Custom" design preview to render the actual
// Elementor-built bar in an iframe (see PressBar::render_elementor_bar_preview()).
'home' => home_url( '/' ),
'bar_preview_nonce' => wp_create_nonce( 'nx_bar_preview' ),
];

$tabs = apply_filters( 'nx_builder_configs', $tabs );
Expand Down
131 changes: 131 additions & 0 deletions includes/Extensions/PressBar/PressBar.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ public function __construct() {
parent::__construct();
add_action('init', [$this, 'register_post_type']);
add_action('init', [$this, 'load_plugin_dependencies'], -1);
// Renders the Elementor-built bar standalone so the admin Design preview
// can show the real design instead of a static theme thumbnail.
add_action('template_redirect', [$this, 'render_elementor_bar_preview']);
add_filter( 'get_edit_post_link', function($link, $id){
$post = get_post( $id );
if ( $post && 'nx_bar' === $post->post_type && class_exists('\Elementor\Plugin') ) {
Expand Down Expand Up @@ -2094,6 +2097,134 @@ public function add_scripts($settings, $params){
return $settings;
}

/**
* Renders the Elementor-built bar in a clean, standalone page so the admin
* Design preview can show the ACTUAL Elementor design in an iframe instead of
* a static theme thumbnail.
*
* Triggered on the frontend via `?nx_bar_preview={elementor_id}&_wpnonce=...`.
* It renders the same content as the live bar (get_builder_content_for_display)
* but prints ONLY Elementor's own stylesheets and no scripts / no
* wp_head()/wp_footer() — so the preview shows the bar and nothing else (no
* admin toolbar, no chat widgets, no other notifications).
*
* @hooked template_redirect
* @return void
*/
public function render_elementor_bar_preview() {
if ( ! isset( $_GET['nx_bar_preview'] ) ) {
return;
}

$elementor_id = absint( $_GET['nx_bar_preview'] );
$nonce = isset( $_GET['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ) : '';

// Only logged-in users who can manage NotificationX may render the preview.
if ( ! $elementor_id || ! wp_verify_nonce( $nonce, 'nx_bar_preview' ) || ! current_user_can( 'edit_notificationx' ) ) {
status_header( 403 );
exit;
}

if ( ! class_exists( '\Elementor\Plugin' ) ) {
status_header( 404 );
exit;
}

$post = get_post( $elementor_id );
if ( ! $post || 'nx_bar' !== $post->post_type || 'publish' !== get_post_status( $elementor_id ) ) {
status_header( 404 );
exit;
}

$frontend = \Elementor\Plugin::$instance->frontend;

// Populate the style queue like the live frontend does, WITHOUT firing every
// plugin's wp_enqueue_scripts (which would pull in chat widgets, other
// popups, etc.). We render only Elementor's own assets.
$frontend->register_styles();
$frontend->enqueue_styles();

// Same render used on the live site by print_bar_notice(); `true` inlines
// the document's generated CSS so the design styling always travels with it.
// This also enqueues the per-widget (image/heading/button) stylesheets.
$content = $frontend->get_builder_content_for_display( $elementor_id, true );

nocache_headers();

// Print ONLY Elementor-related stylesheets (base framework, global kit,
// per-widget CSS, Google Fonts). No third-party CSS, no scripts at all,
// and no wp_head()/wp_footer().
$print_elementor_styles = function () {
$styles = wp_styles();
if ( ! $styles ) {
return;
}
$styles->all_deps( $styles->queue );
foreach ( $styles->to_do as $handle ) {
$reg = isset( $styles->registered[ $handle ] ) ? $styles->registered[ $handle ] : null;
$src = ( $reg && $reg->src ) ? strtolower( (string) $reg->src ) : '';
$h = strtolower( $handle );
$allow = ( strpos( $h, 'elementor' ) === 0 )
|| ( strpos( $h, 'e-' ) === 0 )
|| ( strpos( $h, 'eael' ) === 0 )
|| ( strpos( $h, 'font-awesome' ) === 0 || strpos( $h, 'fontawesome' ) === 0 )
|| ( strpos( $h, 'google-font' ) === 0 )
|| ( $src && strpos( $src, '/elementor/' ) !== false )
|| ( $src && strpos( $src, 'uploads/elementor' ) !== false )
|| ( $src && strpos( $src, 'essential-addons' ) !== false )
|| ( $src && ( strpos( $src, 'fonts.googleapis' ) !== false || strpos( $src, 'fonts.gstatic' ) !== false ) );
if ( $allow ) {
$styles->do_item( $handle );
}
}
};
?>
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo( 'charset' ); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?php $print_elementor_styles(); ?>
<style>
html, body { margin: 0 !important; padding: 0; background: transparent; }
body { overflow-x: hidden; }
</style>
</head>
<body class="nx-bar-elementor-preview elementor-page elementor-page-<?php echo esc_attr( $elementor_id ); ?>">
<?php echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<script>
/* Fit the iframe height to the bar. Runs inside the (same-origin) iframe,
re-measuring after images load and whenever the parent resizes the
frame for the desktop/tablet/phone tabs. */
(function () {
function nxFit() {
var fe = window.frameElement;
if ( ! fe || ! document.body ) { return; }
// Collapse first so the document reports its TRUE content height —
// otherwise scrollHeight stays inflated from the previous (taller)
// device view and leaves blank space when switching back.
fe.style.height = '0px';
var h = Math.max( document.body.scrollHeight, document.body.offsetHeight );
fe.style.height = h + 'px';
}
window.addEventListener( 'load', nxFit );
window.addEventListener( 'resize', nxFit );
if ( window.ResizeObserver && document.body ) {
try { new ResizeObserver( nxFit ).observe( document.body ); } catch ( e ) {}
}
var imgs = document.images || [];
for ( var i = 0; i < imgs.length; i++ ) { imgs[ i ].addEventListener( 'load', nxFit ); }
nxFit();
setTimeout( nxFit, 200 );
setTimeout( nxFit, 800 );
})();
</script>
</body>
</html>
<?php
exit;
}

public function hide_image_field($fields) {
$fields['image-section'] = Rules::is('source', $this->id, true, $fields['image-section']);
return $fields;
Expand Down
28 changes: 22 additions & 6 deletions nxdev/notificationx/fields/NxBarPresets.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ const NxBarPresets = () => {

const basePath = `${assets.admin}/images/extensions/themes`;

// Elementor previews render the live design in an iframe (see
// PressBar::render_elementor_bar_preview()); everything else stays an <img>.
const isElementorPreview = values?.is_elementor && values?.elementor_id;

const previewUrl = useMemo(() => {
if (!values) return '';

// Elementor Path
if (values.is_elementor && values.elementor_id && values.elementor_bar_theme) {
return `${basePath}/bar-elementor/${values.elementor_bar_theme}.jpg`;
// Elementor: live preview of the actual Elementor-built bar.
if (values.is_elementor && values.elementor_id) {
return `${assets.home}?nx_bar_preview=${values.elementor_id}&_wpnonce=${assets.bar_preview_nonce || ''}`;
}

// Gutenberg Path
Expand All @@ -32,7 +36,7 @@ const NxBarPresets = () => {

// Fallback
return values.preview_url || '';
}, [values, basePath]);
}, [values, basePath, assets]);

const showEmptyState =
values?.is_gutenberg &&
Expand All @@ -48,8 +52,20 @@ const NxBarPresets = () => {
</div>
) : (
<div className="nxbar-selected-presets">
<div className="nxbar-selected-presets-gutenberg">
<img src={previewUrl} alt="" />
<div
className="nxbar-selected-presets-gutenberg"
style={isElementorPreview ? { display: 'block', width: '100%', padding: '12px' } : undefined}
>
{isElementorPreview ? (
<iframe
className="nx-bar-elementor-preview-frame"
src={previewUrl}
scrolling="no"
style={{ width: '100%', border: 0, display: 'block', minHeight: '60px' }}
/>
) : (
<img src={previewUrl} alt="" />
)}
</div>
</div>
)}
Expand Down
31 changes: 25 additions & 6 deletions nxdev/notificationx/fields/helpers/PressbarAdminPreview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,11 @@ const PressbarAdminPreview = ({ position, nxBar, dispatch }) => {
const previewUrl = useMemo(() => {
if (!settings) return '';

// Elementor Path
if (settings.is_elementor && settings.elementor_id && settings.elementor_bar_theme) {
return `${basePath}/bar-elementor/${settings.elementor_bar_theme}.jpg`;
// Elementor: render a LIVE preview of the actual Elementor-built bar
// (see PressBar::render_elementor_bar_preview()) instead of a static
// theme thumbnail, so the preview always matches the real design.
if (settings.is_elementor && settings.elementor_id) {
return `${assets.home}?nx_bar_preview=${settings.elementor_id}&_wpnonce=${assets.bar_preview_nonce || ''}`;
}

// Gutenberg Path
Expand All @@ -248,8 +250,10 @@ const PressbarAdminPreview = ({ position, nxBar, dispatch }) => {

// Fallback
return settings.preview_url || '';
}, [settings, basePath]);
}, [settings, basePath, assets]);
const isBuildWithBuilder = (settings?.is_elementor || settings?.is_gutenberg) && (settings?.elementor_id || settings?.gutenberg_id );
// Elementor previews render in an iframe (live design); everything else is an <img>.
const isElementorPreview = settings?.is_elementor && settings?.elementor_id;

return (
<Fragment>
Expand All @@ -275,8 +279,23 @@ const PressbarAdminPreview = ({ position, nxBar, dispatch }) => {
</button>
</div>
</div>
{ isBuildWithBuilder ?
<img src={previewUrl} alt="" /> :
{ isBuildWithBuilder ?
( isElementorPreview ?
<iframe
className="nx-bar-elementor-preview-frame"
src={previewUrl}
scrolling="no"
style={{
width: previewType === 'phone' ? '375px' : previewType === 'tablet' ? '768px' : '100%',
maxWidth: '100%',
margin: '0 auto',
border: 0,
display: 'block',
minHeight: '60px',
}}
/> :
<img src={previewUrl} alt="" />
) :
<div
id={`nx-bar-${settings.nx_id}`}
className={classNames(
Expand Down