feat: Add banner management functionality
- Implemented a new Banner model to represent banner data. - Created a BannerRepository for database interactions related to banners. - Developed a BannerService to handle business logic for banners. - Added admin views for listing and adding banners. - Integrated banner hooks for frontend rendering and click tracking. - Created frontend styles and scripts for banner display and interaction. - Updated database migrations to include a new banners table. - Enhanced AdminController to manage banner actions and pages.
This commit is contained in:
112
public/css/banner-frontend.css
Normal file
112
public/css/banner-frontend.css
Normal file
@@ -0,0 +1,112 @@
|
||||
.sodino-banner {
|
||||
direction: rtl;
|
||||
position: relative;
|
||||
z-index: 9999;
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
}
|
||||
|
||||
.sodino-banner-wrap {
|
||||
margin: 0 auto;
|
||||
max-width: 1200px;
|
||||
padding: 18px 22px;
|
||||
background: #ffffff;
|
||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 18px 35px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
|
||||
.sodino-banner-top {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: calc(100% - 32px);
|
||||
max-width: 1100px;
|
||||
}
|
||||
|
||||
.sodino-banner-bottom {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: calc(100% - 32px);
|
||||
max-width: 1100px;
|
||||
}
|
||||
|
||||
.sodino-banner-floating_bar {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: calc(100% - 32px);
|
||||
max-width: 1100px;
|
||||
}
|
||||
|
||||
.sodino-banner-position-top.sodino-banner-floating_bar,
|
||||
.sodino-banner-position-middle.sodino-banner-floating_bar {
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
.sodino-banner-position-bottom.sodino-banner-floating_bar,
|
||||
.sodino-banner-position-cart.sodino-banner-floating_bar,
|
||||
.sodino-banner-position-product_page.sodino-banner-floating_bar {
|
||||
bottom: 20px;
|
||||
}
|
||||
|
||||
.sodino-banner-popup {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
max-width: 95%;
|
||||
width: 680px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 30px 60px rgba(15, 23, 42, 0.12);
|
||||
}
|
||||
|
||||
.sodino-banner-close {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
right: 16px;
|
||||
background: rgba(15, 23, 42, 0.06);
|
||||
border: none;
|
||||
color: #0f172a;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 9999px;
|
||||
font-size: 1.2rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sodino-banner-content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.sodino-banner-image {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 1rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sodino-banner-link {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.sodino-banner-top,
|
||||
.sodino-banner-bottom,
|
||||
.sodino-banner-floating_bar,
|
||||
.sodino-banner-popup {
|
||||
width: calc(100% - 20px);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.sodino-banner-popup {
|
||||
max-width: 95%;
|
||||
}
|
||||
}
|
||||
165
public/hooks/banner-hooks.php
Normal file
165
public/hooks/banner-hooks.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use Sodino\Repositories\BannerRepository;
|
||||
use Sodino\Services\BannerService;
|
||||
|
||||
global $sodino_banner_service;
|
||||
$bannerRepository = new BannerRepository();
|
||||
$sodino_banner_service = new BannerService($bannerRepository);
|
||||
|
||||
add_action('wp_head', 'sodino_render_top_banner', 1);
|
||||
add_filter('the_content', 'sodino_render_middle_banner');
|
||||
add_action('wp_footer', 'sodino_render_bottom_banner', 20);
|
||||
add_action('woocommerce_after_single_product_summary', 'sodino_render_product_banner', 5);
|
||||
add_action('woocommerce_before_cart', 'sodino_render_cart_banner');
|
||||
add_action('wp_enqueue_scripts', 'sodino_enqueue_banner_assets');
|
||||
add_action('wp_ajax_nopriv_sodino_banner_click', 'sodino_handle_banner_click');
|
||||
add_action('wp_ajax_sodino_banner_click', 'sodino_handle_banner_click');
|
||||
|
||||
function sodino_enqueue_banner_assets() {
|
||||
if (is_admin()) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_register_style('sodino-banner-frontend', plugin_dir_url(__FILE__) . '../css/banner-frontend.css', [], SODINO_VERSION);
|
||||
wp_enqueue_style('sodino-banner-frontend');
|
||||
wp_register_script('sodino-banner-frontend', plugin_dir_url(__FILE__) . '../js/banner-frontend.js', [], SODINO_VERSION, true);
|
||||
wp_localize_script('sodino-banner-frontend', 'sodinoBannerFrontend', [
|
||||
'ajaxUrl' => admin_url('admin-ajax.php'),
|
||||
'nonce' => wp_create_nonce('sodino_banner_click'),
|
||||
]);
|
||||
wp_enqueue_script('sodino-banner-frontend');
|
||||
}
|
||||
|
||||
function sodino_get_banner_html($banner) {
|
||||
global $sodino_banner_service;
|
||||
$html = '';
|
||||
$content = '';
|
||||
|
||||
switch ($banner->content_type) {
|
||||
case 'image':
|
||||
$image = esc_url($banner->content_value);
|
||||
if (!empty($banner->link_url)) {
|
||||
$content = sprintf('<a href="%s" class="sodino-banner-link" data-banner-id="%d">%s</a>', esc_url($banner->link_url), esc_attr($banner->id), '<img src="' . $image . '" alt="' . esc_attr($banner->title) . '" class="sodino-banner-image" />');
|
||||
} else {
|
||||
$content = '<img src="' . $image . '" alt="' . esc_attr($banner->title) . '" class="sodino-banner-image" />';
|
||||
}
|
||||
break;
|
||||
case 'shortcode':
|
||||
$content = do_shortcode(wp_kses_post($banner->content_value));
|
||||
break;
|
||||
case 'html':
|
||||
default:
|
||||
$content = wp_kses_post($banner->content_value);
|
||||
break;
|
||||
}
|
||||
|
||||
$linkAttributes = '';
|
||||
if (!empty($banner->link_url) && $banner->content_type !== 'image') {
|
||||
$linkAttributes = sprintf(' data-banner-id="%d" href="%s" class="sodino-banner-link"', esc_attr($banner->id), esc_url($banner->link_url));
|
||||
}
|
||||
|
||||
$closeButton = '<button type="button" class="sodino-banner-close" aria-label="'.esc_attr__('بستن بنر', 'sodino').'">×</button>';
|
||||
$wrapperClass = 'sodino-banner-wrap sodino-banner-' . esc_attr($banner->display_type) . ' sodino-banner-position-' . esc_attr($banner->position);
|
||||
|
||||
$style = $banner->display_type === 'popup' ? 'style="display:none;"' : '';
|
||||
$html .= '<div class="' . $wrapperClass . '" data-banner-id="' . esc_attr($banner->id) . '" ' . $style . '>';
|
||||
if ($banner->display_type === 'popup' || $banner->display_type === 'floating_bar') {
|
||||
$html .= $closeButton;
|
||||
}
|
||||
$html .= '<div class="sodino-banner-content">';
|
||||
if ($banner->content_type !== 'image' && !empty($banner->link_url)) {
|
||||
$html .= '<a' . $linkAttributes . '>' . $content . '</a>';
|
||||
} else {
|
||||
$html .= $content;
|
||||
}
|
||||
$html .= '</div>';
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
function sodino_render_banner_position($position) {
|
||||
global $sodino_banner_service;
|
||||
|
||||
if (!isset($sodino_banner_service)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$banners = $sodino_banner_service->getActiveBanners(['position' => $position, 'limit' => 1]);
|
||||
if (empty($banners)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$banner = reset($banners);
|
||||
$sodino_banner_service->increaseImpression($banner->id);
|
||||
return sodino_get_banner_html($banner);
|
||||
}
|
||||
|
||||
function sodino_render_top_banner() {
|
||||
if (is_admin()) {
|
||||
return;
|
||||
}
|
||||
|
||||
echo sodino_render_banner_position('top');
|
||||
}
|
||||
|
||||
function sodino_render_middle_banner($content) {
|
||||
if (is_admin() || !is_singular() || !in_the_loop() || is_feed()) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$banner = sodino_render_banner_position('middle');
|
||||
if (empty($banner)) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
return $banner . $content;
|
||||
}
|
||||
|
||||
function sodino_render_bottom_banner() {
|
||||
if (is_admin()) {
|
||||
return;
|
||||
}
|
||||
|
||||
echo sodino_render_banner_position('bottom');
|
||||
}
|
||||
|
||||
function sodino_render_product_banner() {
|
||||
if (is_admin()) {
|
||||
return;
|
||||
}
|
||||
|
||||
echo sodino_render_banner_position('product_page');
|
||||
}
|
||||
|
||||
function sodino_render_cart_banner() {
|
||||
if (is_admin()) {
|
||||
return;
|
||||
}
|
||||
|
||||
echo sodino_render_banner_position('cart');
|
||||
}
|
||||
|
||||
function sodino_handle_banner_click() {
|
||||
if (!isset($_POST['banner_id']) || !isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'sodino_banner_click')) {
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
$bannerId = intval($_POST['banner_id']);
|
||||
if (!$bannerId) {
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
global $sodino_banner_service;
|
||||
if (!isset($sodino_banner_service)) {
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
$sodino_banner_service->increaseClick($bannerId);
|
||||
wp_send_json_success();
|
||||
}
|
||||
65
public/js/banner-frontend.js
Normal file
65
public/js/banner-frontend.js
Normal file
@@ -0,0 +1,65 @@
|
||||
(function () {
|
||||
const closeButtons = document.querySelectorAll('.sodino-banner-close');
|
||||
const clicked = new Set();
|
||||
|
||||
function getCookie(name) {
|
||||
const value = '; ' + document.cookie;
|
||||
const parts = value.split('; ' + name + '=');
|
||||
if (parts.length === 2) {
|
||||
return parts.pop().split(';').shift();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function sendClick(bannerId) {
|
||||
if (!bannerId || clicked.has(bannerId) || !window.sodinoBannerFrontend) {
|
||||
return;
|
||||
}
|
||||
|
||||
clicked.add(bannerId);
|
||||
|
||||
fetch(window.sodinoBannerFrontend.ajaxUrl, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
|
||||
body: 'action=sodino_banner_click&banner_id=' + encodeURIComponent(bannerId) + '&security=' + encodeURIComponent(window.sodinoBannerFrontend.nonce),
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('click', function (event) {
|
||||
const target = event.target.closest('.sodino-banner-link');
|
||||
if (target) {
|
||||
const bannerId = target.dataset.bannerId;
|
||||
sendClick(bannerId);
|
||||
}
|
||||
});
|
||||
|
||||
closeButtons.forEach(function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
const wrapper = this.closest('.sodino-banner-wrap');
|
||||
if (!wrapper) {
|
||||
return;
|
||||
}
|
||||
wrapper.style.display = 'none';
|
||||
const bannerId = wrapper.dataset.bannerId;
|
||||
if (bannerId) {
|
||||
document.cookie = 'sodino_banner_' + bannerId + '=hidden; path=/; max-age=' + 60 * 60 * 24;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.sodino-banner-wrap').forEach(function (banner) {
|
||||
const bannerId = banner.dataset.bannerId;
|
||||
if (getCookie('sodino_banner_' + bannerId) === 'hidden') {
|
||||
banner.style.display = 'none';
|
||||
}
|
||||
|
||||
if (banner.classList.contains('sodino-banner-popup')) {
|
||||
setTimeout(function () {
|
||||
if (getCookie('sodino_banner_' + bannerId) !== 'hidden') {
|
||||
banner.style.display = 'block';
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user