Commit inicial - WordPress Análisis de Precios Unitarios

- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
<svg style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<svg id="icon-tve-dash-notification" width="40" height="41" viewBox="0 0 40 41" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle opacity="0.7" cx="20" cy="20.7935" r="20"/>
<rect x="10" y="10.7935" width="20" height="20" rx="1"/>
<path d="M30 25.0793H23.5135C23.5135 25.0793 23.5135 27.9365 20.2703 27.9365C17.027 27.9365 17.027 25.0793 17.027 25.0793H10"/>
</svg>
<svg id="icon-tve-dash-tar" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1" y="1" width="14" height="14" rx="1" stroke="#879198" stroke-width="1.5"/>
<path d="M15 11H10.4595C10.4595 11 10.4595 13 8.18919 13C5.91892 13 5.91892 11 5.91892 11H1" stroke="#879198" stroke-width="1.5"/>
</svg>
<svg id="icon-tvd-notification-cross" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.8211 1.3415L10.6451 0.166504L5.98305 4.82484L1.32097 0.166504L0.14502 1.3415L4.80711 5.99984L0.14502 10.6582L1.32097 11.8332L5.98305 7.17484L10.6451 11.8332L11.8211 10.6582L7.159 5.99984L11.8211 1.3415Z" fill="currentColor"/>
</svg>
<svg id="icon-notification-type-error" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2.00006C6.47 2.00006 2 6.47006 2 12.0001C2 17.5301 6.47 22.0001 12 22.0001C17.53 22.0001 22 17.5301 22 12.0001C22 6.47006 17.53 2.00006 12 2.00006ZM14.59 8.00006L12 10.5901L9.41 8.00006L8 9.41006L10.59 12.0001L8 14.5901L9.41 16.0001L12 13.4101L14.59 16.0001L16 14.5901L13.41 12.0001L16 9.41006L14.59 8.00006ZM4 12.0001C4 16.4101 7.59 20.0001 12 20.0001C16.41 20.0001 20 16.4101 20 12.0001C20 7.59006 16.41 4.00006 12 4.00006C7.59 4.00006 4 7.59006 4 12.0001Z" fill="currentColor"/>
</svg>
<svg id="icon-notification-type-info" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM12 20C7.59 20 4 16.41 4 12C4 7.59 7.59 4 12 4C16.41 4 20 7.59 20 12C20 16.41 16.41 20 12 20ZM10 14.17L16.59 7.58L18 9L10 17L6 13L7.41 11.59L10 14.17Z" fill="currentColor"/>
</svg>
<svg id="icon-notification-type-success" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM12 20C7.59 20 4 16.41 4 12C4 7.59 7.59 4 12 4C16.41 4 20 7.59 20 12C20 16.41 16.41 20 12 20ZM10 14.17L16.59 7.58L18 9L10 17L6 13L7.41 11.59L10 14.17Z" fill="currentColor"/>
</svg>
<svg id="icon-notification-type-warning" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.99 2.00012C6.47 2.00012 2 6.48012 2 12.0001C2 17.5201 6.47 22.0001 11.99 22.0001C17.52 22.0001 22 17.5201 22 12.0001C22 6.48012 17.52 2.00012 11.99 2.00012ZM13 13.0001V7.00012H11V13.0001H13ZM13 17.0001V15.0001H11V17.0001H13ZM4 12.0001C4 16.4201 7.58 20.0001 12 20.0001C16.42 20.0001 20 16.4201 20 12.0001C20 7.58012 16.42 4.00012 12 4.00012C7.58 4.00012 4 7.58012 4 12.0001Z" fill="currentColor"></path>
</svg>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
module.exports = Backbone.Collection.extend( {
/**
* helper function to get the last item of a collection
*
* @return Backbone.Model
*/
last: function () {
return this.at( this.size() - 1 );
}
} );

View File

@@ -0,0 +1,6 @@
var NotificationModel = require( './../models/Notification' );
module.exports = Backbone.Collection.extend({
model: NotificationModel,
});

View File

@@ -0,0 +1,46 @@
module.exports = Backbone.Model.extend({
idAttribute: 'id',
defaults: {
active: {},
dismissed: {},
},
getDate: function () {
if (this.has('start') && this.get('start')) {
let startDate = new Date(this.get('start'));
let currentDate = new Date(TD_Notification?.date_time_now);
let timeDiff = currentDate.getTime() - startDate.getTime();
// Convert the difference to months, days, hours, or minutes
let yearsDiff = Math.floor(timeDiff / (1000 * 3600 * 24 * 365));
if (yearsDiff > 0) {
return yearsDiff + (yearsDiff === 1 ? " year ago" : " years ago");
}
let monthsDiff = Math.floor(timeDiff / (1000 * 3600 * 24 * 30));
if (monthsDiff > 0) {
return monthsDiff + (monthsDiff === 1 ? " month ago" : " months ago");
}
let daysDiff = Math.floor(timeDiff / (1000 * 3600 * 24));
if (daysDiff > 0) {
return daysDiff + (daysDiff === 1 ? " day ago" : " days ago");
}
let hoursDiff = Math.floor(timeDiff / (1000 * 3600));
if (hoursDiff > 0) {
return hoursDiff + (hoursDiff === 1 ? " hour ago" : " hours ago");
}
let minsDiff = Math.floor(timeDiff / (1000 * 60));
if (minsDiff > 0) {
return minsDiff + (minsDiff === 1 ? " minute ago" : " minutes ago");
}
return "just now";
} else {
return "";
}
}
});

View File

