-
Notifications
You must be signed in to change notification settings - Fork 60
EPS-1573: Preview page promotion notice #1330
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
46ba0f9
5549f64
31865cd
c170d5a
a2dfc7e
f91e373
9133230
7e7d675
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -143,6 +143,9 @@ function () { | |
|
||
add_action( 'astra_notice_before_markup_header-footer-elementor-rating', [ $this, 'rating_notice_css' ] ); | ||
|
||
// Add Elementor preview notice | ||
add_action( 'wp_footer', [ $this, 'elementor_preview_notice' ] ); | ||
|
||
require_once HFE_DIR . 'inc/class-hfe-analytics.php'; | ||
|
||
} | ||
|
@@ -268,6 +271,301 @@ public function rating_notice_css() { | |
wp_enqueue_style( 'hfe-admin-style', HFE_URL . 'assets/css/admin-header-footer-elementor.css', [], HFE_VER ); | ||
} | ||
|
||
/** | ||
* Display Elementor preview notice in footer when in preview mode. | ||
* | ||
* @since 2.4.9 | ||
* @return void | ||
*/ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What: Potential for Denial of Service if many notices are created/removed quickly due to localStorage handling and DOM manipulation. Why: The JavaScript to manage the notice could lead to performance issues if users quickly interact with the notification multiple times, potentially resulting in heavy DOM manipulation or localStorage writes that could degrade user experience, especially on lower-power devices. How: Consider debouncing the |
||
public function elementor_preview_notice() { | ||
// Show notice only for page post type in preview mode | ||
if ( ! $this->should_show_preview_notice() ) { | ||
return; | ||
} | ||
|
||
?> | ||
<style> | ||
@keyframes slideInFromBottom { | ||
0% { | ||
transform: translateY(100%); | ||
opacity: 0; | ||
} | ||
100% { | ||
transform: translateY(0); | ||
opacity: 1; | ||
} | ||
} | ||
|
||
@keyframes slideOutToBottom { | ||
0% { | ||
transform: translateY(0); | ||
opacity: 1; | ||
} | ||
100% { | ||
transform: translateY(100%); | ||
opacity: 0; | ||
} | ||
} | ||
|
||
.hfe-promo-notice { | ||
position: fixed; | ||
bottom: 0; | ||
left: 0; | ||
right: 0; | ||
background: #000; | ||
color: #ffffff; | ||
padding: 10px 20px; | ||
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.15); | ||
z-index: 999999; | ||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | ||
font-size: 14px; | ||
font-weight: 500; | ||
animation: slideInFromBottom 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94); | ||
backdrop-filter: blur(10px); | ||
border-top: 1px solid rgba(255, 255, 255, 0.2); | ||
display: none; | ||
} | ||
|
||
.hfe-promo-notice.show { | ||
display: block; | ||
} | ||
|
||
.hfe-promo-notice.hide { | ||
animation: slideOutToBottom 0.4s cubic-bezier(0.55, 0.06, 0.68, 0.19) forwards; | ||
} | ||
|
||
.hfe-promo-notice-container { | ||
display: flex; | ||
align-items: center; | ||
justify-content: space-around; | ||
} | ||
|
||
.hfe-promo-notice-content { | ||
display: flex; | ||
align-items: center; | ||
/* flex: 1; */ | ||
} | ||
|
||
.hfe-promo-notice-icon { | ||
display: inline-flex; | ||
width: 24px; | ||
height: 24px; | ||
margin-right: 12px; | ||
background: rgba(255, 255, 255, 0.2); | ||
border-radius: 50%; | ||
align-items: center; | ||
justify-content: center; | ||
font-size: 14px; | ||
flex-shrink: 0; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What: CSS can potentially affect performance with many animate styles in rapid succession. Why: Heavy animations can impact performance, especially on older devices or with multiple elements being animated simultaneously. This can lead to jank and a poor user experience. How: Consider simplifying animations or limiting them based on user interaction. Alternatively, conditionally load animation styles only when necessary to reduce initial load time. |
||
|
||
.hfe-promo-notice-text { | ||
flex: 1; | ||
line-height: 1.4; | ||
} | ||
|
||
.hfe-promo-notice-title { | ||
font-weight: 600; | ||
margin-bottom: 2px; | ||
font-size: 15px; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What: Inconsistent visibility checks for notice removal leading to user confusion. Why: If the notice visibility is not properly managed, users may experience inconsistent behavior when interacting with the notification banner, impeding its user-friendliness. How: Refactor the script to ensure clear visibility states upon interaction. Additionally, add comments to explain each section of the script for future developers. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What: The use of inline styles may hinder maintainability and consistency in styling. Why: Inline styles can lead to difficulties in managing consistent theming and maintenance as the codebase grows. How: Extract styles into dedicated CSS files or utilize existing stylesheets, ensuring they're scoped correctly, to facilitate easier updates and increases in maintainability. |
||
|
||
.hfe-promo-notice-description { | ||
opacity: 0.9; | ||
font-size: 20px; | ||
} | ||
|
||
.hfe-promo-notice-cta { | ||
margin-left: 16px; | ||
padding: 8px 16px; | ||
background: rgb(96, 5, 255); | ||
border: 1px solid rgb(96, 5, 255); | ||
color: #ffffff; | ||
text-decoration: none; | ||
border-radius: 25px; | ||
font-size: 13px; | ||
font-weight: 600; | ||
transition: all 0.2s ease; | ||
white-space: nowrap; | ||
} | ||
|
||
.hfe-promo-notice-cta:hover { | ||
background:rgb(96, 5, 255); | ||
color: #ffffff; | ||
text-decoration: none; | ||
} | ||
|
||
.hfe-promo-notice-close { | ||
margin-left: 16px; | ||
position: absolute; | ||
right:5px; | ||
background: none; | ||
border: none; | ||
color: rgba(255, 255, 255, 0.8); | ||
font-size: 20px; | ||
cursor: pointer; | ||
padding: 4px; | ||
width: 28px; | ||
height: 28px; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
border-radius: 50%; | ||
transition: all 0.2s ease; | ||
flex-shrink: 0; | ||
} | ||
|
||
.hfe-promo-notice-close:hover { | ||
background: rgba(255, 255, 255, 0.1); | ||
color: #ffffff; | ||
} | ||
|
||
@media (max-width: 768px) { | ||
.hfe-promo-notice { | ||
padding: 12px 16px; | ||
font-size: 13px; | ||
} | ||
|
||
.hfe-promo-notice-container { | ||
flex-direction: column; | ||
align-items: stretch; | ||
gap: 12px; | ||
} | ||
|
||
.hfe-promo-notice-content { | ||
flex-direction: column; | ||
align-items: flex-start; | ||
text-align: left; | ||
} | ||
|
||
.hfe-promo-notice-icon { | ||
margin-right: 8px; | ||
width: 20px; | ||
height: 20px; | ||
font-size: 12px; | ||
} | ||
|
||
.hfe-promo-notice-cta { | ||
margin-left: 0; | ||
margin-top: 8px; | ||
align-self: flex-start; | ||
} | ||
|
||
.hfe-promo-notice-close { | ||
position: absolute; | ||
top: 8px; | ||
right: 8px; | ||
margin-left: 0; | ||
} | ||
} | ||
|
||
@media (max-width: 480px) { | ||
.hfe-promo-notice-container { | ||
padding-right: 40px; | ||
} | ||
|
||
.hfe-promo-notice-title { | ||
font-size: 14px; | ||
} | ||
|
||
.hfe-promo-notice-description { | ||
font-size: 12px; | ||
} | ||
} | ||
</style> | ||
|
||
<div id="hfe-promo-notice" class="hfe-promo-notice"> | ||
<div class="hfe-promo-notice-container"> | ||
<div class="hfe-promo-notice-content"> | ||
<!-- <div class="hfe-promo-notice-icon">🚀</div> --> | ||
<div class="hfe-promo-notice-text"> | ||
<!-- <div class="hfe-promo-notice-title">Unlock More Elementor Widgets!</div> --> | ||
<div class="hfe-promo-notice-description">Psst… want to save hours? Get 300+ professionally built templates.</div> | ||
</div> | ||
<a href="https://ultimateelementor.com/pricing/?utm_source=preview&utm_medium=notice&utm_campaign=uae-lite" | ||
target="_blank" | ||
class="hfe-promo-notice-cta"> | ||
Unlock Now | ||
</a> | ||
</div> | ||
<button class="hfe-promo-notice-close" onclick="hfePromoNotice.dismiss()">×</button> | ||
</div> | ||
</div> | ||
|
||
<script>window.hfePromoNotice={storageKey:'hfe_promo_notice_dismissed',oneMonthMs:2592000000,init:function(){this.shouldShow()&&this.show()},shouldShow:function(){try{const a=localStorage.getItem(this.storageKey);if(!a)return!0;const b=parseInt(a),c=Date.now();return c-b>=this.oneMonthMs}catch(a){return!0}},show:function(){const a=document.getElementById('hfe-promo-notice');a&&a.classList.add('show')},dismiss:function(){const a=document.getElementById('hfe-promo-notice');a&&(a.classList.add('hide'),setTimeout(()=>{a.remove()},400));try{localStorage.setItem(this.storageKey,Date.now().toString())}catch(a){console.log('Could not save dismissal state')}}};"loading"===document.readyState?document.addEventListener('DOMContentLoaded',function(){hfePromoNotice.init()}):hfePromoNotice.init();</script> | ||
<?php | ||
} | ||
|
||
/** | ||
* Check if preview notice should be shown for page post type only. | ||
* | ||
* @since 2.4.9 | ||
* @return bool | ||
*/ | ||
private function should_show_preview_notice() { | ||
// Don't show if UAE Pro is already installed/activated | ||
if ( is_plugin_active( 'ultimate-elementor/ultimate-elementor.php' ) || | ||
file_exists( WP_PLUGIN_DIR . '/ultimate-elementor/ultimate-elementor.php' ) ) { | ||
return false; | ||
} | ||
|
||
// Basic preview check | ||
if ( ! isset( $_GET['preview'] ) || sanitize_text_field( wp_unslash( $_GET['preview'] ) ) !== 'true' ) { | ||
return false; | ||
} | ||
|
||
// Must have preview_id | ||
if ( ! isset( $_GET['preview_id'] ) ) { | ||
return false; | ||
} | ||
|
||
$preview_id = intval( sanitize_text_field( wp_unslash( $_GET['preview_id'] ) ) ); | ||
|
||
// Verify preview nonce for security (if available) | ||
if ( isset( $_GET['preview_nonce'] ) ) { | ||
$preview_nonce = sanitize_text_field( wp_unslash( $_GET['preview_nonce'] ) ); | ||
if ( ! wp_verify_nonce( $preview_nonce, 'post_preview_' . $preview_id ) ) { | ||
return false; | ||
} | ||
} | ||
|
||
// Check if it's a page post type (not header/footer templates) | ||
$post_type = get_post_type( $preview_id ); | ||
if ( $post_type !== 'page' ) { | ||
return false; | ||
} | ||
|
||
// Exclude header/footer templates from UAE | ||
$template_type = get_post_meta( $preview_id, 'ehf_template_type', true ); | ||
if ( ! empty( $template_type ) ) { | ||
return false; // This is a header/footer template, don't show notice | ||
} | ||
|
||
// Optional: Check if page uses Elementor | ||
if ( ! $this->is_elementor_page( $preview_id ) ) { | ||
return false; | ||
} | ||
|
||
// Optional: Allow filtering for custom conditions | ||
return apply_filters( 'hfe_show_preview_notice', true, $preview_id ); | ||
} | ||
|
||
/** | ||
* Check if the page is built with Elementor. | ||
* | ||
* @since 2.4.9 | ||
* @param int $post_id Post ID to check. | ||
* @return bool | ||
*/ | ||
private function is_elementor_page( $post_id ) { | ||
if ( ! class_exists( '\Elementor\Plugin' ) ) { | ||
return false; | ||
} | ||
|
||
$elementor_data = get_post_meta( $post_id, '_elementor_data', true ); | ||
return ! empty( $elementor_data ); | ||
} | ||
|
||
/** | ||
* Prints the admin notics when Elementor is not installed or activated or version outdated. | ||
* | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What: SQL Injection Risk in the preview nonce verification step.
Why: The method currently checks for a nonce on the preview request which adds a layer of security. However, if this nonce isn't properly validated or if the structure of the nonce is predictable, it could lead to vulnerabilities. Always ensure that nonce validation follows WordPress best practices to mitigate risks effectively.
How: Consider further securing nonce verification with better validation measures. Ensure the 'preview_nonce' is unique per request and hidden, and implement logging for failed nonce checks to help identify potential abuse.