@@ -0,0 +1,131 @@
TD_Notification = TD_Notification || {};
// Import necessary modules
const NotificationDrawer = require( './views/NotificationDrawer' );
const NotificationsList = require( './views/NotificationsList' );
const NotificationCollection = require( './collections/Notifications' );
const FooterView = require( './views/NotificationFooter' );
( function ( $ ) {
$(document).ready(function () {
// Initialize variables
let notificationType = 'Active';
let notificationData = [];
let notificationListView = null;
let footerView = null;
// Function to render the UI
const renderUI = function () {
// Filter data based on notification type
const filteredData = notificationType === 'Active' ? TD_Notification?.data?.active : TD_Notification?.data?.dismissed;
// Count dismissed and active notifications
const dismissedCount = TD_Notification?.data?.dismissed?.length;
const activeCount = TD_Notification?.data?.active?.length;
if ( activeCount <= 0 ) {
$('.notification-indicator').parent().remove();
}
// Create a new collection with filtered data
const collection = new NotificationCollection( filteredData );
// Render NotificationDrawer
const notificationDrawer = new NotificationDrawer( {
el: $( '.td-app-notification-counter' ),
collection: collection,
activeCount,
dismissedCount
} );
notificationDrawer.render();
// Render NotificationsList
notificationListView = new NotificationsList( {
el: $('.td-app-notification-holder'),
collection,
notificationType,
dismissedCount,
activeCount,
} );
notificationListView.render();
footerView = new FooterView({
el: $('.notification-footer'),
collection: collection,
notificationType: notificationType,
hideDismissAll: notificationType === 'Dismissed' || activeCount < 2,
});
footerView.render();
// Event handler for collection change
notificationListView.on( 'collectionChanged', function ( data ) {
notificationData = data.collection.toJSON();
renderUI(); // Re-render UI with updated data
} );
footerView.on( 'collectionChanged', function ( data ) {
notificationData = data.collection.toJSON();
renderUI(); // Re-render UI with updated data
} );
// Event handler for notification type change
notificationListView.on( 'notificationTypeChanged', function ( data ) {
notificationType = data?.notification_type ? data?.notification_type : 'Active';
renderUI(); // Re-render UI with updated notification type
} );
$('.tve-notification').last().css('border-bottom', 'none')
};
window.render_ui = renderUI;
// Check visibility every 100 milliseconds
const interval = setInterval(function () {
if ($('.td-app-notification-counter').is(':visible')) {
// Render the UI for the first time
renderUI();
clearInterval(interval);
}
}, 100);
// Initial check in case the element is already visible when the script runs
if ($('.td-app-notification-counter').is(':visible')) {
renderUI();
clearInterval(interval);
}
const checkEmptyOrNot = setInterval(function () {
if ($('.td-app-notification-wrapper').is(':empty')) {
renderUI();
} else {
clearInterval(checkEmptyOrNot);
}
}, 100);
// Function to close the notification drawer
const closeNotificationDrawer = function () {
$( '.td-app-notification-overlay' ).addClass( 'close' );
$( '.td-app-notification-drawer' ).removeClass( 'open' );
};
// Event listener for Esc key press
$(document).on('keydown', function (e) {
if (e.key === 'Escape') {
closeNotificationDrawer();
}
});
const getUrlParameter = function(name) {
name = name.replace(/[\[\]]/g, '\\$&'); // Escape special characters for regex
const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
const results = regex.exec(window.location.href);
if (!results) return null; // If no match is found
if (!results[2]) return ''; // If the parameter exists but no value is set
return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
if ( getUrlParameter('notify') == 1 ) {
$( '.td-app-notification-overlay' ).removeClass( 'close' );
$( '.td-app-notification-drawer' ).addClass( 'open' );
}
});
} )( jQuery );

View File

@@ -0,0 +1,12 @@
/**
* Created by Russel Hussain on 4/29/2024.
*/
( function ( $ ) {
module.exports = Backbone.View.extend( {
render: function () {
return this;
}
} )
} )( jQuery );

View File

@@ -0,0 +1,47 @@
/**
* Created by Russel Hussain on 05/05/2024.
*/
( function ( $ ) {
const BaseView = require( './Base' );
module.exports = BaseView.extend( {
notificationType: 'Active',
translate: TD_Notification.t,
initialize: function ( options ) {
if (options && options.notificationType) {
this.notificationType = options.notificationType;
}
},
render: function () {
this.$el.html( this.getHtml() );
return this;
},
getHtml: function () {
let theme_class = TD_Notification.notify_class ? TD_Notification.notify_class : '';
let html = '<div class="no-notifications no-notifications-'+ theme_class +'">';
let notificationType = this.notificationType;
if ( notificationType === 'Active' ) {
html += '<img alt="Dannie the Detective" src="'+ TD_Notification?.image_url +'">' +
'<div class="great-scott great-scott-'+ theme_class +'">' + this.translate.no_new_title + '</div>' +
'<div class="no-new-notifications no-new-notification-'+ theme_class +'">' + this.translate.no_new + '</div>' +
'<span class="switch-notification-type toggle-to-dismissed switch-notification-type-'+ theme_class + '">' + this.translate.see_dismissed +'</span>';
}
if (notificationType === 'Dismissed') {
html += '<img alt="Dannie the Detective" src="'+ TD_Notification?.image_url +'">' +
'<div class="great-scott great-scott-'+ theme_class +'">' + this.translate.no_new_title + '</div>' +
'<div class="no-new-notifications no-new-notification-'+ theme_class +'">' + this.translate.no_dismissed + '</div>' +
'<span class="switch-notification-type toggle-to-active switch-notification-type-'+ theme_class +'">' + this.translate.see_new + '</span>';
}
html += '</div>';
return html;
}
} );
} )( jQuery );

View File

@@ -0,0 +1,111 @@
( function ( $ ) {
const BaseView = require( './Base' );
module.exports = BaseView.extend( {
render: function () {
this.$el.append( this.getHtml() );
return this;
},
getHtml: function () {
let theme_class = TD_Notification.notify_class ? TD_Notification.notify_class : '';
let html = '<div class="tve-notification notification-'+ theme_class +'">' +
'<div class="icon icon-'+ theme_class +'">' +
'<svg class="tve-circle-close type-' + this.model.get('type') + '" xmlns="http://www.w3.org/2000/svg">><use xlink:href="#icon-notification-type-' + this.model.get('type') + '"></use></svg>' +
'</div>' +
'<div class="body">' +
'<div class="title title-'+ theme_class +'">' +
'<div>' + this.model.get('title') + '</div>';
if (this.model.getDate()) {
html += '<div class="date date-'+ theme_class +'">' + this.model.getDate() + '</div>';
}
html += '</div>' +
'<div class="notification-content notification-content-'+ theme_class +'">' + this.addMediaPlayer(this.model.get('content')) + '</div>' +
'<div class="tvd-notification-actions">';
if ( this.model.get( 'button1_action' )) {
html += '<a type="button" class="tve-dash-button notify-primary notify-primary-'+ theme_class +'" href="' + this.model.get( 'button1_action' ) + '" target="_blank" rel="noopener noreferrer">' + this.model.get( 'button1_label' ) + '</a>';
}
if ( this.model.get( 'button2_action' ) ) {
html += '<a type="button" class="tve-dash-button notify-secondary notify-secondary-'+ theme_class +'" href="' + this.model.get( 'button2_action' ) + '" target="_blank" rel="noopener noreferrer">' + this.model.get( 'button2_label' ) + '</a>';
}
if ( this.model.get('dismissed') != 1 ) {
html += '<span class="dismiss-notification dismiss-notification-'+ theme_class +'" data-id="' + this.model.get('notification_id') + '">Dismiss</span>';
}
html += '</div>' +
'</div>' +
'</div>';
return html;
},
addMediaPlayer: function (content) {
content = content.replace(/&nbsp;/g, '<br>');
// Check for direct video links (mp4, webm, ogg)
const videoRegex = /(https?:\/\/\S+\.(mp4|webm|ogg))/gi;
if (videoRegex.test(content)) {
content = content.replace(videoRegex, (match, videoUrl) => {
return `<div class="notification-content-video-container"><video class="custom-video-player" controls>
<source src="${videoUrl}" type="video/mp4">
Your browser does not support the video tag.
</video></div>`;
});
}
// Check for YouTube links
const youtubeRegex = /(https?:\/\/(www\.)?youtube\.com\/watch\?v=[\w-]+|https?:\/\/youtu\.be\/[\w-]+)/gi;
if (youtubeRegex.test(content)) {
content = content.replace(youtubeRegex, (match, youtubeUrl) => {
const urlParams = new URLSearchParams(new URL(youtubeUrl).search);
const videoId = urlParams.get('v') || youtubeUrl.split('/').pop();
const embedUrl = `https://www.youtube.com/embed/${videoId}`;
return `<div class="notification-content-video-container"><iframe class="custom-iframe" src="${embedUrl}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></div>`;
});
}
const youtubeFigureRegex = /<figure[^>]*>\s*<div[^>]*>\s*(https?:\/\/(www\.)?youtube\.com\/watch\?v=[\w-]+|https?:\/\/youtu\.be\/[\w-]+)\s*<\/div>\s*<\/figure>/gi;
content = content.replace(youtubeFigureRegex, (match, youtubeUrl) => {
const urlParams = new URLSearchParams(new URL(youtubeUrl).search);
const videoId = urlParams.get('v') || youtubeUrl.split('/').pop();
const embedUrl = `https://www.youtube.com/embed/${videoId}`;
return `<div class="notification-content-video-container"><iframe class="custom-iframe" src="${embedUrl}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></div>`;
});
// Check for GIF images inside <a> tags
const gifInAnchorRegex = /<a[^>]*>\s*(https?:\/\/\S+\.(gif))\s*<\/a>/gi;
if (gifInAnchorRegex.test(content)) {
content = content.replace(gifInAnchorRegex, (match, gifUrl) => {
return `<div class="notification-content-image-container"><img class="custom-image-player" src="${gifUrl}" alt="GIF"></div>`;
});
}
// Check for image links (jpg, jpeg, png, gif)
const imageRegex = /(https?:\/\/\S+\.(jpg|jpeg|png|gif))/gi;
if (content.includes('<img')) {
return content;
}
if (imageRegex.test(content)) {
content = content.replace(imageRegex, (match, imageUrl) => {
return `<div class="notification-content-image-container"><img class="custom-image-player" src="${imageUrl}" alt="Image"></div>`;
});
}
const htmlTagRegex = /<\/?[a-z][\s\S]*>/i;
if (!htmlTagRegex.test(content)) {
content = content.replace(/\n/g, '<br>');
}
return content;
}
} );
} )( jQuery );

View File

@@ -0,0 +1,48 @@
(function ( $ ) {
const BaseView = require( './Base' );
module.exports = BaseView.extend( {
dismissedCount: 0,
activeCount: 0,
events: {
'click .tvd-notifications-btn': 'open',
'click .td-app-notification-counter-holder': 'open',
'click .text-notify-t-automator': 'open',
},
initialize: function ( options ) {
$( window ).on('scroll', this.adjustDrawerPosition.bind(this));
$( document ).on('click', this.handleDocumentClick.bind(this));
if ( options ) {
this.activeCount = options.activeCount;
this.dismissedCount = options.dismissedCount;
}
},
adjustDrawerPosition: function () {
const scrollTop = $( window ).scrollTop();
const newTop = 124 + scrollTop + 'px';
this.$( '.td-app-notification-drawer' ).css( 'top', newTop );
},
render: function () {
this.$( '.td-app-notification-counter-holder' ).text( this.activeCount );
this.activeCount >= 1 ? this.$( '.td-app-notification-counter-holder' ).show() : this.$( '.td-app-notification-counter-holder' ).hide();
return this;
},
open: function () {
$( '.td-app-notification-overlay' ).removeClass( 'close' );
$( '.td-app-notification-drawer' ).addClass( 'open' );
},
handleDocumentClick: function (event) {
if ( $(event.target).closest('.td-app-notification-overlay.overlay').length) {
$( '.td-app-notification-overlay' ).addClass( 'close' );
$( '.td-app-notification-drawer' ).removeClass( 'open' );
}
}
} );
} )( jQuery );

View File

@@ -0,0 +1,73 @@
( function ( $ ) {
const BaseView = require( './Base' );
module.exports = BaseView.extend( {
hideDismissAll: false,
events: {
'click .dismiss-all': 'dismissAll',
},
initialize: function ( options ) {
if ( options && options.hideDismissAll ) {
this.hideDismissAll = options.hideDismissAll;
}
this.listenTo( this.collection, 'change:dismiss-all', this.collectionChanged );
},
render: function () {
this.$el.html( this.getHtml() );
return this;
},
getHtml: function () {
let theme_class = TD_Notification.notify_class ? TD_Notification.notify_class : '';
let html = '<div class="pagination pagination-'+ theme_class +'"></div>' +
'<div class="dismiss-all dismiss-all-'+ theme_class +'">';
if ( !this.hideDismissAll ) {
html += '<span class="dismiss-all dismiss-all-'+ theme_class +'">Dismiss All</span>';
}
html += '</div>';
return html;
},
dismissAll: function () {
this.collection.each(function (item) {
const index = TD_Notification.data.active.findIndex(notification => notification.notification_id == item.get('notification_id'));
if (index !== -1) {
TD_Notification.data.active.splice(index, 1);
}
TD_Notification.data.dismissed.unshift(item.toJSON());
});
this.collectionChanged();
this.dismissAllNotification();
},
dismissAllNotification: function () {
$.ajax( {
type: 'POST',
url: TD_Notification.baseUrl + '/dismiss-all',
headers: {
'X-WP-Nonce': TD_Notification?.dismiss_nonce // Pass the nonce in the headers
},
success: function (response) {
// Handle success response
},
error: function (xhr, status, error) {
console.error('Error marking notification as read:', error);
}
} );
},
collectionChanged: function () {
this.trigger( 'collectionChanged', { notification_type: 'Active', collection: this.collection } );
}
});
} )( jQuery );

View File

@@ -0,0 +1,75 @@
( function ( $ ) {
let BaseView = require( './Base' );
module.exports = BaseView.extend( {
data: {
notification_type: 'Active',
notification_number: 0,
active_count: 0,
dismissed_count: 0
},
events: {
'click .toggle-to-dismissed': 'toggleNotification',
'click .toggle-to-active': 'toggleNotification'
},
initialize: function ( options ) {
if ( options ) {
this.data.active_count = options.activeCount;
this.data.dismissed_count = options.dismissedCount;
this.data.notification_type = options.notificationType;
}
if ( this.data.notification_type === 'Dismissed' ) {
this.collection.each( function ( item ) {
if ( item.get( 'dismissed' ) == 0 ) {
item.set( 'dismissed', 1 );
}
});
}
this.data.notification_number = this.collection.length;
},
render: function () {
this.$el.html( this.getHtml() );
return this;
},
getHtml: function () {
let html = '';
let theme_class = TD_Notification.notify_class ? TD_Notification.notify_class : '';
if ( this.data.notification_type === 'Active' ) {
html += '<span class="tvd-notification-number notification-number-'+ theme_class +'">(' + this.data.notification_number + ') New Notifications</span>';
} else if ( this.data.notification_type === 'Dismissed' ) {
html += '<span class="tvd-notification-number notification-number-'+ theme_class +'"> (' + this.data.notification_number + ') Notifications</span>';
}
html += '<div class="toggle-notifications">';
//If current tab is active and no of dismissed notification is zero, no need to show
if ( this.data.notification_type === 'Active' && this.data.dismissed_count > 0 ) {
html += '<span class="toggle-to-dismissed toggle-notifications toggle-'+ theme_class +'">Dismissed Notifications</span>';
}
//If Current tab is Dismissed should have option to go to active notification
if ( this.data.notification_type === 'Dismissed' ) {
html += '<span class="toggle-to-active toggle-notifications toggle-'+ theme_class +'">Active Notifications</span>';
}
html += '</div>';
html += '<span class="tvd-close-notification-drawer close-notification-'+ theme_class +'">';
html += '<svg class="icon-tve-dash-notification-cross"><use xlink:href="#icon-tvd-notification-cross"></use></svg>';
html += '</span>';
return html;
},
toggleNotification: function ( event ) {
const notificationType = $( event.currentTarget ).hasClass( 'toggle-to-dismissed' ) ? 'Dismissed' : 'Active';
if (this.data.notification_type !== notificationType) {
this.data.notification_type = notificationType;
this.trigger('notificationTypeChanged', { notification_type: notificationType });
}
event.stopPropagation();
}
} );
} )( jQuery );

View File

@@ -0,0 +1,149 @@
(function ( $ ) {
let BaseView = require('./Base');
let HeaderView = require('./NotificationHeader');
let FooterView = require('./NotificationFooter');
let NotificationView = require('./NotificationController');
let NoNotificationView = require('./NoNotification');
module.exports = BaseView.extend( {
headerView: null,
hideDismissAll: false,
notificationType: 'Active',
dismissedCount: 0,
activeCount: 0,
events: {
'click .tvd-close-notification-drawer': 'close',
'click .dismiss-notification': 'dismiss',
'click .switch-notification-type': 'toggleNotification',
},
initialize: function ( options ) {
if ( options ) {
this.activeCount = options.activeCount;
this.dismissedCount = options.dismissedCount;
this.hideDismissAll = options.notificationType === 'Dismissed' || this.activeCount < 2;
this.notificationType = options.notificationType;
}
this.listenTo( this.collection, 'change:dismiss', this.collectionChanged );
},
render: function () {
this.renderHeader();
const $wrapperContainer = this.$('.td-app-notification-wrapper');
if (this.collection.length > 0) {
$wrapperContainer.html('<div class="tvd-notifications-list"></div>');
this.renderList();
} else {
$wrapperContainer.empty();
new NoNotificationView( {
el: $wrapperContainer,
notificationType: this.notificationType
} ).render();
}
if ($wrapperContainer.find('.notification-footer').length === 0) {
this.renderFooter();
}
return this;
},
renderHeader: function () {
this.headerView = new HeaderView( {
el: this.$( '.td-app-notification-header' ),
collection: this.collection,
activeCount: this.activeCount,
dismissedCount: this.dismissedCount,
notificationType: this.notificationType
} ).render();
this.listenTo( this.headerView, 'notificationTypeChanged', this.notificationTypeChanged );
},
renderList: function () {
const $listContainer = this.$( '.tvd-notifications-list' );
$listContainer.empty();
// Instantiate NotificationView outside the loop
const notificationViews = this.collection.map(function(model) {
return new NotificationView({
model: model
});
});
// Render each NotificationView instance within the loop
notificationViews.forEach(function(notificationView) {
$listContainer.append(notificationView.render().el);
});
},
renderFooter: function () {
const $notificationFooter = $( '.notification-footer' );
$notificationFooter.empty();
new FooterView({
el: $notificationFooter,
collection: this.collection,
notificationType: this.notificationType,
hideDismissAll: this.hideDismissAll,
}).render();
},
toggleNotification: function ( event ) {
const notificationType = $( event.currentTarget ).hasClass( 'toggle-to-dismissed' ) ? 'Dismissed' : 'Active';
this.trigger('notificationTypeChanged', { notification_type: notificationType });
},
dismiss: function ( event ) {
event.preventDefault();
let notification_id = $( event.currentTarget ).data( 'id' );
let item = this.collection.findWhere( { notification_id: String(notification_id) } );
if ( item ) {
this.collection.remove(item);
const index = TD_Notification.data.active.findIndex(notification => notification.notification_id== notification_id);
if (index !== -1) {
TD_Notification.data.active.splice(index, 1);
}
TD_Notification.data.dismissed.unshift(item.toJSON());
this.dismissNotification( notification_id );
this.collectionChanged();
}
},
dismissNotification: function ( remoteId ) {
$.ajax( {
type: 'POST',
url: TD_Notification.baseUrl + '/dismiss',
headers: {
'X-WP-Nonce': TD_Notification?.dismiss_nonce // Pass the nonce in the headers
},
data: {
notification_id: remoteId,
},
success: function ( response ) {
// Handle success response
},
error: function (xhr, status, error) {
console.error('Error marking notification as read:', error);
}
} );
},
close: function () {
const notificationType = 'Active';
this.trigger('notificationTypeChanged', { notification_type: notificationType });
$( '.td-app-notification-overlay' ).addClass( 'close' );
$( '.td-app-notification-drawer' ).removeClass( 'open' );
},
collectionChanged: function () {
this.trigger( 'collectionChanged', { notification_type: 'Active', collection: this.collection } );
},
notificationTypeChanged: function ( data ) {
this.trigger( 'notificationTypeChanged', data );
}
} );
} )( jQuery );

View File

@@ -0,0 +1,278 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
if ( ! class_exists( 'App_Notification' ) ) {
class App_Notification {
protected $namespace = 'tve-dash/v1';
protected $class_path;
protected $base_path;
protected $template_path;
protected $current_page;
/**
* @var App_Notification
*/
public static $_instance;
/**
* App_Notification constructor.
*/
private function __construct() {
$this->base_path = dirname( dirname( __FILE__ ) );
$this->class_path = $this->base_path . '/classes';
$current_page = isset( $_GET['page'] ) ? $_GET['page'] : '';
$this->current_page = isset( $_GET['action'] ) ? $_GET['action'] : $current_page;
$this->template_path = $this->base_path . '/templates';
$this->_load();
$this->_hooks();
}
/**
* Singleton instance
*
* @return App_Notification
*/
public static function instance() {
if ( is_null( self::$_instance ) ) {
self::$_instance = new self();
}
return self::$_instance;
}
/**
* Enqueue notification scripts
*/
public function tve_dash_enqueue_notification_scripts() {
$limit = 10;
$offset = 0;
$js_prefix = tve_dash_is_debug_on() ? '.js' : '.min.js';
$notifications = ( new TD_Notifications )->check_notification();
$product_key = TD_Ian_Helper::get_product_name_by_page( $this->current_page );
$notify_class = TD_Ian_Helper::class_by_product( $product_key );
$image_url = TVE_DASH_URL . '/css/images/No-notifications-Thrive.png';
$params = array(
'baseUrl' => tva_get_rest_route_url( $this->namespace, 'app-notification' ),
't' => include $this->path( 'i18n.php' ),
'dash_url' => admin_url( 'admin.php?page=tve_dash_section' ),
'image_url' => $image_url,
'data' => $notifications,
'limit' => $limit,
'offset' => $offset + $limit,
'current_page' => $this->current_page,
'notify_class' => $notify_class,
'date_time_now' => current_time( 'Y-m-d H:i:s ' ),
'dismiss_nonce' => wp_create_nonce( 'wp_rest' ),
);
tve_dash_enqueue_style( 'tve_dash_app_notification',
TVE_DASH_URL . '/inc/app-notification/assets/css/tve-app-notification.css' );
tve_dash_enqueue_script( 'tve_dash_app_notification',
TVE_DASH_URL . '/inc/app-notification/assets/dist/tve-app-notification' . $js_prefix, array(
'jquery',
'backbone',
), TVE_DASH_VERSION, true );
tve_dash_admin_enqueue_scripts( 'tve_dash_app_notification' );
wp_enqueue_style( 'wp-block-library' );
wp_enqueue_style( 'wp-block-library-theme' );
wp_localize_script( 'tve_dash_app_notification', 'TD_Notification', $params );
}
/**
* Check if the current page is the editor
*
* @return bool
*/
private function isEditor() {
return 'architect' === $this->current_page;
}
private function is_ttb() {
return 'thrive-theme-dashboard' === $this->current_page;
}
/**
* Enqueue notification icons
*/
public function tve_dash_enqueue_notification_icons() {
$icon_file_path = TVE_DASH_PATH . '/inc/app-notification/assets/css/notification-icons.svg';
if ( file_exists( $icon_file_path ) ) {
include_once $icon_file_path;
}
}
/**
* Register REST routes
*/
public static function register_rest_routes() {
require_once dirname( __FILE__ ) . DIRECTORY_SEPARATOR . "Notification_Controller.php";
$notificationController = new TD_Notification_Controller();
$notificationController->register_routes();
}
/**
* Load required files
*
* @param string $path
*/
private function _load( $path = '' ) {
$path = $path ? $path : $this->class_path;
$dir = new DirectoryIterator( $path );
foreach ( $dir as $file ) {
if ( $file->isDot() ) {
continue;
}
if ( file_exists( $file->getPathname() ) && $file->isFile() ) {
require_once( $file->getPathname() );
}
}
// Run the migration if db tables are not created
TD_DbMigration::migrate();
if ( ! wp_next_scheduled( 'delete_expired_notice_daily' ) ) {
wp_schedule_event( time(), 'daily', 'delete_expired_notice_daily' );
}
add_action( 'delete_expired_notice_daily', [ $this, 'delete_expired_notices' ] );
}
public function delete_expired_notices() {
global $wpdb;
$current_time = current_time( 'Y-m-d H:i:s' );
$expired_notices_query = $wpdb->prepare( "
SELECT ID
FROM {$wpdb->prefix}td_app_notifications
WHERE end < %s
", $current_time );
$expired_notices = $wpdb->get_results( $expired_notices_query );
foreach ( $expired_notices as $notice ) {
$wpdb->delete( $wpdb->prefix . 'td_app_notifications', array( 'ID' => $notice->ID ) );
}
}
/**
* Get the full path to a file
*
* @param string $file
*
* @return string
*/
private function path( $file = '' ) {
return untrailingslashit( $this->base_path ) . ( ! empty( $file ) ? DIRECTORY_SEPARATOR : '' ) . ltrim( $file,
'\\/' );
}
/**
* Hook into WordPress actions and filters
*/
public function _hooks() {
add_filter( 'tve_dash_admin_product_menu', array( $this, 'add_to_dashboard_menu' ) );
if ( $this->isEditor() ) {
add_action( 'wp_loaded', array( $this, 'tve_dash_enqueue_notification_scripts' ) );
add_action( 'tcb_editor_iframe_after', array( $this, 'include_notification_icons_svg' ) );
} else {
add_action( 'admin_enqueue_scripts', array( $this, 'tve_dash_enqueue_notification_scripts' ) );
}
add_action( 'admin_enqueue_scripts', array( $this, 'tve_dash_enqueue_notification_icons' ) );
add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) );
add_action( 'tvd_notification_inbox', array( $this, 'notification_button' ) );
}
public function include_notification_icons_svg() {
$file_path = TVE_DASH_PATH . '/inc/app-notification/assets/css/notification-icons.svg';
if ( file_exists( $file_path ) ) {
include $file_path;
}
}
public static function get_unread_count() {
global $wpdb;
$current_time = current_time( 'Y-m-d H:i:s' );
$unread_count = $wpdb->get_var(
$wpdb->prepare(
"SELECT COUNT(id) FROM {$wpdb->prefix}td_app_notifications WHERE dismissed = 0 AND start <= %s",
$current_time
)
);
if ( $unread_count > 99 ) {
$unread_count = '99+';
}
return $unread_count;
}
/**
* Add notifications to the dashboard menu
*
* @param array $menus
*
* @return array
*/
public function add_to_dashboard_menu( $menus = array() ) {
$count = static::get_unread_count();
if ( $count > 0 ) {
$menus['app_notifications'] = array(
'parent_slug' => 'tve_dash_section',
'page_title' => __( 'App Notification', 'thrive-dash' ),
'menu_title' => __( 'Notifications',
'thrive-dash' ) . '<span class="notification-indicator"></span>',
'capability' => TVE_DASH_CAPABILITY,
'menu_slug' => 'tve_dash_section&notify=1',
'function' => 'tve_dash_section',
);
} else {
unset( $menus['app_notifications'] );
}
return $menus;
}
/**
* Render the notification button
*
* @param bool $return
*
* @return string|void
*/
public function notification_button( $return = false ) {
$template = $this->template_path . '/app-notification-button.php';
ob_start();
if ( file_exists( $template ) ) {
include $template;
}
$html = ob_get_clean();
if ( $return ) {
return $html;
}
echo $html; // phpcs:ignore
}
}
}

View File

@@ -0,0 +1,45 @@
<?php
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
class TD_DbMigration {
static $tableName = 'td_app_notifications';
public static function migrate() {
global $wpdb;
$charsetCollate = $wpdb->get_charset_collate();
$table = $wpdb->prefix . static::$tableName;
if ( $wpdb->get_var("SHOW TABLES LIKE '$table'") != $table ) {
$sql = "CREATE TABLE $table (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`slug` varchar(255) NOT NULL,
`title` text NOT NULL,
`content` longtext NOT NULL,
`type` varchar(64) NOT NULL,
`level` text NOT NULL,
`notification_id` bigint(20) unsigned DEFAULT NULL,
`notification_name` varchar(255) DEFAULT NULL,
`start` datetime DEFAULT NULL,
`end` datetime DEFAULT NULL,
`button1_label` varchar(255) DEFAULT NULL,
`button1_action` varchar(255) DEFAULT NULL,
`button2_label` varchar(255) DEFAULT NULL,
`button2_action` varchar(255) DEFAULT NULL,
`dismissed` tinyint(1) NOT NULL DEFAULT 0,
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (id),
KEY ian_dates (start, end),
KEY ian_type (type),
KEY ian_dismissed (dismissed)
) $charsetCollate;";
dbDelta($sql);
}
$index_exists = $wpdb->get_results("SHOW INDEX FROM $table WHERE Key_name = 'ian_slug'");
if ($index_exists) {
$wpdb->query("ALTER TABLE $table DROP INDEX ian_slug");
}
}
}

View File

@@ -0,0 +1,61 @@
<?php
class TD_Ian_Helper {
public static function product_list() {
return [
'tve_dash',// Thrive Dashboard
'thrive_automator',//Thrive Automator
'thrive_apprentice', //Thrive Apprentice
'tcm', // Thrive Comments
'tvo',// Thrive Ovation
'tab_admin_dashboard', // Thrive Optimize
'thrive_leads', // Thrive Leads
'tqb', // Thrive Quiz Builder
'tve_ult', //Thrive Ultimatum
'architect', //Thrive Architect
'thrive-theme-dashboard' //Thrive Theme Builder
];
}
public static function class_by_product( $product ) {
$classList = [
'tve_dash' => 'notify-td',
'thrive_automator' => 'notify-t-automator',
'thrive_apprentice' => 'notify-ta',
'tcm' => 'notify-tcm',
'tvo' => 'notify-tvo',
'tab_admin_dashboard' => 'notify-t-optimize',
'thrive_leads' => 'notify-thrive-leads',
'tqb' => 'notify-tqb',
'tve_ult' => 'notify-tve-ult',
'architect' => 'notify-tar',
'thrive-theme-dashboard' => 'notify-ttb'
];
return isset( $classList[ $product ] ) ? $classList[ $product ] : '';
}
public static function get_product_name_by_page( $page ) {
$products = self::product_list();
$product = "";
foreach ( $products as $p ) {
if ( strpos( $page, $p ) !== false ) {
$product = $p;
break;
}
}
return $product;
}
public static function get_user_product_ids() {
$user_license_details = thrive_get_transient( 'td_ttw_licenses_details' );
$user_products = [];
foreach ( $user_license_details as $license ) {
$user_products[] = $license['product_id'];
}
return $user_products;
}
}

View File

@@ -0,0 +1,104 @@
<?php
/**
* Notification Controller Class
*
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
if ( ! class_exists( 'TD_Notification_Controller' ) ) {
/**
* Notification Controller Class.
*
* @since 1.0.0
*/
class TD_Notification_Controller {
/**
* REST namespace for the controller.
*
* @since 1.0.0
* @var string
*/
const REST_NAMESPACE = 'tve-dash/v1';
/**
* Registers REST API routes for the controller.
*
* @since 1.0.0
*/
public function register_routes() {
register_rest_route( static::REST_NAMESPACE, 'app-notification/dismiss', array(
array(
'methods' => \WP_REST_Server::CREATABLE,
'callback' => array( $this, 'dismiss_notification' ),
'args' => array(
'notification_id' => array(
'type' => 'integer || string',
'required' => true,
)
),
'permission_callback' => function () {
return current_user_can( 'manage_options' );
},
),
) );
register_rest_route( static::REST_NAMESPACE, 'app-notification/dismiss-all', array(
array(
'methods' => \WP_REST_Server::CREATABLE,
'callback' => array( $this, 'dismiss_all_notification' ),
'permission_callback' => function () {
return current_user_can( 'manage_options' );
},
),
) );
}
/**
* Dismisses a notification.
*
* @param WP_REST_Request $request The request object.
*
* @return WP_REST_Response Response object indicating success or failure.
* @since 1.0.0
*
*/
public function dismiss_notification( $request ) {
require_once dirname( __FILE__ ) . DIRECTORY_SEPARATOR . "Notifications.php";
global $wpdb;
$notification_id = intval( $request->get_param( 'notification_id' ) );
$updated = $wpdb->update( $wpdb->prefix . 'td_app_notifications', [ 'dismissed' => 1 ],
[ 'notification_id' => $notification_id ] );
if ( ! $updated ) {
return new WP_REST_Response( array( 'success' => false ), 403 );
}
( new TD_Notifications() )->update_transients();
return new WP_REST_Response( array( 'success' => true ), 200 );
}
/**
* Dismisses all notifications.
*
* @return WP_REST_Response Response object indicating success or failure.
* @since 1.0.0
*
*/
public function dismiss_all_notification() {
require_once dirname( __FILE__ ) . DIRECTORY_SEPARATOR . "Notifications.php";
global $wpdb;
$wpdb->update( $wpdb->prefix . 'td_app_notifications', [ 'dismissed' => 1 ], [ 'dismissed' => 0 ] );
( new TD_Notifications() )->update_transients();
return new WP_REST_Response( array( 'success' => true ), 200 );
}
}
}

View File

@@ -0,0 +1,253 @@
<?php
class TD_Notifications {
private $_url;
private $_connection;
private $_request_params;
public function __construct() {
$this->_connection = TD_TTW_Connection::get_instance();
$this->_request_params = [
'user_id' => $this->_connection->ttw_id,
'user_email' => $this->_connection->ttw_email,
'last_notification_id' => get_option( 'td_last_notification_id' ),
];
$this->_url = TD_TTW_Connection::get_ttw_url() . '/wp-content/notifications.json';
}
public function handle_notifications() {
if ( $this->is_transients_exist() ) {
return;
}
$notifications = $this->get( $this->_request_params );
if ( ! $notifications || ! is_array( $notifications ) || is_wp_error( $notifications ) ) {
return;
}
foreach ( $notifications as $notification ) {
if ( $this->is_exist( $notification['notification_id'] ) || ( isset( $notification['end'] ) && $this->is_expired( $notification['end'] ) ) ) {
continue;
}
$this->add_notification( $notification );
}
}
public function check_notification() {
$this->handle_notifications();
$notifications = $this->get_notification();
return $notifications;
}
private function get_notification( $update_call = false ) {
global $wpdb;
$table_name = $wpdb->prefix . 'td_app_notifications';
$current_time = current_time( 'Y-m-d H:i:s' );
if ( $this->is_transients_exist() && ! $update_call ) {
return get_transient( 'td_app_notifications_transients' );
}
$active_notifications_query = $wpdb->prepare(
"SELECT * FROM $table_name
WHERE start <= %s
AND (end IS NULL OR end >= %s)
AND dismissed = 0
ORDER BY start DESC",
$current_time, $current_time
);
$active_notifications = $wpdb->get_results( $active_notifications_query, ARRAY_A );
$dismissed_notifications_query = $wpdb->prepare( " SELECT *
FROM $table_name
WHERE dismissed = 1
AND (end IS NULL OR end >= %s)
ORDER BY start DESC
", $current_time );
$dismissed_notifications = $wpdb->get_results( $dismissed_notifications_query, ARRAY_A );
if ( ! $active_notifications && ! $dismissed_notifications ) {
return false;
}
$notifications = [
'active' => $active_notifications,
'dismissed' => $dismissed_notifications
];
if ( ! $update_call ) {
$this->store_transients( $notifications );
}
return $notifications;
}
private function add_notification( $notification ) {
if ( ! $notification || ! is_array( $notification ) || empty( $notification ) ) {
return false;
}
if ( isset( $notification['have_license'] ) && is_array( $notification['have_license'] ) ) {
if ( ! $this->verify_access( $notification['have_license'], $notification['dont_have_license'] ) ) {
return;
}
}
global $wpdb;
$table_name = $wpdb->prefix . 'td_app_notifications';
if ( isset( $notification['btns'] ) ) {
foreach ( $notification['btns'] as $key => $value ) {
if ( $key === 'main' ) {
$notification['button1_label'] = sanitize_text_field( $value['text'] );
$notification['button1_action'] = esc_url_raw( $value['url'] );
}
if ( $key === 'alt' ) {
$notification['button2_label'] = sanitize_text_field( $value['text'] );
$notification['button2_action'] = esc_url_raw( $value['url'] );
}
}
}
if ( isset( $notification['btns'] ) ) {
unset( $notification['btns'] );
}
unset( $notification['have_license'] );
unset( $notification['dont_have_license'] );
$additional_data = [
'created' => current_time( 'Y-m-d H:i:s' ),
'updated' => current_time( 'Y-m-d H:i:s' ),
];
$notification = array_merge( $notification, $additional_data );
$last_notification_id = get_option( 'td_last_notification_id', 0 );
if ( isset( $notification['notification_id'] ) && $last_notification_id < $notification['notification_id'] ) {
update_option( 'td_last_notification_id', $notification['notification_id'] );
}
$wpdb->insert( $table_name, $notification );
}
private function verify_access( $have_license, $dont_have_license ) {
if ( ! is_array( $have_license ) && ! is_array( $dont_have_license ) ) {
return true;
}
$user_products = TD_Ian_Helper::get_user_product_ids();
if ( ! empty( $have_license ) && ! empty( $dont_have_license ) ) {
$has_any_license = ! empty( array_intersect( $user_products, $have_license ) );
$has_no_restricted_license = empty( array_intersect( $user_products, $dont_have_license ) );
return $has_any_license || $has_no_restricted_license;
}
if ( ! empty( $have_license ) ) {
return ! empty( array_intersect( $user_products, $have_license ) );
}
if ( ! empty( $dont_have_license ) ) {
return ! empty( array_intersect( $user_products, $dont_have_license ) );
}
return true;
}
// Check if the notification is already expired or not
private function is_expired( $end ) {
if ( ! $end || empty( $end ) ) {
return false;
}
$current_time = strtotime( current_time( 'Y-m-d H:i:s' ) );
return $current_time > strtotime( $end );
}
// Retrieve data from the server using the REST API
private function get( $args = [] ) {
$request_params = [
'body' => $args,
'headers' => [
'Content-Type' => 'application/json',
],
'timeout' => 30,
'sslverify' => false,
];
$notifications = wp_remote_get( $this->_url, $request_params );
if ( is_wp_error( $notifications ) ) {
return [];
}
$response_code = wp_remote_retrieve_response_code( $notifications );
if ( $response_code != 200 ) {
return [];
}
if ( is_wp_error( $notifications ) ) {
return false;
}
$notifications = json_decode( wp_remote_retrieve_body( $notifications ), true );
if ( ! is_array( $notifications ) ) {
return [];
}
return $notifications;
}
// Check if the notification already exists in the database
private function is_exist( $notification_id ) {
global $wpdb;
$table_name = $wpdb->prefix . 'td_app_notifications';
$notification = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_name WHERE notification_id = %d",
$notification_id ) );
if ( $notification ) {
return true;
}
return false;
}
// Check if the transients already exist
private function is_transients_exist() {
$transient_name = 'td_app_notifications_transients';
$transient = get_transient( $transient_name );
if ( $transient ) {
return true;
}
return false;
}
// Store the transients
private function store_transients( $notifications ) {
if ( ! $notifications || ! is_array( $notifications ) || is_wp_error( $notifications ) ) {
return;
}
set_transient( 'td_app_notifications_transients', $notifications, time() + DAY_IN_SECONDS );
}
public function update_transients() {
$this->store_transients( $this->get_notification( true ) );
}
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
return array(
'Notifications' => __( 'Notifications', 'thrive-dash' ),
'no_new_title' => __( 'Great Scott! Where\'d they all go?', 'thrive-dash' ),
'no_new' => __( 'You have no new notifications.', 'thrive-dash' ),
'no_dismissed' => __( 'You have no dismissed notifications.', 'thrive-dash' ),
'see_dismissed' => __( 'See Dismissed Notifications', 'thrive-dash' ),
'see_new' => __( 'See New Notifications', 'thrive-dash' ),
);

View File

@@ -0,0 +1,29 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
$product_key = TD_Ian_Helper::get_product_name_by_page( $this->current_page );
$dom_class = TD_Ian_Helper::class_by_product( $product_key );
$class = "td-app-notification-counter";
if ( $this->current_page === 'tcm_admin_dashboard' ) {
$class .= " tvd-right";
}
?>
<li class="<?php echo $class?>">
<span id="tvd-notifications-btn ian-notification" class="tvd-notifications-btn ian-icon-<?php echo $dom_class?>">
<span class="td-app-notification-counter-holder" style="display: none;"></span>
<svg width="20" height="20" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1" y="0.793457" width="20" height="20" rx="1" stroke="#12394A" stroke-width="1.5"/>
<path d="M21 15.0793H14.5135C14.5135 15.0793 14.5135 17.9365 11.2703 17.9365C8.02703 17.9365 8.02703 15.0793 8.02703 15.0793H1" stroke="#12394A" stroke-width="1.5"/>
</svg>
</span>
</li>