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,67 @@
<?php
/**
* Advanced Ads Pro
*
* @package AdvancedAds
* @author Advanced Ads <support@wpadvancedads.com>
* @license GPL-2.0+
* @link https://wpadvancedads.com
* @copyright since 2013 Advanced Ads
*
* @wordpress-plugin
* Plugin Name: Advanced Ads Pro
* Version: 3.0.4
* Description: Advanced features to boost your ad revenue.
* Plugin URI: https://wpadvancedads.com/add-ons/advanced-ads-pro/
* Author: Advanced Ads
* Author URI: https://wpadvancedads.com
* Text Domain: advanced-ads-pro
* Domain Path: /languages
* License: GPL-2.0+
* License URI: https://www.gnu.org/licenses/gpl-2.0.txt
*
* @requires
* Requires at least: 5.7
* Requires PHP: 7.4
*/
// Early bail!!
if ( ! function_exists( 'add_filter' ) ) {
header( 'Status: 403 Forbidden' );
header( 'HTTP/1.1 403 Forbidden' );
exit();
}
if ( defined( 'AAP_FILE' ) ) {
return;
}
define( 'AAP_FILE', __FILE__ );
define( 'AAP_VERSION', '3.0.4' );
// Load the autoloader.
require_once __DIR__ . '/includes/class-autoloader.php';
\AdvancedAds\Pro\Autoloader::get()->initialize();
/**
* Install the plugin.
*
* @since 2.26.0
*/
( new \AdvancedAds\Pro\Installation\Install() )->initialize();
if ( ! function_exists( 'wp_advads_pro' ) ) {
/**
* Returns the main instance of the plugin.
*
* @since 2.26.0
*
* @return \AdvancedAds\Pro\Plugin
*/
function wp_advads_pro() {
return \AdvancedAds\Pro\Plugin::get();
}
}
\AdvancedAds\Pro\Bootstrap::get()->start();

View File

@@ -0,0 +1,262 @@
/**
PLACEMENT TESTS PAGE
*/
/* stylelint-disable */
.advads-placement-tests-table {
min-width: 80%;
background: #fff;
border: 1px solid #c3c4c7;
border-collapse: collapse;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
}
.advads-placement-tests-table tbody tr {
border-top: 1px solid #f1f1f1;
}
.advads-placement-tests-table th,
.advads-placement-tests-table td {
margin: 0;
padding: 10px;
text-align: left;
vertical-align: top;
}
.advads-placement-tests-table tbody tr td:first-child {
width: 70px;
}
.advads-placement-tests-table tbody tr td:nth-child(2) {
width: 225px;
}
.advads-placement-tests-table tbody tr td:last-child {
width: 70px;
}
.advads-placements-table-test-column {
width: 150px;
}
.advads-placements-table-options
.advads-option-placement-custom-position
fieldset {
margin-left: 2em;
margin-bottom: 1em;
}
.advads-placements-table-options
.advads-option-placement-custom-position
fieldset
legend {
margin-left: -2em;
font-weight: bold;
}
/**
PLACEMENT VISITOR AND DISPLAY CONDITIONS
*/
.advads-placements-table .advads-conditions-table tbody tr td {
width: auto;
}
/**
AD GROUPD PAGE
*/
.advads-option-group-pro-grid-size > div {
line-height: 28px;
}
/**
AD EDIT PAGE
*/
#advads-pro-weekdays {
margin-top: 5px;
font-size: 12px;
}
#advads-pro-weekdays option {
padding-bottom: 1px;
}
#advads-custom-code-wrap {
float: none;
overflow: auto;
}
#advads-custom-code-textarea {
width: 100%;
height: 10em;
}
#modal-category-hierarchy .preloader {
text-align: center;
}
#modal-category-hierarchy .preloader img {
width: 28px;
height: 28px;
}
#modal-category-hierarchy .level-header {
display: inline-block;
margin: 0.5em 0.5em 0 0;
}
#modal-category-hierarchy .level-header .dashicons {
color: #7a7a79;
cursor: pointer;
}
#modal-category-hierarchy .level-header .dashicons.not-parent {
visibility: hidden;
cursor: default;
}
#modal-category-hierarchy .category-level .category-level {
margin-left: 2em;
padding-left: 1.5em;
border-left: 1px dashed #9b9b9b;
}
.rtl #modal-category-hierarchy .category-level .category-level {
margin-right: 2em;
margin-left: 0;
padding-right: 1.5em;
padding-left: 0;
border-right: 1px dashed #9b9b9b;
border-left: none;
}
#modal-category-hierarchy .category-level .category-level.folded {
height: 0;
overflow: hidden;
}
#modal-category-hierarchy .advads-category.advads-button[data-status="active"] {
border-color: #0074a2 !important;
background: #2ea2cc !important;
color: #fff !important;
box-shadow: none;
border-width: 1px;
}
#modal-category-hierarchy .advads-category.advads-button[data-status="mixed"] {
background: #fff;
}
#modal-category-hierarchy .advads-category.advads-button span:after {
content: "";
border: none;
width: 0;
}
#modal-category-hierarchy .advads-category.advads-button span:before {
font-family: "dashicons";
padding: 0;
width: 28px;
display: inline-block;
border-right: 1px solid #ccc;
margin-right: 1em;
height: 28px;
line-height: 30px;
text-align: center;
color: #ccc;
margin-left: -1em;
}
.rtl #modal-category-hierarchy .advads-category.advads-button span:before {
border-left: 1px solid #ccc;
border-right: none;
margin-left: 1em;
margin-right: 0;
}
#modal-category-hierarchy .advads-category.advads-button span:before {
content: "\f159";
}
#modal-category-hierarchy .advads-category.advads-button:hover span:before,
#modal-category-hierarchy
.advads-category.advads-button[data-status="mixed"]:hover
span:before {
content: "\f12a";
}
#modal-category-hierarchy
.advads-category.advads-button[data-status="active"]
span:before {
content: "\f12a";
color: white;
border-color: #0074a2;
}
#modal-category-hierarchy
.advads-category.advads-button[data-status="mixed"]
span:before {
content: "\f14f";
color: #2ea2cc;
border-color: #0074a2;
}
#modal-category-hierarchy
.advads-category.advads-button[data-status="active"]:hover
span:before {
content: "\f159";
}
/**
MODULE ACTIVATION
*/
.advads-sub-settings {
display: none;
}
input.advads-has-sub-settings:checked ~ .advads-sub-settings {
display: block;
}
#advads-settings-hide-by-post-type {
-webkit-columns: 200px 5;
-moz-columns: 200px 5;
columns: 200px 5;
column-gap: 10px;
}
#advads-settings-hide-by-post-type label {
display: block;
}
.advads-color-green {
color: green;
}
.advads-color-red {
color: red;
}
.advads-settings-margin {
margin: 1.33em 0;
}
.d-block {
display: block;
}
.advads-eadblocker-radio-button {
margin-bottom: 5px;
margin-right: 1.5em;
}
.width-100 {
width: 100%;
}
.advads-eab-overlay-notice {
display: none;
}
.advads-settings-checkbox-inline {
-webkit-columns: 200px 5;
-moz-columns: 200px 5;
columns: 200px 5;
column-gap: 10px;
}
.advads-settings-checkbox-inline label {
display: block;
margin-bottom: 9px;
}

View File

@@ -0,0 +1,4 @@
<?php
/**
* Silence is golden.
*/

View File

@@ -0,0 +1,4 @@
<?php
/**
* Silence is golden.
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -0,0 +1,88 @@
/**
* Advanced ads cache-busting admin bar.
*/
var advanced_ads_pro_admin_bar;
if ( ! advanced_ads_pro_admin_bar ) {
advanced_ads_pro_admin_bar = {
offset: 0,
adminBar: null,
bufferedAds: [],
/**
* Observe ads inserted using Cache Busting
*
* @param {object} event Cache Busting event.
*/
observe: function ( event ) {
var that = advanced_ads_pro_admin_bar;
if ( event.event === 'hasAd' && event.ad && event.ad.title && event.ad.cb_type !== 'off' ) {
if ( ! that.adminBar ) {
// No admin-bar yet: buffer.
that.bufferedAds.push( event.ad );
} else {
// Flush buffer if not empty.
if ( that.bufferedAds.length > 0 ) {
that.flush();
}
// Inject current ad.
that.inject( event.ad );
}
}
},
/**
* Flush earlier collected items.
*/
flush: function() {
var that = advanced_ads_pro_admin_bar, i = 0;
for (i = that.bufferedAds.length - 1; i >= 0; i-- ) {
that.inject( that.bufferedAds[i] );
}
that.bufferedAds = [];
},
/**
* Inject an ad.
*
* @param {object} ad An ad to inject.
*/
inject: function ( ad ) {
var that = advanced_ads_pro_admin_bar;
if ( that.offset === 0 ) {
// Remove 'No Ads found' `<li>`.
jQuery( '#wp-admin-bar-advads_no_ads_found' ).remove();
}
const count = ad.count ?? 1;
const countString = 1 === count ? '' : `(${count}) `;
const internalId = ad.id ? `${ad.type}_${ad.id}` : `${ad.type}_${ad.title}`;
const previousOccurrence = that.adminBar.find( `[data-aditem="${internalId}"]` );
if ( previousOccurrence.length ) {
const countSpan = previousOccurrence.find( '.occurence-count' );
countSpan.attr( 'data-count', parseInt( countSpan.attr( 'data-count' ), 10 ) + 1 ).text( `(${countSpan.data( 'count' )}) ` );
} else {
that.adminBar.append( '<li id="wp-admin-bar-advads_current_ad_' + that.offset + '"><div class="ab-item ab-empty-item" data-aditem="' + `${internalId}` + '">' +
`<span class="occurence-count" data-count="${count}">` + countString + '</span>' + ad.title + ' (' + ad.type + ')</div></li>' );
}
that.offset += 1;
}
};
}
if ( typeof advanced_ads_pro !== 'undefined' ) {
advanced_ads_pro.observers.add( advanced_ads_pro_admin_bar.observe );
}
( window.advanced_ads_ready || jQuery( document ).ready ).call( null, function() {
advanced_ads_pro_admin_bar.adminBar = jQuery( '#wp-admin-bar-advads_current_ads-default' );
advanced_ads_pro_admin_bar.flush();
if ( window.advads_admin_bar_items ) {
// Append items that do not use cache-busting.
window.advads_admin_bar_items.forEach( advanced_ads_pro_admin_bar.inject )
}
} );

View File

@@ -0,0 +1,550 @@
;(function($){
// CFP cookies prefix
var cname = 'advads_procfp';
var cname_vc = 'advanced_ads_ad_clicks'; // visitor conditions
// CFP cookies parameter ( domain/path )
var PATH = null;
var DOMAIN = null;
/**
* wrapper for JSON.parse
*/
function jsonDecode( str ) {
try {
var res = JSON.parse( str );
return res;
} catch( Ex ) {
return null;
}
}
// Max ad click conditions/ passive Cache busting.
$( document ).on( 'advads-passive-cb-conditions', function( e, cbs ) {
cbs.conditions['ad_clicks'] = 'check_ad_clicks';
cbs['check_ad_clicks'] = function ( options, ad ) {
if ( advads.cookie_exists( cname_vc + '_' + ad.id ) ) {
var C_vc = advads.get_cookie( cname_vc + '_' + ad.id );
C_vc = jsonDecode( C_vc );
}
if ( C_vc ) {
var now = parseInt( new Date().getTime() / 1000 );
for ( var i in C_vc ) {
if ( '_' + options.expiration == i ) {
// if still valid counter and click limit reached
if ( C_vc[i]['ttl'] >= now && C_vc[i]['count'] >= parseInt( options.limit ) ) {
return false;
}
}
}
}
return true;
};
});
var cfpTracker = function() {
this.$elements = {};
this.currentIFrame = false;
this.focusLost = false;
this.wrappers = [
'.google-auto-placed'
];
this.attributes = {
'data-anchor-status': 'displayed',
'data-vignette-loaded': 'true'
};
this.lastClick = 0;
this.init();
}
cfpTracker.prototype = {
constructor: cfpTracker,
init: function () {
const that = this;
let touchmoved;
// increment counter on click on link
$( document ).on( 'click', 'a[data-cfpa]', function () {
that.onClick( parseInt( $( this ).attr( 'data-cfpa' ) ) );
} );
// Increment counter on blur only if an iframe has recently been hovered.
$( window ).on( 'blur', function ( e ) {
// Use timeout of 0 ms as a workaround to make sure that the active element has changed correctly.
setTimeout( function () {
if ( ! that.currentIFrame ) {
// Loop parent nodes from the target to the delegation node to recognise iframe clicks.
for ( let target = document.activeElement; target && target !== this && target !== document; target = target.parentNode ) {
that.currentIFrame = that.checkWrappers( target );
if ( that.currentIFrame ) {
break;
}
}
}
if ( that.currentIFrame ) {
that.onClick( that.currentIFrame );
that.focusLost = true;
top.focus();
}
}, 0 );
} );
// mouse passes over an ad that has not yet been initialized (adsense and other distant ads - OR just an ad that contains no link nor iframes)
$( document ).on( 'mouseenter', 'div[data-cfpa]', function () {
var id = parseInt( $( this ).attr( 'data-cfpa' ) );
that.addElement( id );
} );
// Detect swipe and click on mobile devices.
document.addEventListener( 'touchmove', function () {
touchmoved = true;
}, false );
document.addEventListener( 'touchstart', function () {
touchmoved = false;
}, false );
// Detect desktop and mobile clicks.
['click', 'touchend'].forEach(
function ( event ) {
document.addEventListener( event, function ( e ) {
// Prevent swipes and simultaneous clicks on wrapper and element as well as fast clicks in a row
if ( touchmoved || ( that.getTimestamp() - that.lastClick ) < 1 ) {
return;
}
let adId = null;
// Loop parent nodes from the target to the delegation node.
for ( let target = e.target; target && target !== this && target !== document; target = target.parentNode ) {
// Loop all predefined wrappers to detect clicks on Google Auto Ads iframes.
adId = that.checkWrappers( target );
if ( adId ) {
that.onClick( adId );
break;
}
// Check if clicked element is a cfp wrapper.
if ( target.hasAttribute( 'data-cfpa' ) && target.hasAttribute( 'data-cfptl' ) ) {
adId = parseInt( target.getAttribute( 'data-cfpa' ), 10 );
that.onClick( adId );
break;
}
}
} );
}
);
},
/**
* Create current timestamp
*
* @return {number}
*/
getTimestamp: function () {
return Math.floor( Date.now() / 1000 );
},
/**
* Check if target element is a predefined wrapper or has Google Auto Ads attributes.
*
* @param target
* @returns {string|null}
*/
checkWrappers: function ( target ) {
for ( let i = 0, wrappersLength = this.wrappers.length, selector = null; i < wrappersLength; i ++ ) {
selector = this.wrappers[i];
if ( target.matches && target.matches( selector ) ) {
return ( selector === '.google-auto-placed' ) ? 'google-auto-placed' : null;
}
}
for ( const [key, value] of Object.entries( this.attributes ) ) {
if ( target.hasAttribute( key ) && target.getAttribute( key ) === value ) {
return 'google-auto-placed';
}
}
return null;
},
addElement: function( $el ) {
if ( false === $el instanceof jQuery ) {
// Select the first ad since there may be multiple copies of the same ad on the page.
$el = $( 'div[data-cfpa="' + $el + '"]' ).first();
}
var hasIframe = $el.find( 'iframe' ).length ? true : false;
if ( !hasIframe ) {
if ( !$el.find( 'a' ).length ) {
// no an anchor and no iframe -- likely and ad that is not yet loaded (adsense or other ad network)
return;
}
}
var adID = parseInt( $el.attr( 'data-cfpa' ) );
this.$elements[adID] = $el;
// remove attribute from the wrapper
$el.removeAttr( 'data-cfpa' );
// And then move it to the first anchor or iframe found
if ( hasIframe ) {
$el.find( 'iframe' ).first().attr({
'data-cfpa': adID,
})
if ( $el.attr( 'data-cfph' ) ) {
$el.find( 'iframe' ).first().attr({
'data-cfph': $el.attr( 'data-cfph' ),
})
}
} else {
$el.find( 'a' ).not( '.advads-edit-button' ).first().attr({
'data-cfpa': adID,
})
if ( $el.attr( 'data-cfph' ) ) {
$el.find( 'a' ).not( '.advads-edit-button' ).first().attr({
'data-cfph': $el.attr( 'data-cfph' ),
})
}
}
// remove Hours attribute from the wrapper
$el.removeAttr( 'data-cfph' );
// update TTL field for all outdated counter (visitor conditions)
if ( advads.cookie_exists( cname_vc + '_' + adID ) ) {
var C_vc = advads.get_cookie( cname_vc + '_' + adID );
C_vc = jsonDecode( C_vc );
if ( C_vc ) {
var now = parseInt( new Date().getTime() / 1000 ), cookie_modified = false;
for ( var i in C_vc ) {
if ( !C_vc.hasOwnProperty( i ) ) continue;
if ( 'exp' == i ) continue;
if ( C_vc[i]['ttl'] < now ) {
var period = parseFloat( i.substr( 1 ) );
var newTTL = C_vc[i]['ttl'];
while ( newTTL < now ) {
newTTL += period * 60 * 60;
}
C_vc[i]['ttl'] = newTTL;
C_vc[i]['count'] = 0;
cookie_modified = true;
}
}
if ( cookie_modified ) {
var expTime = new Date( C_vc['exp'] );
advads.set_cookie_sec( cname_vc + '_' + adID, JSON.stringify( C_vc, 'false', false ), parseInt( expTime.getTime() / 1000 ), PATH, DOMAIN );
}
}
}
},
/**
* Ban the visitor
*/
_banVisitor: function() {
var now = new Date();
var d = new Date();
d.setTime( d.getTime() + ( advadsCfpInfo.cfpBan * 24 * 60 * 60 * 1000 ) );
var ban = ( d.getTime() - now.getTime() ) / 1000;
advads.set_cookie_sec( 'advads_pro_cfp_ban', 1, ban, PATH, DOMAIN );
// Select ad wrappers and delete them.
document.querySelectorAll( '[data-cfpw]:not([data-cfp-exclude])' ).forEach( function( el ) {
el.remove();
} );
this.removeEmptyWrappers();
// Select Google AdSense Auto Ads and delete them.
this.wrappers.forEach( function ( wrapper ) {
jQuery( wrapper ).remove();
} );
for ( const [key, value] of Object.entries( this.attributes ) ) {
jQuery( '[' + key + '="' + value + '"]' ).remove();
}
},
/**
* Remove top-level wrappers that do not contain ads.
*/
removeEmptyWrappers: function() {
// Select top-level wrappers that are not ads.
document.querySelectorAll( '[data-cfptl]:not([data-cfpw])' ).forEach( function( el ) {
// If there are no nested ads, remove the top-level wrapper.
if ( ! el.querySelectorAll( '[data-cfpw]' ).length ) {
el.remove();
};
} );
},
/**
* Click handler.
*
* @param {string} ID Ad id.
*/
onClick: function( ID ){
var that = this,
// Module wide cookie
cookieVisitor = false,
// Visitor condition cookie.
cookieVisitorCondition = false;
this.lastClick = this.getTimestamp();
if ( 'google-auto-placed' !== ID && $( '[data-cfpa="' + ID + '"]' ).attr( 'data-cfph' ) ) {
// if there are some visitor conditions, use the vc cookie
if ( advads.cookie_exists( cname_vc + '_' + ID ) ) {
cookieVisitorCondition = jsonDecode( advads.get_cookie( cname_vc + '_' + ID ) );
}
const clickLimits = jsonDecode( $( '[data-cfpa="' + ID + '"]' ).attr( 'data-cfph' ) );
if ( cookieVisitorCondition ) {
// Cookie already exists, increment each counter (keep expiration time)
const nowInSeconds = parseInt( new Date().getTime() / 1000, 10 );
const adWrapper = document.querySelectorAll( '[data-cfpw="' + ID + '"]' );
for ( var h in cookieVisitorCondition ) {
if ( ! cookieVisitorCondition.hasOwnProperty( h ) ) {
continue;
}
if ( h === 'exp') {
// Ignore the key that contains expiration date.
continue;
}
cookieVisitorCondition[h]['count'] = parseInt( cookieVisitorCondition[h]['count'], 10 ) + 1;
if ( cookieVisitorCondition[h]['ttl'] >= nowInSeconds && cookieVisitorCondition[h]['count'] >= parseInt( clickLimits[h], 10 ) ) {
adWrapper.forEach( function( node ) {
node.remove();
} );
// If the ad was wrapper with a top-level wrapper, try to remove it.
that.removeEmptyWrappers();
}
}
var now = new Date();
var expiry = new Date( cookieVisitorCondition.exp );
var expirySecs = parseInt( ( expiry.getTime() - now.getTime() ) / 1000 );
advads.set_cookie_sec( cname_vc + '_' + ID, JSON.stringify( cookieVisitorCondition, 'false', false ), expirySecs, PATH, DOMAIN );
} else {
// create a new cookie
var cval = {}, maxHValue = 0;
var d = new Date();
var now = new Date();
for ( var h in clickLimits ) {
h = h.substring( 1 );
if ( parseFloat( h ) > maxHValue ) {
maxHValue = parseFloat( h );
}
cval['_' + h] = {
count: 1,
ttl: parseInt( ( ( now.getTime() / 1000 ) + ( parseFloat( h ) * 3600 ) ), 10 ),
};
}
// use the longest hour value for the expiry time
d.setTime( d.getTime() + ( maxHValue * 60 * 60 * 1000 ) );
var expires = "expires="+ d.toUTCString();
var expirySecs = parseInt( ( d.getTime() - now.getTime() ) / 1000 );
cval['exp'] = expires;
advads.set_cookie_sec( cname_vc + '_' + ID, JSON.stringify( cval, 'false', false ), expirySecs, PATH, DOMAIN );
if ( 1 === clickLimits[Object.keys( clickLimits )[0]] ) {
document.querySelectorAll( '[data-cfpw="' + ID + '"]' ).forEach( function ( node ) {
node.remove();
} );
this.removeEmptyWrappers();
}
}
}
if ( '1' !== advadsCfpInfo.cfpEnabled ) {
return;
}
// use the module wide CFP cookie
if ( advads.cookie_exists( cname + '_' + ID ) ) {
cookieVisitor = jsonDecode( advads.get_cookie( cname + '_' + ID ) );
}
if ( cookieVisitor ) {
// Cookie already exists, increment the counter (keep expiration time)
cookieVisitor.count = parseInt( cookieVisitor.count, 10 ) + 1;
var now = new Date();
var expiry = new Date( cookieVisitor.exp );
var expirySecs = ( expiry.getTime() - now.getTime() ) / 1000;
advads.set_cookie_sec( cname + '_' + ID, JSON.stringify( cookieVisitor, 'false', false ), expirySecs, PATH, DOMAIN );
if ( parseInt( advadsCfpInfo.cfpClickLimit,10 ) <= cookieVisitor.count ) {
// CFP module enabled - ban this visitor
that._banVisitor();
}
} else {
// create a new cookie
var d = new Date();
var now = new Date();
d.setTime( d.getTime() + ( parseInt( advadsCfpInfo.cfpExpHours, 10 ) * 60 * 60 * 1000 ) );
var expires = "expires="+ d.toUTCString();
var expirySecs = ( d.getTime() - now.getTime() ) / 1000;
advads.set_cookie_sec( cname + '_' + ID, '{"count":1,"exp":"' + expires + '"}', expirySecs, PATH, DOMAIN );
if ( parseInt( advadsCfpInfo.cfpClickLimit, 10 ) === 1 ) {
// CFP module enabled - ban this visitor
that._banVisitor();
}
}
},
}
$( function(){
/**
* Max ad click ( visitor conditions )
*/
window.advadsProCfp = new cfpTracker();
// IFRAME click tracking
$( document ).on( 'mouseenter', 'iframe[data-cfpa]', function(e){
var ID = parseInt( $( this ).attr( 'data-cfpa' ) );
advadsProCfp.currentIFrame = ID;
} ).on( 'mouseenter', '.google-auto-placed', function(e){
// Use the same ID for all Google AdSense Auto Ads.
advadsProCfp.currentIFrame = 'google-auto-placed';
} ).on( 'mouseleave mouseout', '[data-cfpa], .google-auto-placed', function(){
advadsProCfp.currentIFrame = false;
if ( advadsProCfp.focusLost ) {
advadsProCfp.focusLost = false;
$( window ).trigger( 'focus' );
}
} );
// construct all elements already present in the queue
for( var i in advadsCfpQueue ) {
if ( advadsCfpQueue.hasOwnProperty( i ) ) {
advadsProCfp.addElement( advadsCfpQueue[i] );
}
}
advadsCfpQueue = [];
/**
* Click fraud protection module.
*/
if ( 'undefined' == typeof window.advadsCfpInfo.cfpPath ) return;
// get the path/domain parameter to use in cookies
if ( '' != advadsCfpInfo.cfpPath ) {
PATH = advadsCfpInfo.cfpPath;
}
if ( '' != advadsCfpInfo.cfpDomain ) {
DOMAIN = advadsCfpInfo.cfpDomain;
}
} );
})(window.jQuery);
/**
* Reload on resize.
*/
var advanced_ads_resizetimeout = 1000; // time to wait until resized window width is saved, in millisec
var advanced_ads_cookieexpires = 30; // days until the cookie expires
var advanced_ads_browser_width = advanced_ads_get_browser_width();
// Save browserWidth in a cookie if not set or not equal to current saved width.
if ( window.advads !== undefined ) {
var cookieValue = advads.get_cookie( 'advanced_ads_visitor' );
var info = cookieValue ? JSON.parse( cookieValue ) : {};
if ( ! info.browser_width || info.browser_width !== advanced_ads_browser_width ) {
advanced_ads_save_width( advanced_ads_browser_width );
}
}
// Save new browser width, when window resizes.
if ( window.addEventListener ) { // most non-IE browsers and IE9
window.addEventListener( 'resize', advanced_ads_resize_window, false );
} else if ( window.attachEvent ) { // Internet Explorer 5 or above
window.attachEvent( 'onresize', advanced_ads_resize_window );
}
function advanced_ads_resize_window() {
advads_resize_delay( function () {
var previous_width = advanced_ads_browser_width;
advanced_ads_browser_width = advanced_ads_get_browser_width();
if ( previous_width === advanced_ads_browser_width ) {
// Return if the viewport has not actually changed
// Scroll events were triggering this on iOS due to the position of the address bar.
return;
}
advanced_ads_save_width( advanced_ads_browser_width );
var advanced_ads_responsive = window.advanced_ads_responsive || {};
if ( window.jQuery && parseInt( advanced_ads_responsive.reload_on_resize, 10 ) ) {
jQuery( document ).triggerHandler( 'advanced-ads-resize-window' );
}
}, advanced_ads_resizetimeout );
}
// Save width in cookie.
function advanced_ads_save_width( width ) {
if ( window.advads === undefined ) {
return;
}
var cookieValue = advads.get_cookie( 'advanced_ads_visitor' );
var info = cookieValue ? JSON.parse( cookieValue ) : {};
info.browser_width = width;
advads.set_cookie( 'advanced_ads_visitor', JSON.stringify( info ), advanced_ads_cookieexpires, advanced_ads_cookies.cookie_path, advanced_ads_cookies.cookie_domain );
}
// Create a listener calling only once after resize.
// http://stackoverflow.com/questions/2854407/javascript-jquery-window-resize-how-to-fire-after-the-resize-is-completed
var advads_resize_delay = ( function () {
var timer = 0;
return function ( callback, ms ) {
clearTimeout( timer );
timer = setTimeout( callback, ms );
};
} )();
/**
* Get the width of the browser.
*/
function advanced_ads_get_browser_width() {
if ( window.jQuery ) {
return jQuery( window ).width();
} else {
var browserWidth = 0;
if ( typeof ( window.innerWidth ) == 'number' ) {
// Non-IE
browserWidth = window.innerWidth;
} else if ( document.documentElement && document.documentElement.clientWidth ) {
// IE 6+ in 'standards compliant mode'
browserWidth = document.documentElement.clientWidth;
} else if ( document.body && document.body.clientWidth ) {
// IE 4 compatible
browserWidth = document.body.clientWidth;
}
return browserWidth;
}
}

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,4 @@
<?php
/**
* Silence is golden.
*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
/**
* If there is no cache-busting script add this to decrypt TCF privacy encrypted ads.
*/
if ( ! advanced_ads_pro ) {
var advanced_ads_pro = {
observers: jQuery.Callbacks()
};
}
document.addEventListener( 'advanced_ads_privacy', function ( event ) {
if (
event.detail.previousState !== 'unknown'
&& ! ( event.detail.previousState === 'rejected' && event.detail.state === 'accepted' )
) {
return;
}
if ( event.detail.state === 'accepted' || event.detail.state === 'not_needed' ) {
var encodedAd = 'script[type="text/plain"][data-tcf="waiting-for-consent"]';
var decoded_ads = {},
decode_ad = function ( node ) {
if ( typeof node.dataset.noTrack === 'undefined' || node.dataset.noTrack !== 'impressions' ) {
if ( ! decoded_ads.hasOwnProperty( node.dataset.bid ) ) {
decoded_ads[node.dataset.bid] = [];
}
decoded_ads[node.dataset.bid].push( parseInt( node.dataset.id, 10 ) );
}
advads.privacy.decode_ad( node );
};
// Find all scripts and decode them.
document.querySelectorAll( encodedAd ).forEach( function ( node ) {
decode_ad( node );
} );
if ( Object.keys( decoded_ads ).length ) {
advanced_ads_pro.observers.fire( {event: 'advanced_ads_decode_inserted_ads', ad_ids: decoded_ads} );
}
}
} );

View File

@@ -0,0 +1 @@
if(!advanced_ads_pro)var advanced_ads_pro={observers:jQuery.Callbacks()};document.addEventListener("advanced_ads_privacy",(function(e){if(("unknown"===e.detail.previousState||"rejected"===e.detail.previousState&&"accepted"===e.detail.state)&&("accepted"===e.detail.state||"not_needed"===e.detail.state)){var a={};document.querySelectorAll('script[type="text/plain"][data-tcf="waiting-for-consent"]').forEach((function(e){!function(e){void 0!==e.dataset.noTrack&&"impressions"===e.dataset.noTrack||(a.hasOwnProperty(e.dataset.bid)||(a[e.dataset.bid]=[]),a[e.dataset.bid].push(parseInt(e.dataset.id,10))),advads.privacy.decode_ad(e)}(e)})),Object.keys(a).length&&advanced_ads_pro.observers.fire({event:"advanced_ads_decode_inserted_ads",ad_ids:a})}}));

View File

@@ -0,0 +1,4 @@
<?php
/**
* Silence is golden.
*/

View File

@@ -0,0 +1,259 @@
export default () => {
'use strict';
const fallbacks = {},
ajaxResponses = {};
let Observer = null,
observedAds = 0;
/**
* Extract AdSense fallback item data from the AdSense ad wrapper
*
* @param {Node} node the ad or group wrapper.
* @return {{}} fallback item data
*/
const extractFallBackData = (node) => {
const result = {};
node.classList.forEach((className) => {
if (className.startsWith('gas_fallback-')) {
const exploded = className.split('-');
result.ad = exploded[1];
result.fallback = exploded[2];
}
});
return result;
};
/**
* Bind the mutation observer to an AdSense ad tag
*
* @param {Node} ins the <ins/> tag of the AdSense ad which hold the "data-ad-status" attribute.
*/
const observeAd = (ins) => {
if (null === Observer) {
Observer = new window.MutationObserver((records) => {
for (const record of records) {
if (
'unfilled' !==
record.target.getAttribute('data-ad-status')
) {
return;
}
document.dispatchEvent(
new CustomEvent('advanced_ads_pro_adsense_unfilled', {
detail: { ad: record.target },
})
);
}
});
}
// Only observe the `data-ad-status` attribute, and only one the <ins/> tag.
Observer.observe(ins, { attributeFilter: ['data-ad-status'] });
observedAds++;
};
/**
* Inject the fallback ad or group using passive CB
*
* @param {Object} fallBackData fallback item data extracted from the AdSense ad wrapper.
* @param {Object} placement CB placement data (can be in passive or AJAX format).
*/
const passiveInject = (fallBackData, placement) => {
const fallBackItem = fallBackData.fallback.split('_');
// The fallback item is an ad.
if ('ad' === fallBackItem[0]) {
const adInfo = getAdInfo(placement, fallBackItem[1]);
if (!adInfo) {
return;
}
const passiveAd = new window.Advads_passive_cb_Ad(
adInfo,
fallBackData.cbId
);
// Remove the current AdSense ad wrapper then inject the fallback ad's output.
fallBackData.wrapper.remove();
passiveAd.output({
track: true,
inject: true,
do_has_ad: true,
});
}
// The fallback item is a group.
if ('group' === fallBackItem[0]) {
placement.id = parseInt(fallBackItem[1], 10);
placement.type = 'group';
placement.group_info = placement.adsense_fallback_group_info;
const passiveGroup = new window.Advads_passive_cb_Group(
placement,
fallBackData.cbId
);
// Remove the current AdSense ad wrapper then inject the fallback group's output.
fallBackData.wrapper.remove();
passiveGroup.output();
}
};
/**
* Inject (using passive CB) the fallback into the AdSense ad's CB wrapper.
*
* Check into the passive placements list first as this is the most common case.
* Then check into the AJAX CB placement afterward.
* If no matching placement is found do not inject. The feature works only with CB placements.
*
* @param {Object} fallbackData fallback ad/group data for the current AdSense ad.
*/
const injectFallback = (fallbackData) => {
const placement =
getPassivePlacement(fallbackData.cbId) ||
getAjaxPlacement(fallbackData.cbId);
if (placement) {
passiveInject(fallbackData, placement);
adHealthEntry(placement);
}
};
/**
* Add an ad health entry about the unfilled AdSense slot.
*
* @param {Object} placement data about the placement containing the failed AdSense ad.
*/
const adHealthEntry = (placement) => {
if (
!advancedAds.adHealthNotice.enabled ||
!document.getElementById('wpadminbar')
) {
return;
}
const allFine = document.getElementById(
'wp-admin-bar-advanced_ads_ad_health_fine'
);
allFine?.remove();
const adHealth = document
.getElementById('wp-admin-bar-advanced_ads_ad_health')
.querySelector('ul');
const li = document.createElement('li');
li.role = 'group';
li.id = 'wp-admin-bar-advanced_ads_gads_fallback';
const div = document.createElement('div');
div.className = 'ab-item ab-empty-item';
div.role = 'menuitem';
div.textContent = window.advancedAds.adHealthNotice.pattern.replace(
'[ad_title]',
placement.ads[
parseInt(placement.placement_info.item.split('_')[1], 10)
].title
);
adHealth.appendChild(li);
li.appendChild(div);
};
/**
* Get data about a potential passive CB placement given the CB wrapper ID (HTML id attribute).
*
* @param {string} cbId CB wrapper ID (a.k.a. elementid or elementId).
* @return {Object|boolean} false if no matching placement found.
*/
const getPassivePlacement = (cbId) => {
return (
Object.values(window.advads_passive_placements).find(
(placement) => placement.elementid.indexOf(cbId) === 0
) || false
);
};
/**
* Get data about a potential AJAX CB placement given the CB wrapper ID (HTML id attribute).
*
* @param {string} cbId CB wrapper ID (a.k.a. elementid or elementId).
* @return {Object|boolean} false if no matching placement found.
*/
const getAjaxPlacement = (cbId) => {
return 'undefined' !== typeof ajaxResponses[cbId]
? ajaxResponses[cbId]
: false;
};
/**
* Get ad data for passive CB injection of the fallback — iterate (call it repeatedly) through all ads for a group
*
* @param {Object} placement placement data.
* @param {string} adId the ad ID.
* @return {Object|boolean} false if no matching ad found.
*/
const getAdInfo = (placement, adId) => {
return placement.ads[adId] || false;
};
/**
* Process fallback when Google signals an unfilled spot
*/
document.addEventListener('advanced_ads_pro_adsense_unfilled', (ev) => {
observedAds--;
if (0 === observedAds) {
// Cleanup when all observed ads are unfilled.
Observer.disconnect();
}
const fallbackData = extractFallBackData(
ev.detail.ad.closest('[class^="gas_fallback-"]')
);
if (!fallbacks[fallbackData.ad]) {
return;
}
injectFallback(fallbacks[fallbackData.ad]);
});
/**
* Collect AJAX CB placements data
*/
document.body.addEventListener('advads_ajax_cb_response', ({ detail }) => {
detail.response.forEach((placement) => {
ajaxResponses[placement.elementId] = placement;
});
});
// Initialization
window.advanced_ads_ready(() => {
if (!window.advanced_ads_pro) {
return;
}
/**
* Observe any newly injected AdSense Ad (injected via CB)
*/
window.advanced_ads_pro.postscribeObservers.add((event) => {
if ('postscribe_done' !== event.event) {
return;
}
if (-1 === event.ad.indexOf('gas_fallback-')) {
return;
}
const cbWrapper = event.ref[0],
itemWrapper = cbWrapper.querySelector(
'[class^="gas_fallback-"]'
);
const fallbackData = extractFallBackData(itemWrapper);
fallbacks[fallbackData.ad] = {
fallback: fallbackData.fallback,
wrapper: itemWrapper,
cbId: cbWrapper.id,
};
observeAd(itemWrapper.querySelector('ins'));
});
});
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,202 @@
/* eslint-disable */
/*
* Group refresh
*/
import jQuery from 'jquery';
import { PassivePlacement } from './passive-placement';
export const GroupRefresh = {
/**
* Wrapper ids of groups with refresh enabled.
*/
element_ids: {},
/**
* Equivalent passive cb data for groups using ajax refresh
*/
passiveRefresh: {},
/**
* Collect placement group and ad data on the very first impression of an ajax refresh group
*
* @param {Object} data all the data needed for a passive placement creation.
*/
collectPassiveRefreshData: (data) => {
GroupRefresh.passiveRefresh[data.cb_id] = data;
},
/**
* Empty a CB wrapper then create a passive placement using the same wrapper
*
* @param {string} cbid id of the CB wrapper.
*/
switchToPassive: (cbid) => {
const data = GroupRefresh.passiveRefresh[cbid];
setTimeout(
() => {
jQuery(`.${data.cb_id}`).empty();
GroupRefresh.launchRefresh(data);
},
parseInt(data.default_interval, 10)
);
},
/**
* Create and output a passive placement with a refreshed group
*
* @param {Object} data data needed for a passive placement creation.
*/
launchRefresh: (data) => {
new PassivePlacement(
{
id: data.placement_info.id,
type: data.type,
ads: data.ads,
placement_info: data.placement_info,
group_info: data.group_info,
group_wrap: data.group_wrap,
},
data.cb_id
).output();
},
/**
* Requests group with AJAX. Uses single request, if possible.
*
* @param {obj} query
* @param {int} interval
*/
add_query: (function advanced_ads_group_refresh_add_query(query, interval) {
var queries = [];
return function (query, interval) {
var elementid = query.elementid;
var call_at = new Date().getTime() + interval;
queries[call_at] = queries[call_at] || [];
queries[call_at].push(query);
setTimeout(function () {
var now = new Date().getTime();
var requests = [];
for (const call_time in queries) {
if (!queries.hasOwnProperty(call_time)) {
continue;
}
if (now > call_time - 1000) {
// Gather multiple requests into one request.
var queries_for_time = queries[call_time];
var queries_length = queries_for_time.length;
for (var i = 0; i < queries_length; i++) {
requests.push(queries_for_time[i]);
}
delete queries[call_time];
}
}
advanced_ads_pro.process_ajax_ads(requests);
}, interval);
};
})(),
/**
* Find a first floated div.
*
* @param {jQuery} $el Element id.
* @return string/false
*/
find_float: function ($el) {
var r = false;
$el.find('div').each(function (i, el) {
if (this.style.float === 'left' || this.style.float === 'right') {
r = this.style.float;
return false;
}
});
return r;
},
/**
* Prepare the wrapper for inserting new ad.
*
* @param {jQuery} $el Wrapper id.
* @param {string|bool} float The float setting of the ad.
* @param {bool} is_first_time True for the first ad injection.
*/
prepare_wrapper: function ($el, float, is_first_time) {
if (!is_first_time) {
this.maybe_increase_sizes($el);
$el.empty();
}
this.set_float($el, float);
},
/**
* Increase the width and height of the wrapper to fit an ad.
* The width and height can only be increased, not decreased to minimize content jumping.
*
* @param {jQuery} $el Element id.
*/
maybe_increase_sizes: function ($el) {
var float = $el.css('float');
if (['left', 'right'].indexOf(float) === -1) {
float = false;
}
var sizes = {};
if (float) {
// Check if we need to increase the width.
var prev_w = parseInt($el.css('min-width'), 10) || 0;
// Get measured width.
var now_w = $el.prop('scrollWidth') || 0;
if (now_w > prev_w) {
sizes['min-width'] = now_w;
}
}
// Check if we need to increase the height.
var prev_h = parseInt($el.css('min-height'), 10) || 0;
// Get measured height.
var now_h = $el.prop('scrollHeight') || 0;
if (now_h > prev_h) {
sizes['min-height'] = now_h;
}
if (sizes['min-height'] || sizes['min-width']) {
$el.css(sizes);
}
},
/**
* Get the 'float' attribute from the ad or the placement (nested floated divs) and assign it to '$el'.
*
* @param {jQuery} $el Wrapper id.
* @param {string|bool} float The float setting of the ad.
*/
set_float: function ($el, float) {
if (['left', 'right'].indexOf(float) === -1) {
float = false;
}
var prev_float = $el.data('prev_float') || false;
if (float !== prev_float) {
$el.data('prev_float', float);
if (float) {
// Allow to measure the size of the '$el' that contains floated divs using 'scrollWidth/Height'
// Allow texts to be floated around the '$el' that has 'min-height' set.
$el.css({ 'min-width': '', 'min-height': '', float: float });
} else {
$el.css({ 'min-width': '', 'min-height': '', float: '' });
}
}
},
};
export const GroupRefreshCompat = () => {
window.advanced_ads_group_refresh = GroupRefresh;
};

View File

@@ -0,0 +1,358 @@
/* eslint-disable */
import jQuery from 'jquery';
export class PassiveAd {
/**
* Constructor
*
* @param {Object} ad_info object which contains info about the ad.
* @param {string} elementid id of wrapper div.
*/
constructor(ad_info, elementid) {
if (
typeof ad_info !== 'object' ||
!advads_pro_utils.isset(ad_info.id) ||
!advads_pro_utils.isset(ad_info.title) ||
!advads_pro_utils.isset(ad_info.content)
) {
throw new SyntaxError('Can not create Advads_passive_cb_Ad obj');
}
this.id = ad_info.id;
this.title = ad_info.title;
this.content = ad_info.content ? ad_info.content : '';
this.type = ad_info.type;
this.expiry_date = parseInt(ad_info.expiry_date) || 0;
this.visitors = ad_info.visitors;
this.once_per_page = ad_info.once_per_page;
this.elementid = elementid ? elementid : null;
this.day_indexes = ad_info.day_indexes ? ad_info.day_indexes : null;
this.debugmode = ad_info.debugmode;
this.tracking_enabled =
ad_info.tracking_enabled === undefined ||
ad_info.tracking_enabled === true;
this.blog_id = ad_info.blog_id ? ad_info.blog_id : '';
this.privacy = ad_info.privacy ? ad_info.privacy : {};
this.position = ad_info.position ? ad_info.position : '';
// Let modules and add-ons add info to an ad.
document.dispatchEvent(
new CustomEvent('advanced-ads-passive-cb-ad-info', {
detail: {
ad: this,
adInfo: ad_info,
},
})
);
}
/**
* Returns the ad output
*
* @param {Object} options output options
* track - track if true, do not track if false
* inject - inject the ad if true, return the ad content if false
* do_has_ad - true if we need to call the hasAd function
*
* @return {string} the ad output if inject is `false`.
*/
output(options) {
options = options || {};
if (this.debugmode) {
var is_displayed = this.can_display({ ignore_debugmode: true })
? 'displayed'
: 'hidden';
var debug_message = jQuery(this.content)
.find('.advads-passive-cb-debug')
.data(is_displayed);
// inject debug info
this.content = this.content.replace(
'##advanced_ads_passive_cb_debug##',
debug_message
);
}
if (options.do_has_ad) {
advanced_ads_pro.hasAd(this.id, 'ad', this.title, 'passive');
}
if (options.track && this.tracking_enabled) {
if (!advanced_ads_pro.passive_ads[this.blog_id]) {
advanced_ads_pro.passive_ads[this.blog_id] = [];
}
advanced_ads_pro.passive_ads[this.blog_id].push(this.id);
}
advads_pro_utils.log(
'output passive ad',
this.id,
this.elementid,
this.content
);
if (!options.inject) {
return this.content;
}
advanced_ads_pro.inject(this.elementid, this.content);
}
/**
* Check if the ad can be displayed in frontend due to its own conditions
*
* @param {Object} check_options possibility to bypass the check when in debug mode.
* @returns {boolean} whether the ad can be displayed.
*/
can_display(check_options) {
check_options = check_options || {};
if (this.debugmode && !check_options.ignore_debugmode) {
return true;
}
if ('' === this.content.trim()) {
advads_pro_utils.log(
'passive ad id',
this.id,
'cannot be displayed: empty content'
);
return false;
}
if (!this.can_display_by_visitor()) {
advads_pro_utils.log(
'passive ad id',
this.id,
'cannot be displayed: by_visitor'
);
return false;
}
if (!this.can_display_by_expiry_date()) {
advads_pro_utils.log(
'passive ad id',
this.id,
'cannot be displayed: by_expiry_date'
);
return false;
}
if (!this.can_display_by_timeout()) {
advads_pro_utils.log(
'passive ad id',
this.id,
'cannot be displayed: by_timeout'
);
return false;
}
if (!this.can_display_by_display_limit()) {
advads_pro_utils.log(
'passive ad id',
this.id,
'cannot be displayed: by_display_limit'
);
return false;
}
if (!this.can_display_by_weekday()) {
advads_pro_utils.log(
'passive ad id',
this.id,
'cannot be displayed: by_weekday'
);
return false;
}
if (!this.can_display_by_cfp()) {
advads_pro_utils.log(
'passive ad id',
this.id,
'cannot be displayed: by_cfp'
);
return false;
}
if (!this.can_display_by_consent()) {
advads_pro_utils.log(
'passive ad id',
this.id,
'cannot be displayed: by_consent'
);
return false;
}
const canDisplay = { display: true };
// Let modules and add-ons do a `canDisplay` check.
document.dispatchEvent(
new CustomEvent('advanced-ads-passive-cb-can-display', {
detail: {
canDisplay,
checkOptions: check_options,
adInfo: this,
},
})
);
return canDisplay.display;
}
/**
* Check visitor conditions
*
* @return {boolean} true if the ad can be displayed in frontend based on visitor settings, false otherwise
*/
can_display_by_visitor() {
if (!Array.isArray(this.visitors) || this.visitors.length === 0) {
return true;
}
window.Advads_passive_cb_Conditions.init();
var pos = 0,
last_result = false,
_condition;
for (var i = 0; i < this.visitors.length; ++i) {
_condition = this.visitors[pos];
// ignore OR if last result was true
if (last_result && _condition.connector === 'or') {
pos++;
continue;
}
last_result = window.Advads_passive_cb_Conditions.frontend_check(
_condition,
this
);
if (!last_result) {
// return false only, if the next condition doesnt have an OR operator
pos++;
if (
!this.visitors[pos] ||
this.visitors[pos].connector !== 'or'
) {
return false;
}
} else {
pos++;
}
}
return true;
}
/**
* Check expiry date
*
* @return {boolean} true if not expired yet
*/
can_display_by_expiry_date() {
if (this.expiry_date <= 0) {
return true;
}
// check against current time (universal time)
return this.expiry_date > ~~(new Date().getTime() / 1000);
}
/**
* Check if ad can be displayed today
*
* @return {boolean} true if ad can be displayed
*/
can_display_by_weekday() {
if (!this.day_indexes) {
return true;
}
var date = new Date(),
offsetMinutes =
window.advanced_ads_pro_ajax_object.wp_timezone_offset / 60,
offsetHours =
offsetMinutes / 60 >= 0
? Math.floor(offsetMinutes / 60)
: Math.ceil(offsetMinutes / 60);
offsetMinutes = date.getUTCMinutes() + (offsetMinutes % 60);
if (offsetMinutes > 60) {
offsetHours++;
offsetMinutes %= 60;
}
date.setHours(date.getUTCHours() + offsetHours);
date.setMinutes(offsetMinutes);
return jQuery.inArray(date.getDay(), this.day_indexes) >= 0;
}
/**
* Check close and timeout feature implemented by Advads Layer
*
* @return {boolean} true if ad can be displayed in frontend based on expiry date, false otherwise
*/
can_display_by_timeout() {
//check if ad was closed with a cookie before (Advads layer plugin)
return !advads_pro_utils.isset(advads.get_cookie('timeout_' + this.id));
}
/**
* Check if the ad can be displayed based on display limit
*
* @return {boolean} true if limit is not reached, false otherwise
*/
can_display_by_display_limit() {
if (this.once_per_page) {
var adsLen = advanced_ads_pro.ads.length;
for (var i = 0; i < adsLen; i++) {
if (
advanced_ads_pro.ads[i].type === 'ad' &&
parseInt(advanced_ads_pro.ads[i].id, 10) === this.id
) {
return false;
}
}
}
return true;
}
/**
* Check if the user is banned (Click Fraud Protection module).
*
* @return {boolean} false if banned
*/
can_display_by_cfp() {
// Check if the global setting is ignored.
for (const visitor of this.visitors) {
if (
visitor['type'] === 'ad_clicks' &&
visitor['exclude-from-global']
) {
return true;
}
}
// Check if the user is banned
return !advads.get_cookie('advads_pro_cfp_ban');
}
/**
* Check if ad can be displayed based on user's consent.
*
* @return {boolean} true if allowed
*/
can_display_by_consent() {
// If consent is not needed for the ad.
if (
!advads.privacy ||
this.privacy.ignore ||
(this.type === 'adsense' &&
advads.privacy.is_adsense_npa_enabled()) ||
((this.type === 'image' || this.type === 'dummy') &&
!this.privacy.needs_consent)
) {
return true;
}
var state = advads.privacy.get_state();
return state === 'accepted' || state === 'not_needed';
}
}
export const PassiveAdCompat = () => {
window.Advads_passive_cb_Ad = PassiveAd;
};

View File

@@ -0,0 +1,504 @@
/* eslint-disable */
import jQuery from 'jquery';
export class PassiveGroup {
/**
* Constructor
*
* @param {object} item object which contains info about the group.
* @param {string} elementid cache busting wrapper id.
*/
constructor(item, elementid) {
if (
!advads_pro_utils.isset(item.group_info.id) ||
!advads_pro_utils.isset(item.group_info.type) ||
!advads_pro_utils.isset(item.group_info.weights) ||
!advads_pro_utils.isset(item.group_info.ordered_ad_ids) ||
!advads_pro_utils.isset(item.group_info.ad_count) ||
!advads_pro_utils.isset(item.ads)
) {
throw new SyntaxError('Can not create Advads_passive_cb_Group obj');
}
this.id = item.group_info.id;
this.name = item.group_info.name ? item.group_info.name : this.id;
this.type = item.group_info.type;
this.weights = item.group_info.weights;
this.ordered_ad_ids = item.group_info.ordered_ad_ids;
this.ad_count = item.group_info.ad_count;
this.elementid = elementid ? elementid : null;
this.slider_options = advads_pro_utils.isset(
item.group_info.slider_options
)
? item.group_info.slider_options
: false;
this.refresh_enabled = advads_pro_utils.isset(
item.group_info.refresh_enabled
);
if (advads_pro_utils.isset(item.group_info.refresh_interval_for_ads)) {
this.refresh_interval = item.group_info.refresh_interval_for_ads;
} else if (advads_pro_utils.isset(item.group_info.refresh_interval)) {
// Deprecated.
this.refresh_interval = item.group_info.refresh_interval;
} else {
this.refresh_interval = 2000;
}
this.placement =
item instanceof Advads_passive_cb_Placement ? item : false;
this.random = item.group_info.random;
this.ads = item.ads;
this.group_wrap = item.group_wrap;
this.is_empty = true;
}
/**
* Inject group output inside the cache busting wrapper
*/
output() {
var ad_for_adblocker =
this.placement && this.placement.get_ad_for_adblocker();
advanced_ads_pro.hasAd(this.id, 'group', this.name, 'passive');
if (!ad_for_adblocker && this.refresh_enabled) {
this.output_refresh();
return;
}
var ordered_ad_ids,
ads_displayed = 0,
output_buffer = [];
const group_for_ab =
this.placement && this.placement.get_group_for_adblocker();
if (group_for_ab) {
this.ads = group_for_ab.ads;
}
switch (this.type) {
case 'ordered':
case 'slider':
ordered_ad_ids = this.shuffle_ordered_ads(
this.ordered_ad_ids,
this.weights
);
break;
case 'grid':
ordered_ad_ids = this.random
? this.shuffle_ads()
: this.shuffle_ordered_ads(
this.ordered_ad_ids,
this.weights
);
break;
default:
ordered_ad_ids = this.shuffle_ads();
}
if (!Array.isArray(ordered_ad_ids) || !jQuery.isPlainObject(this.ads)) {
return;
}
for (var i = 0; i < ordered_ad_ids.length; i++) {
if (!this.ads.hasOwnProperty(ordered_ad_ids[i])) {
continue;
}
var ad_info = this.ads[ordered_ad_ids[i]];
if (typeof ad_info === 'object') {
var ad = new Advads_passive_cb_Ad(ad_info, this.elementid);
if (ad.can_display()) {
if (ad_for_adblocker) {
ad = ad_for_adblocker;
}
const canTrack =
!advads.privacy ||
'unknown' !== advads.privacy.get_state();
if (
(this.type === 'slider' && this.slider_options) ||
this.group_wrap
) {
output_buffer.push(
ad.output({
track: canTrack,
inject: false,
do_has_ad: true,
})
);
} else {
ad.output({
track: canTrack,
inject: true,
do_has_ad: true,
});
}
ads_displayed++;
this.is_empty = false;
}
}
// break the loop when maximum ads are reached
if (ads_displayed === this.ad_count) {
break;
}
// show only first ad when an ad blocker is found.
if (!this.is_empty && ad_for_adblocker) {
break;
}
}
if (output_buffer.length) {
if (this.type === 'slider' && this.slider_options) {
output_buffer = this.output_slider(output_buffer);
}
advanced_ads_pro.inject(
this.elementid,
this.add_group_wrap(output_buffer, ads_displayed)
);
}
}
/**
* Output for group that has a refresh set up
*/
output_refresh() {
var ordered_ad_ids = this.ordered_ad_ids,
output_buffer = [],
self = this,
index = 0,
ad_id,
prev_ad_id = false,
tracked_ads = [],
ads_displayed = 0,
interval = this.refresh_interval;
var $el = jQuery('.' + self.elementid);
$el = advanced_ads_pro._inject_before(this.elementid, $el);
if (!Array.isArray(ordered_ad_ids) || !jQuery.isPlainObject(this.ads)) {
return;
}
/**
* Track the ad.
*
* @param {Object} ad Advads_passive_cb_Ad
*/
function track_ad(ad) {
if (jQuery.inArray(ad.id, tracked_ads) < 0 && ad.tracking_enabled) {
var data = {};
data[ad.blog_id] = [ad.id];
advanced_ads_pro.observers.fire({
event: 'inject_passive_ads',
ad_ids: data,
});
}
}
function pick_ids() {
switch (self.type) {
case 'ordered':
var prev_index = ordered_ad_ids.indexOf(prev_ad_id);
if (prev_index !== -1) {
var new_ids = ordered_ad_ids
.slice(prev_index + 1)
.concat(ordered_ad_ids.slice(0, prev_index));
} else {
var new_ids = ordered_ad_ids;
}
break;
default:
var new_ids = self.shuffle_ads();
var prev_index = new_ids.indexOf(prev_ad_id);
if (prev_index !== -1) {
new_ids.splice(prev_index, 1);
}
}
return new_ids;
}
function get_ad_interval(ad_id) {
if (typeof self.refresh_interval !== 'object') {
return parseInt(self.refresh_interval, 10) || 2000;
}
return parseInt(self.refresh_interval[ad_id], 10) || 2000;
}
/**
* Get the 'float' property from the ad or the placement.
*
* @param {Object} ad Advads_passive_cb_Ad
* @return string
*/
function get_position(ad) {
var position = '';
if (
advads_pro_utils.isset_nested(
self.placement,
'placement_info',
'options',
'placement_position'
)
) {
position =
self.placement.placement_info.options.placement_position;
}
if (['left', 'right'].indexOf(position) === -1) {
position = ad.position;
}
return position;
}
(function tick() {
var new_ids = pick_ids();
var idsLen = new_ids.length;
for (var i = 0; i < idsLen; i++) {
var ad_id = new_ids[i];
var ad_info = self.ads[ad_id];
if (typeof ad_info === 'object') {
var ad = new Advads_passive_cb_Ad(ad_info, self.elementid);
if (ad.can_display()) {
// The first ad will be tracked like all other passive ads.
if (ads_displayed === 0) {
output_buffer = [
ad.output({
track: true,
inject: false,
do_has_ad: true,
}),
];
advanced_ads_group_refresh.prepare_wrapper(
$el,
get_position(ad),
true
);
} else {
var do_has_ad =
jQuery.inArray(ad_id, tracked_ads) < 0;
output_buffer = [
ad.output({
track: false,
inject: false,
do_has_ad: do_has_ad,
}),
];
track_ad(ad);
advanced_ads_group_refresh.prepare_wrapper(
$el,
get_position(ad),
false
);
}
tracked_ads.push(ad.id);
advanced_ads_pro.inject(
self.elementid,
self.add_group_wrap(output_buffer, 1)
);
self.is_empty = false;
ads_displayed++;
setTimeout(function () {
if (
!self.placement ||
self.placement.can_display()
) {
tick();
}
}, get_ad_interval(ad_id));
prev_ad_id = ad.id;
break;
}
}
}
})();
}
/**
* Get markup to inject around each ad and around entire set of ads (if needed)
*
* @param {Array} output_buffer output buffer.
* @param {number} ads_displayed amount of ad to show for the group.
* @returns {string} output with wrapper (if any)
*/
add_group_wrap(output_buffer, ads_displayed) {
if (!output_buffer.length) {
return '';
}
var before = '',
after = '';
if (this.group_wrap) {
for (var i = 0; i < this.group_wrap.length; i++) {
var wrap = this.group_wrap[i];
wrap.min_ads = wrap.min_ads || 1;
if (typeof wrap !== 'object' || wrap.min_ads > ads_displayed) {
continue;
}
if (wrap.before) {
before = wrap.before + before;
}
if (wrap.after) {
after = after + wrap.after;
}
if (typeof wrap.each === 'string') {
for (var j = 0; j < output_buffer.length; j++) {
output_buffer[j] = wrap.each.replace(
'%s',
output_buffer[j]
);
}
} else if (typeof wrap.each === 'object') {
var each_obj = wrap.each;
for (var j = 0; j < output_buffer.length; j++) {
for (var format_index in each_obj) {
var ad_wrapped = false;
if (
each_obj.hasOwnProperty(format_index) &&
format_index !== 'all' &&
(1 + j) % parseInt(format_index, 10) === 0
) {
output_buffer[j] = each_obj[
format_index
].replace('%s', output_buffer[j]);
ad_wrapped = true;
break;
}
}
if (!ad_wrapped && each_obj.all) {
// applied here since JavaScript does not guarantee object key order
output_buffer[j] = each_obj.all.replace(
'%s',
output_buffer[j]
);
}
}
}
}
}
return before + output_buffer.join('') + after;
}
/**
* Output slider markup around slides
*
* @param output_buffer output buffer
* @deprecated since AAS_VERSION > 1.3.1
* @returns {*[]}
*/
output_slider(output_buffer) {
var output_html, ads_output;
if (
output_buffer.length > 1 &&
typeof jQuery.fn.unslider === 'function'
) {
ads_output = output_buffer.join('</li><li>');
output_buffer = [];
output_buffer.push(
'<div id="' +
this.slider_options.slider_id +
'" class="' +
this.slider_options.init_class +
' ' +
this.slider_options.prefix +
'slider"><ul><li>'
);
output_buffer.push(ads_output);
output_buffer.push('</li></ul></div>');
/* custom css file was added with version 1.1 of Advads Slider. Deactivate the following lines if there are issues with your layout
output_buffer.push( "<style>.advads-slider { position: relative; width: 100% !important; overflow: hidden; } " );
output_buffer.push( ".advads-slider ul, .advads-slider li { list-style: none; margin: 0 !important; padding: 0 !important; } " );
output_buffer.push( ".advads-slider ul li { width: 100%; float: left; }</style>" );
*/
output_buffer.push(
'<scr' +
"ipt>jQuery(function() { jQuery('." +
this.slider_options.init_class +
"').unslider({ " +
this.slider_options.settings +
' }); });</scr' +
'ipt>'
);
}
return output_buffer;
}
/**
* Shuffle ads that have the same weights.
*
* @param {Array} ordered_ad_ids Ad ids.
* @param {Object} weights ad_id: weight pairs.
* @return {Array} ordered_ad_ids Ad ids.
*/
shuffle_ordered_ads(ordered_ad_ids, weights) {
// Get weights of ordered ad ids.
const weight_array = [];
for (var i = 0; i < ordered_ad_ids.length; i++) {
var weight = weights[ordered_ad_ids[i]];
if (!weight) {
return ordered_ad_ids;
}
weight_array.push(weight);
}
var count = weight_array.length;
var pos = 0;
for (var i = 1; i <= count; i++) {
if (i == count || weight_array[i] !== weight_array[i - 1]) {
var slice_len = i - pos;
if (slice_len !== 1) {
var shuffled = advads_pro_utils.shuffle_array(
ordered_ad_ids.slice(pos, pos + slice_len)
);
// Replace the unshuffled chunk of array with the shuffled one.
var arg = [pos, slice_len].concat(shuffled);
Array.prototype.splice.apply(ordered_ad_ids, arg);
}
pos = i;
}
}
return ordered_ad_ids;
}
/**
* shuffle ads based on ad weight
*
* @return {Array} shuffled array with ad ids
*/
shuffle_ads() {
const shuffled_ads = [],
ad_weights = jQuery.extend({}, this.weights);
let random_ad_id = advads_pro_utils.get_random_el_by_weight(ad_weights);
// while non-zero weights are set select random next
while (null !== random_ad_id) {
// remove chosen ad from weights array
delete ad_weights[random_ad_id];
// put random ad into shuffled array
shuffled_ads.push(parseInt(random_ad_id, 10));
random_ad_id = advads_pro_utils.get_random_el_by_weight(ad_weights);
}
return shuffled_ads;
}
}
export const PassiveGroupCompat = () => {
window.Advads_passive_cb_Group = PassiveGroup;
};

View File

@@ -0,0 +1,310 @@
/* eslint-disable */
import jQuery from 'jquery';
export class PassivePlacement {
/**
* Constructor
*
* @param {Object} placement
* @param {string} element_id
*/
constructor(placement, element_id) {
if (
typeof placement !== 'object' ||
!placement.hasOwnProperty('id') ||
!placement.hasOwnProperty('type') ||
!placement.hasOwnProperty('ads') ||
!placement.hasOwnProperty('placement_info') ||
typeof placement.ads !== 'object'
) {
throw new SyntaxError(
'Can not create Advads_passive_cb_Placement obj'
);
}
this.id = placement.id;
this.ajax_query = placement.ajax_query;
this.type = placement.type;
this.element_id = element_id;
this.ads = placement.ads;
this.ads_for_ab = placement.ads_for_ab;
this.groups_for_ab = placement.groups_for_ab;
this.placement_info = placement.placement_info;
this.placement_id = advads_pro_utils.isset_nested(
this.placement_info,
'id'
)
? parseInt(this.placement_info.id, 10)
: null;
this.group_info = placement.group_info;
this.group_wrap = placement.group_wrap;
this.server_info_duration =
parseInt(placement.server_info_duration, 10) || 0;
// Conditions that can be checked by passive cache-busting only if cookies exist.
// If not, ajax cache-busting will be used.
this.server_conditions = placement.server_conditions;
if (placement.inject_before) {
advanced_ads_pro.inject_before.push({
elementId: this.element_id,
data: placement.inject_before,
});
}
}
/**
* Check if the placement can be displayed in frontend due to its own conditions
*
* @return {boolean} true if the placement can be displayed in frontend, false otherwise
*/
can_display() {
if (
advads_pro_utils.isset_nested(this.placement_info, 'test_id') &&
jQuery.inArray(
this.placement_info.slug,
advanced_ads_pro.get_random_placements()
) < 0
) {
// do not deliver placement, that belongs to a test, and was not randomly selected by weight
return false;
}
//check if placement was closed with a cookie before (Advads layer plugin)
if (
advads_pro_utils.isset_nested(
this.placement_info,
'layer_placement',
'close',
'enabled'
) &&
this.placement_info.layer_placement.close.enabled
) {
if (
advads_pro_utils.isset_nested(
this.placement_info,
'layer_placement',
'close',
'timeout_enabled'
) &&
this.placement_info.layer_placement.close.timeout_enabled &&
advads_pro_utils.isset(
advads.get_cookie(
'timeout_placement_' + this.placement_info.slug
)
)
) {
return false;
}
}
//check if placement was closed with a cookie before (Sticky Ads plugin)
if (
advads_pro_utils.isset_nested(
this.placement_info,
'close',
'enabled'
) &&
this.placement_info.close.enabled
) {
if (
advads_pro_utils.isset_nested(
this.placement_info,
'close',
'timeout_enabled'
) &&
this.placement_info.close.timeout_enabled &&
advads_pro_utils.isset(
advads.get_cookie(
'timeout_placement_' + this.placement_info.slug
)
)
) {
return false;
}
}
// dont show `Custom Position` placement ad if selector doesnt exist
if (
advads_pro_utils.isset_nested(this.placement_info, 'options') &&
typeof this.placement_info.options === 'object'
) {
var params = this.placement_info.options;
// do not show `Custom Position` placement ad if selector doesn't exist
if (!advads_pro_utils.selector_exists(params)) {
return false;
}
}
return true;
}
/**
* Check if passive cache-busting can be used, i.e. all needed cookies exist.
*
* @return {boolean} true if passive method can be used for this placement
*/
can_use_passive_cb() {
if (!this.ajax_query) {
return true;
}
var stored_info = window.Advads_passive_cb_Conditions.get_stored_info();
var now = ~~(new Date().getTime() / 1000);
for (var hash in this.server_conditions) {
if (!this.server_conditions.hasOwnProperty(hash)) {
continue;
}
var condition = this.server_conditions[hash];
var stored_type = stored_info[condition.type];
if ('object' !== typeof stored_type) {
return false;
}
var stored_condition = stored_type[hash];
if ('object' !== typeof stored_condition) {
return false;
}
if (
(parseInt(stored_condition.time, 10) || 0) +
this.server_info_duration <
now
) {
return false;
}
}
return true;
}
/**
* Fill the placement with the final output
*/
output() {
let is_empty = true;
const group_for_ab = this.get_group_for_adblocker();
if (this.can_display()) {
switch (this.type) {
case 'ad':
if (!this.can_display()) {
break;
}
let ad = new Advads_passive_cb_Ad(
this.ads[this.id],
this.element_id
); // only one ad, pass it as argument
if (!ad.can_display()) {
break;
}
const ad_for_adblocker = this.get_ad_for_adblocker();
if (group_for_ab) {
this.swap_group_info(group_for_ab);
const group = new Advads_passive_cb_Group(
this,
this.element_id
);
group.output();
is_empty = group.is_empty;
break;
} else if (
ad_for_adblocker &&
ad_for_adblocker.can_display()
) {
ad = ad_for_adblocker;
}
ad.output({
track:
!advads.privacy ||
'unknown' !== advads.privacy.get_state(),
inject: true,
do_has_ad: true,
});
is_empty = false;
break;
case 'group':
if (typeof this.group_info === 'object') {
if (group_for_ab) {
this.swap_group_info(group_for_ab);
}
const group = new Advads_passive_cb_Group(
this,
this.element_id
);
group.output();
is_empty = group.is_empty;
}
break;
}
}
advanced_ads_pro.dispatchWrapperCBEvent(
this.element_id,
is_empty,
'passive',
{
emptyCbOption: Boolean(this.placement_info.cache_busting_empty),
}
);
advanced_ads_pro.observers.fire({
event: 'inject_placement',
id: this.placement_id,
is_empty: is_empty,
cb_type: 'passive',
});
advanced_ads_pro.hasAd(
this.placement_id,
'placement',
this.placement_info.title,
'passive'
);
}
/**
* Swap placement item to group type (if not group yet) and use an alternate group info data
*
* @param {Array} group data about the alternative group to use
*/
swap_group_info(group) {
this.id = group.id;
this.type = 'group';
this.group_info = group;
}
/**
* Get passive ad object from the ad blocker item of the placement
*
* @return {Object|boolean} Advads_passive_cb_Ad or false if no ad for ad blocker is set up.
*/
get_ad_for_adblocker() {
return advanced_ads_pro.adblocker_active && this.ads_for_ab
? new Advads_passive_cb_Ad(
this.ads_for_ab[Object.keys(this.ads_for_ab)[0]],
this.element_id
)
: false;
}
/**
* Get group info from the ad blocker item of the placement
*
* @return {Array|boolean} Group info or false if no group is set up as ad blocker item.
*/
get_group_for_adblocker() {
return advanced_ads_pro.adblocker_active && this.groups_for_ab
? this.groups_for_ab
: false;
}
}
export const PassivePlacementCompat = () => {
window.Advads_passive_cb_Placement = PassivePlacement;
};

View File

@@ -0,0 +1,261 @@
/* eslint-disable */
import jQuery from 'jquery';
export const Utils = {
debug:
window.location &&
window.location.hash &&
window.location.hash.indexOf('#debug=true') !== -1,
// Loop over each item in an array-like value.
each: function (arr, fn, _this) {
var i,
len = (arr && arr.length) || 0;
for (i = 0; i < len; i++) {
fn.call(_this, arr[i], i);
}
},
// Loop over each key/value pair in a hash.
each_key: function (obj, fn, _this) {
if ('object' === typeof obj) {
var key;
for (key in obj) {
if (obj.hasOwnProperty(key)) {
fn.call(_this, key, obj[key]);
}
}
}
},
/**
* Log messages to the browser console.
*/
log: function () {
if (this.debug && this.isset(window.console)) {
var args = Array.prototype.slice.call(arguments);
args.unshift('Advanced Ads CB:');
window.console.log.apply(window.console, args);
}
},
/**
* Log cache-busting arrays (AJAX and passive cb).
*/
print_debug_arrays: function () {
if (advanced_ads_pro.iterations === 0) {
// Available when passive cb is enabled for all ads/groups which are not delivered through a placement.
this.log('passive_ads\n', window.advads_passive_ads);
this.log('passive_groups\n', window.advads_passive_groups);
this.log('passive_placements\n', window.advads_passive_placements);
this.log('ajax_queries\n', window.advads_ajax_queries);
this.log(
window.Advads_passive_cb_Conditions.VISITOR_INFO_COOKIE_NAME +
'\n',
window.Advads_passive_cb_Conditions.get_stored_info()
);
}
},
isset: function (str) {
return typeof str !== 'undefined';
},
/**
* Check if nested object key exists
*
* @param {obj}
* @params {str} level1, .. levelN
* @return {bool} true on success false on failure
*/
isset_nested: function (obj) {
var argsLen = arguments.length;
for (var i = 1; i < argsLen; i++) {
if (!obj || !obj.hasOwnProperty(arguments[i])) {
return false;
}
obj = obj[arguments[i]];
}
return true;
},
is_numeric: function (n) {
return !isNaN(parseFloat(n)) && isFinite(n);
},
// generate a random number between min and max (inclide min and max)
get_random_number: function (min, max) {
var rand = min - 0.5 + Math.random() * (max - min + 1);
return Math.round(rand);
},
/**
* Get random element by weight
*
* @param {object} weights e.g. {'A' => 2, 'B' => 3, 'C' => 5}
* @param {string} skip to skip, e.g. 'A'
* @source applied with fix for order http://stackoverflow.com/a/11872928/904614
*/
get_random_el_by_weight: function (weights, skip) {
var max = 0,
rand;
skip = typeof skip !== 'undefined' ? skip : false;
if (typeof weights === 'object') {
for (var el in weights) {
if (el !== skip && weights.hasOwnProperty(el)) {
max += parseInt(weights[el]) || 0;
}
}
if (max < 1) {
return null;
}
rand = advads_pro_utils.get_random_number(1, max);
for (var el in weights) {
if (el !== skip && weights.hasOwnProperty(el)) {
rand -= weights[el];
if (rand <= 0) {
return el;
}
}
}
}
},
/**
* A 'polyfill' of the native 'bind' function.
*
* @param {function} func
* @param {obj} context
*/
bind: function (func, context) {
return function () {
return func.apply(context, arguments);
};
},
/**
* Shuffle array (knuthfisheryates).
* http://stackoverflow.com/a/2450976/1037948
*
* @param {array} arr
* @return {array} arr
*/
shuffle_array: function (arr) {
var temp,
j,
i = arr.length;
if (!i) {
return arr;
}
while (--i) {
j = ~~(Math.random() * (i + 1));
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
return arr;
},
/**
* Check if the selector of the Custom position placement exists.
*
* @param {array} params Placement options.
* @return bool
*/
selector_exists: function (params) {
var cp_target =
!params.inject_by || params.inject_by === 'pro_custom_element'
? 'pro_custom_element'
: 'container_id';
var el = params[cp_target];
if (!el) {
// Not Custom Position placement.
return true;
}
var $el = jQuery(el);
if (!$el.length) {
advads_pro_utils.log('selector does not exist', el);
return false;
}
if (
!advanced_ads_pro_ajax_object.moveintohidden &&
!$el.filter(':visible').length
) {
advads_pro_utils.log('selector is hidden', el);
return false;
}
return true;
},
/**
* Converts the number in degrees to the radians.
*/
deg2rad: function (deg) {
return (deg * Math.PI) / 180;
},
/**
* Computes the distance between the coordinates and returns the result.
*/
calculate_distance: function (lat1, lon1, lat2, lon2, unit) {
unit = unit || 'km';
lat1 = this.deg2rad(lat1);
lon1 = this.deg2rad(lon1);
lat2 = this.deg2rad(lat2);
lon2 = this.deg2rad(lon2);
const dLon = lon2 - lon1;
a =
Math.pow(Math.cos(lat2) * Math.sin(dLon), 2) +
Math.pow(
Math.cos(lat1) * Math.sin(lat2) -
Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon),
2
);
b =
Math.sin(lat1) * Math.sin(lat2) +
Math.cos(lat1) * Math.cos(lat2) * Math.cos(dLon);
const rad = Math.atan2(Math.sqrt(a), b);
if (unit === 'mi') {
return rad * 3958.755865744;
} else {
return rad * 6371.0;
}
},
/**
* Extract cookie data from a stringified cookie.
*
* @param {string} cookie {
* A stringified cookie.
*
* @type {string} data Cookie data.
* @type {string} expire Expiration time.
* }
* @return {mixed} The data field on success, original stringified cookie on error.
*/
extract_cookie_data(cookie) {
try {
var cookie_obj = JSON.parse(cookie);
} catch (e) {
return cookie;
}
if (typeof cookie_obj !== 'object') {
return cookie;
}
return cookie_obj.data;
},
};
export const UtilsCompat = () => {
window.advads_pro_utils = Utils;
};

View File

@@ -0,0 +1,221 @@
import jQuery from 'jquery';
import { PassiveAdCompat } from './cache-busting/passive-ad';
import { PassivePlacementCompat } from './cache-busting/passive-placement';
import { CacheBusting, CacheBustingCompat } from './cache-busting/cacheBusting';
import { PassiveConditionsCompat } from './cache-busting/passive-conditions';
import { PassiveGroupCompat } from './cache-busting/passive-group';
import { Utils, UtilsCompat } from './cache-busting/utils';
import { GroupRefreshCompat } from './cache-busting/group-refresh';
import Adsense from './adsense/adsense';
CacheBustingCompat();
PassiveConditionsCompat();
PassivePlacementCompat();
PassiveAdCompat();
PassiveGroupCompat();
UtilsCompat();
GroupRefreshCompat();
jQuery(() => {
Adsense();
});
/* eslint-disable */
if (
typeof advads !== 'undefined' &&
typeof advads.privacy.dispatch_event !== 'undefined'
) {
// check for changes in privacy settings.
document.addEventListener('advanced_ads_privacy', function (event) {
if (
event.detail.previousState !== 'unknown' &&
!(
event.detail.previousState === 'rejected' &&
event.detail.state === 'accepted'
)
) {
Utils.log(
'no action! transition from ' +
event.detail.previousState +
' to ' +
event.detail.state
);
return;
}
Utils.log(
'reload ads! transition from ' +
event.detail.previousState +
' to ' +
event.detail.state
);
if (
event.detail.state === 'accepted' ||
event.detail.state === 'not_needed'
) {
var encodedAd =
'script[type="text/plain"][data-tcf="waiting-for-consent"]';
// Find all scripts and decode them.
document.querySelectorAll(encodedAd).forEach(function (node) {
// Add the decoded ad ids to passive_ads, so they can be tracked.
if (
!CacheBusting.passive_ads.hasOwnProperty(node.dataset.bid)
) {
CacheBusting.passive_ads[node.dataset.bid] = [];
}
CacheBusting.passive_ads[node.dataset.bid].push(
parseInt(node.dataset.id, 10)
);
advads.privacy.decode_ad(node);
});
// Observe all child node changes on body; check for dynamically added encoded ads.
new MutationObserver(function (mutations) {
var decoded_ads = {},
decode_ad = function (node) {
if (
typeof node.dataset.noTrack === 'undefined' ||
node.dataset.noTrack !== 'impressions'
) {
if (!decoded_ads.hasOwnProperty(node.dataset.bid)) {
decoded_ads[node.dataset.bid] = [];
}
decoded_ads[node.dataset.bid].push(
parseInt(node.dataset.id, 10)
);
}
advads.privacy.decode_ad(node);
};
mutations.forEach(function (mutation) {
mutation.addedNodes.forEach(function (node) {
// The injected node is the ad itself.
if (
typeof node.tagName !== 'undefined' &&
typeof node.dataset !== 'undefined' &&
node.tagName.toLowerCase() === 'script' &&
node.dataset.tcf === 'waiting-for-consent'
) {
decode_ad(node);
return;
}
// The injected node might hold encoded ads, e.g. in infinite scroll.
if (
typeof node.dataset === 'undefined' ||
node.dataset.tcf !== 'waiting-for-consent'
) {
document
.querySelectorAll(encodedAd)
.forEach(decode_ad);
}
});
});
if (Object.keys(decoded_ads).length) {
CacheBusting.observers.fire({
event: 'advanced_ads_decode_inserted_ads',
ad_ids: decoded_ads,
});
}
}).observe(document, {
subtree: true,
childList: true,
});
}
// Wait for advanced_ads_pro to return to idle state.
if (CacheBusting.busy) {
// Only hook this once to prevent infinite loops.
document.addEventListener(
'advanced_ads_pro.idle',
CacheBusting.process_passive_cb,
{ once: true }
);
return;
}
CacheBusting.process_passive_cb();
});
} else {
// Fallback for older versions of base plugin.
(window.advanced_ads_ready || jQuery(document).ready).call(
null,
function () {
CacheBusting.process_passive_cb();
}
);
}
/* eslint-enable */
// Reload ads when screen resizes.
jQuery(document).on('advanced-ads-resize-window', function () {
const handleResize = function () {
// Remove ajax and passive ads.
let cbCount = CacheBusting.ads.length;
while (cbCount--) {
if ('off' !== CacheBusting.ads.cb_method) {
CacheBusting.ads.splice(cbCount, 1);
}
}
CacheBusting.process_passive_cb();
};
// Wait for advanced_ads_pro to return to idle state.
if (CacheBusting.busy) {
// Only hook this once to prevent infinite loops.
document.addEventListener('advanced_ads_pro.idle', handleResize, {
once: true,
});
return;
}
handleResize();
});
/**
* Removes placement placeholder if cache busting could not fill it.
*/
document.addEventListener('advads_pro_cache_busting_done', (ev) => {
if (!ev.detail.isEmpty || !ev.detail.extra.emptyCbOption) {
return;
}
let wrapper = document.getElementById(ev.detail.elementId);
if (!wrapper) {
return;
}
// If the placement is the only thing in the widget (e.g. not within a column block).
if (wrapper.parentNode && wrapper.parentNode.classList.contains('widget')) {
wrapper = wrapper.parentNode;
}
wrapper.remove();
});
/**
* Update ad health ad count on ad injection.
*/
const updateAdHealthCount = () => {
const adHealth = document.getElementById(
'wp-admin-bar-advanced_ads_ad_health_highlight_ads'
);
if (!adHealth) {
return;
}
adHealth.querySelector('.highlighted_ads_count').innerText =
document.querySelectorAll(
`.${window.advancedAds.frontendPrefix}highlight-wrapper`
).length;
};
CacheBusting.observers.add((event) => {
if (
-1 === ['inject_passive_ads', 'inject_ajax_ads'].indexOf(event.event) ||
(Array.isArray(event.ad_ids) && !event.ad_ids.length)
) {
return;
}
updateAdHealthCount();
});

View File

@@ -0,0 +1,4 @@
<?php
/**
* Silence is golden.
*/

View File

@@ -0,0 +1,546 @@
<?php // phpcs:ignoreFile
use AdvancedAds\Widget;
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Framework\Utilities\Params;
/**
*
* NOTE: can not rely actively on base plugin without prior test for existence (only after plugins_loaded hook)
*/
class Advanced_Ads_Pro_Admin {
/**
* Link to plugin page
*
* @since 1.1
* @const
*/
const PLUGIN_LINK = 'https://wpadvancedads.com/add-ons/advanced-ads-pro/';
/**
* Field name of the user role
*
* @since 1.2.5
* @const
*/
const ROLE_FIELD_NAME = 'advanced-ads-role';
/**
* Advanced Ads user roles array.
*
* @var array
*/
private $roles;
/**
* Initialize the plugin
*
* @since 1.0.0
*/
public function __construct() {
$this->roles = [
'advanced_ads_admin' => __( 'Ad Admin', 'advanced-ads-pro' ),
'advanced_ads_manager' => __( 'Ad Manager', 'advanced-ads-pro' ),
'advanced_ads_user' => __( 'Ad User', 'advanced-ads-pro' ),
'' => __( '--no role--', 'advanced-ads-pro' ),
];
// Add add-on settings to plugin settings page.
add_action( 'advanced-ads-settings-init', [ $this, 'settings_init' ], 9 );
add_filter( 'advanced-ads-setting-tabs', [ $this, 'setting_tabs' ] );
// Add user role selection to users page.
add_action( 'show_user_profile', [ $this, 'add_user_role_fields' ] );
add_action( 'edit_user_profile', [ $this, 'add_user_role_fields' ] );
add_action( 'profile_update', [ $this, 'save_user_role' ] );
// Display warning if advanced visitor conditions are not active.
add_action( 'advanced-ads-visitor-conditions-after', [ $this, 'show_condition_notice' ], 10, 0 );
// Display "once per page" field.
add_action( 'advanced-ads-output-metabox-after', [ $this, 'render_ad_output_options' ] );
// Load admin style sheet.
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_styles' ] );
// Render repeat option for Content placement.
add_action( 'advanced-ads-placement-post-content-position', [ $this, 'render_placement_repeat_option' ], 10, 2 );
add_filter( 'pre_update_option_advanced-ads', [ $this, 'pre_update_advanced_ads_options' ], 10, 2 );
// Show/hide warnings for privacy module based on Pro state.
add_filter( 'advanced-ads-privacy-custom-show-warning', [ $this, 'show_custom_privacy_warning' ] );
add_filter( 'advanced-ads-privacy-tcf-show-warning', '__return_false' );
add_filter( 'advanced-ads-privacy-custom-link-attributes', [ $this, 'privacy_link_attributes' ] );
add_filter( 'advanced-ads-ad-privacy-hide-ignore-consent', [ $this, 'hide_ignore_consent_checkbox' ], 10, 3 );
// Show a warning if cache-busting is enabled, but no placement is used for a widget.
add_action( 'in_widget_form', [ $this, 'show_no_placement_in_widget_warning' ], 10, 3 );
add_action( 'advanced-ads-export-options', [ $this, 'export_options' ] );
// Suggest a text for the WP Privacy Policy
add_action( 'admin_init', [ $this, 'add_privacy_policy_content' ] );
// Trim custom code on save.
add_action( 'advanced-ads-ad-pre-save', [$this, 'trim_custom_code_on_save'], 10, 2 );
}
/**
* Trim whitespaces in custom code when saving an ad
*
* @param Ad $ad the ad.
* @param array $post_data sanitized content of $_POST.
*
* @return void
*/
public function trim_custom_code_on_save( $ad, $post_data ) {
if ( isset( $post_data['custom-code'] ) ) {
$ad->set_prop_temp( 'custom-code', trim( (string) $post_data['custom-code'] ) );
}
}
/**
* Add settings to settings page
*
* @param string $hook settings page hook.
* @since 1.0.0
*/
public function settings_init( $hook ) {
register_setting( Advanced_Ads_Pro::OPTION_KEY, Advanced_Ads_Pro::OPTION_KEY );
/**
* Allow Ad Admin to save pro options.
*
* @param array $settings Array with allowed options.
*
* @return array
*/
add_filter( 'advanced-ads-ad-admin-options', function( $options ) {
$options[] = Advanced_Ads_Pro::OPTION_KEY;
return $options;
} );
// Add new section.
add_settings_section(
Advanced_Ads_Pro::OPTION_KEY . '_modules-enable',
'',
[ $this, 'render_modules_enable' ],
Advanced_Ads_Pro::OPTION_KEY . '-settings'
);
// Add new section.
add_settings_section(
'advanced_ads_pro_settings_section',
'',
[ $this, 'render_other_settings' ],
Advanced_Ads_Pro::OPTION_KEY . '-settings'
);
// Setting for Autoptimize support.
$has_optimizer_installed = Advanced_Ads_Checks::active_autoptimize();
if ( ! $has_optimizer_installed && method_exists( 'Advanced_Ads_Checks', 'active_wp_rocket' ) ) {
$has_optimizer_installed = Advanced_Ads_Checks::active_wp_rocket();
}
if ( $has_optimizer_installed ) {
add_settings_field(
'autoptimize-support',
__( 'Allow optimizers to modify ad codes', 'advanced-ads-pro' ),
[ $this, 'render_settings_autoptimize' ],
Advanced_Ads_Pro::OPTION_KEY . '-settings',
'advanced_ads_pro_settings_section'
);
}
add_settings_field(
'placement-positioning',
__( 'Placement positioning', 'advanced-ads-pro' ),
[ $this, 'render_settings_output_buffering' ],
Advanced_Ads_Pro::OPTION_KEY . '-settings',
'advanced_ads_pro_settings_section'
);
add_settings_field(
'disable-by-post-types',
__( 'Disable ads for post types', 'advanced-ads-pro' ),
[ $this, 'render_settings_disable_post_types' ],
$hook,
'advanced_ads_setting_section_disable_ads'
);
}
/**
* Copy settings from `general` tab in order to prevent it from being cleaned
* when Pro is deactivated.
*
* @param mixed $options Advanced Ads options.
* @return mixed options
*/
public function pre_update_advanced_ads_options( $options ) {
$pro = Advanced_Ads_Pro::get_instance()->get_options();
if ( isset( $options['pro']['general']['disable-by-post-types'] ) && is_array( $options['pro']['general']['disable-by-post-types'] ) ) {
$pro['general']['disable-by-post-types'] = $options['pro']['general']['disable-by-post-types'];
} else {
$pro['general']['disable-by-post-types'] = [];
}
Advanced_Ads_Pro::get_instance()->update_options( $pro );
return $options;
}
/**
* Render content of module enable option
*/
public function render_modules_enable() {
}
/**
* Render additional pro settings
*
* @since 1.1
*/
public function render_other_settings() {
// Save options when the user is on the "Pro" tab.
$selected = $this->get_disable_by_post_type_options();
foreach ( $selected as $item ) { ?>
<input type="hidden" name="<?php echo esc_attr( AAP_SLUG ); ?>[general][disable-by-post-types][]" value="<?php echo esc_html( $item ); ?>">
<?php
}
}
/**
* Render Autoptimize settings field.
*
* @since 1.2.3
*/
public function render_settings_autoptimize() {
$options = Advanced_Ads_Pro::get_instance()->get_options();
$autoptimize_support_disabled = $options['autoptimize-support-disabled'] ?? false;
require AA_PRO_ABSPATH . '/views/setting_autoptimize.php';
}
/**
* Render output buffering settings field.
*/
public function render_settings_output_buffering() {
$placement_positioning = Advanced_Ads_Pro::get_instance()->get_options()['placement-positioning'] === 'js' ? 'js' : 'php';
$allowed_types = [
'post_above_headline',
'custom_position',
];
$allowed_types_names = [];
foreach ( $allowed_types as $allowed_type ) {
$allowed_type = wp_advads_get_placement_type( $allowed_type );
if ( $allowed_type && '' !== $allowed_type->get_title() ) {
$allowed_types_names[] = $allowed_type->get_title();
}
}
require AA_PRO_ABSPATH . '/views/setting-placement-positioning.php';
}
/**
* Render settings to disable ads by post types.
*/
public function render_settings_disable_post_types() {
$selected = $this->get_disable_by_post_type_options();
$post_types = get_post_types(
[
'public' => true,
'publicly_queryable' => true,
],
'objects',
'or'
);
$type_label_counts = array_count_values( wp_list_pluck( $post_types, 'label' ) );
require AA_PRO_ABSPATH . '/views/setting_disable_post_types.php';
}
/**
* Get "Disabled by post type" Pro options.
*/
private function get_disable_by_post_type_options() {
$options = Advanced_Ads_Pro::get_instance()->get_options();
if ( isset( $options['general']['disable-by-post-types'] ) && is_array( $options['general']['disable-by-post-types'] ) ) {
$selected = $options['general']['disable-by-post-types'];
} else {
$selected = [];
}
return $selected;
}
/**
* Add tracking settings tab
*
* @since 1.2.0
* @param array $tabs existing setting tabs.
* @return array $tabs setting tabs with AdSense tab attached.
*/
public function setting_tabs( array $tabs ) {
$tabs['pro'] = [
// TODO abstract string.
'page' => Advanced_Ads_Pro::OPTION_KEY . '-settings',
'group' => Advanced_Ads_Pro::OPTION_KEY,
'tabid' => 'pro',
'title' => 'Pro',
];
return $tabs;
}
/**
* Form field for user role selection
*
* @param array $user user data.
*/
public function add_user_role_fields( $user ) {
if ( ! current_user_can( 'edit_users' ) ) {
return;
}
$role = get_user_meta( $user->ID, self::ROLE_FIELD_NAME, true );
?>
<h3><?php esc_html_e( 'Advanced Ads User Role', 'advanced-ads-pro' ); ?></h3>
<table class="form-table">
<tr>
<th><label for="advads_pro_role"><?php esc_html_e( 'Ad User Role', 'advanced-ads-pro' ); ?></label></th>
<td><select name="<?php echo esc_attr( self::ROLE_FIELD_NAME ); ?>" id="advads_pro_role">
<?php
foreach ( $this->roles as $_slug => $_name ) :
?>
<option value="<?php echo esc_attr( $_slug ); ?>" <?php selected( $role, $_slug ); ?>><?php echo esc_html( $_name ); ?></option>
<?php
endforeach;
?>
</select>
<p class="description"><?php esc_html_e( 'Please note, with the last update, the “Ad Admin“ and “Ad Manager“ roles have the “upload_files“ and the “unfiltered_html“ capabilities.', 'advanced-ads-pro' ); ?></p>
</td>
</tr>
</table>
<?php
}
/**
* Update the user role
*
* @param int $user_id ID of the user.
*/
public function save_user_role( $user_id) {
if (
! array_key_exists( self::ROLE_FIELD_NAME, $_POST )
|| ! current_user_can( 'edit_users' )
|| ! wp_verify_nonce( Params::post( '_wpnonce' ), 'update-user_' . $user_id)
) {
return;
}
// check if this is a valid user role.
$user_role = sanitize_text_field( Params::post( self::ROLE_FIELD_NAME ) );
if ( ! array_key_exists( $user_role, $this->roles ) ) {
return;
}
// Get user object.
$user = new WP_User( $user_id );
// Remove previous role.
$prev_role = get_user_meta( $user_id, self::ROLE_FIELD_NAME, true );
$user->remove_role( $prev_role );
// Save new role as user meta.
update_user_meta( $user_id, self::ROLE_FIELD_NAME, $user_role );
if ( $user_role ) {
// Add role.
$user->add_role( $user_role );
}
}
/**
* Show a notice if advanced visitor conditions are disabled. Maybe some users are looking for it
*/
public function show_condition_notice() {
$options = Advanced_Ads_Pro::get_instance()->get_options();
if ( ! isset( $options['advanced-visitor-conditions']['enabled'] ) ) {
echo '<p>' . sprintf(
wp_kses(
/* translators: %s: URL to the settings page */
__( 'Enable the Advanced Visitor Conditions <a href="%s" target="_blank">in the settings</a>.', 'advanced-ads-pro' ),
[
'a' => [
'href' => [],
'target' => [],
],
]
),
esc_url( admin_url( 'admin.php?page=advanced-ads-settings#top#pro' ) )
) . '</p>';
}
}
/**
* Add output options to ad edit page
*
* @param Ad $ad Ad instance.
*/
public function render_ad_output_options( Ad $ad ) {
$once_per_page = $ad->get_prop( 'once_per_page' ) ? 1 : 0;
require AA_PRO_ABSPATH . '/views/setting_output_once.php';
// Get CodeMirror setting for Custom code textarea.
$settings = $this->get_code_editor_settings();
$custom_code = ! empty( $ad->get_prop( 'custom-code' ) ) ? esc_textarea( $ad->get_prop( 'custom-code' ) ) : '';
$privacy_options = Advanced_Ads_Privacy::get_instance()->options();
require AA_PRO_ABSPATH . '/views/setting_custom_code.php';
}
/**
* Render repeat option for Content placement.
*
* @param string $placement_slug Placement id.
* @param Placement $placement Placement instance.
*/
public function render_placement_repeat_option( $placement_slug, $placement ) {
$data = $placement->get_data();
$words_between_repeats = ! empty( $data['words_between_repeats'] ) ? absint( $data['words_between_repeats'] ) : 0;
require AA_PRO_ABSPATH . '/views/setting_repeat.php';
}
/**
* Get CodeMirror settings.
*/
public function get_code_editor_settings() {
global $wp_version;
if ( 'advanced_ads' !== get_current_screen()->id
|| defined( 'ADVANCED_ADS_DISABLE_CODE_HIGHLIGHTING' )
|| -1 === version_compare( $wp_version, '4.9' ) ) {
return false;
}
// Enqueue code editor and settings for manipulating HTML.
$settings = wp_enqueue_code_editor( [ 'type' => 'text/html' ] );
if ( ! $settings ) {
$settings = false;
}
return $settings;
}
/**
* Register and enqueue admin-specific style sheet.
*/
public function enqueue_admin_styles() {
wp_enqueue_style( AAP_SLUG . '-admin-styles', AAP_BASE_URL . 'assets/admin.css', [], AAP_VERSION );
}
/**
* Only show privacy warning if cache-busting module not enabled.
*
* @param bool $show Whether to show warning.
*
* @return bool
*/
public function show_custom_privacy_warning( $show ) {
if ( ! $show ) {
return $show;
}
$options = Advanced_Ads_Pro::get_instance()->get_options();
return ! isset( $options['cache-busting']['enabled'] );
}
/**
* Update Link in Privacy settings ot settings page instead of external plugin page.
*
* @return array
*/
public function privacy_link_attributes() {
return [
'href' => esc_url( admin_url( 'admin.php?page=advanced-ads-settings#top#pro' ) ),
];
}
/**
* Show the ignore-consent checkbox if this ad has custom code and type is image or dummy.
* The filter is called `advanced-ads-ad-privacy-hide-ignore-consent`, so the return needs to be !$hide to show.
*
* @param bool $hide Whether to show ignore-consent checkbox.
* @param Ad $ad Ad instance.
*
* @return bool
*/
public function hide_ignore_consent_checkbox( $hide, Ad $ad ) {
if ( ! $hide || ! $ad->is_type( [ 'image', 'dummy' ] ) ) {
return $hide;
}
return empty( Advanced_Ads_Pro::get_instance()->get_custom_code( $ad ) );
}
/**
* Show a warning below the form of Advanced Ads widgets if cache-busting is enabled
* but the widget does not use a placement or "Force passive cache-busting" is enabled
*
* Uses the in_widget_form action hook
*
* @param WP_Widget $widget The widget instance (passed by reference).
* @param null $return Return null if new fields are added.
* @param array $instance An array of the widget's settings.
*/
public function show_no_placement_in_widget_warning( $widget, $return, $instance ) {
// bail if this is not the Advanced Ads widget
if ( ! is_a( $widget, Widget::class ) ) {
return;
}
// bail if cache-busting is not enabled or if Force passive cache-busting is enabled
$options = Advanced_Ads_Pro::get_instance()->get_options();
if ( empty( $options['cache-busting']['enabled'] ) || isset( $options['cache-busting']['passive_all'] ) ) {
return;
}
// check item ID and show warning if it is given but does not contain a placement
if ( ! empty( $instance['item_id'] ) && 0 !== strpos( $instance['item_id'], 'placement_' ) ) {
?>
<p class="advads-notice-inline advads-error">
<?php esc_html_e( 'Select a Sidebar placement to enable cache-busting.', 'advanced-ads-pro' ); ?>
<a href="https://wpadvancedads.com/manual/cache-busting/#Cache-Busting_in_Widgets" target="_blank">
<?php esc_html_e( 'Learn more', 'advanced-ads-pro' ); ?>
</a>
</p>
<?php
}
}
/**
* Add Pro options to the list of options to be exported.
*
* @param $options Array of option data keyed by option keys.
* @return $options Array of option data keyed by option keys.
*/
public function export_options( $options ) {
$options[ Advanced_Ads_Pro::OPTION_KEY ] = get_option( Advanced_Ads_Pro::OPTION_KEY );
return $options;
}
/**
* Adds a privacy policy statement under Settings > Privacy > Policy Guide
* which customers can use as a basic templace.
*/
public function add_privacy_policy_content() {
if ( ! function_exists( 'wp_add_privacy_policy_content' ) ) {
return;
}
ob_start();
include AA_PRO_ABSPATH . 'views/privacy-policy-content.php';
wp_add_privacy_policy_content( 'Advanced Ads Pro', wp_kses_post( wpautop( ob_get_clean(), false ) ) );
}
}

View File

@@ -0,0 +1,626 @@
<?php // phpcs:ignoreFile
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Frontend\Stats;
/**
* Class Advanced_Ads_Pro
*/
class Advanced_Ads_Pro {
/**
* Pro options
*
* @var array
*/
protected $options;
/**
* Interal plugin options set by the plugin
*
* @var array (if loaded)
*/
protected $internal_options;
/**
* Option name shared by child modules.
*
* @var string
*/
const OPTION_KEY = 'advanced-ads-pro';
/**
* Name of the frontend script.
*
* @var string
*/
const FRONTEND_SCRIPT_HANDLE = 'advanced-ads-pro/front';
/**
* Instance of Advanced_Ads_Pro
*
* @var Advanced_Ads_Pro
*/
private static $instance;
/**
* Advanced_Ads_Pro constructor.
*/
private function __construct() {
// Setup plugin once base plugin that is initialized at priority `20` is available.
add_action( 'plugins_loaded', [ $this, 'init' ], 30 );
}
/**
* Instance of Advanced_Ads_Pro
*
* @return Advanced_Ads_Pro
*/
public static function get_instance() {
if ( ! isset( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Must not be called before `plugins_loaded` hook.
*/
public function init() {
// TODO: Update routines will be handled by the Advanced Ads Framework. Move this function then accordingly.
$this->plugin_updates();
// Load config and modules.
$options = $this->get_options();
Advanced_Ads_ModuleLoader::loadModules( AAP_PATH . '/modules/', isset( $options['modules'] ) ? $options['modules'] : [] );
// Load admin on demand.
if ( is_admin() ) {
new Advanced_Ads_Pro_Admin();
// Run after the internal Advanced Ads version has been updated by the `Advanced_Ads_Upgrades`, because.
// The `Admin_Notices` can update this version, and the `Advanced_Ads_Upgrades` will not be called.
add_action( 'init', [ $this, 'maybe_update_capabilities' ] );
add_filter( 'advanced-ads-notices', [ $this, 'add_notices' ] );
} else {
// Force advanced js file to be attached.
add_filter( 'advanced-ads-activate-advanced-js', '__return_true' );
// Check autoptimize.
if ( method_exists( 'Advanced_Ads_Checks', 'requires_noptimize_wrapping' ) && Advanced_Ads_Checks::requires_noptimize_wrapping() && ! isset( $options['autoptimize-support-disabled'] ) ) {
add_filter( 'advanced-ads-output-inside-wrapper', [ $this, 'autoptimize_support' ] );
}
}
new Advanced_Ads_Pro_Compatibility();
add_action( 'wp_loaded', [ $this, 'wp_loaded' ] );
add_filter( 'advanced-ads-can-display-ad', [ $this, 'can_display_by_display_limit' ], 10, 3 );
add_filter( 'advanced-ads-ad-output', [ $this, 'add_custom_code' ], 30, 2 );
add_filter( 'advanced-ads-ad-output', [ $this, 'encode_ad_custom_code' ], 20, 2 );
add_filter( 'advanced-ads-placement-content-offsets', [ $this, 'placement_content_offsets' ], 10, 6 );
add_action( 'wp_head', [ $this, 'wp_head' ] );
add_action( 'wp_enqueue_scripts', [ $this, 'wp_enqueue_scripts' ] );
}
/**
* Remove shortcodes from the free plugin then replace it with the new ones
*
* @return void
*/
public function wp_loaded() {
$this->remove_shortcodes();
$this->add_shortcodes();
}
/**
* Remove free plugin's shortcodes
*
* @return void
*/
public function remove_shortcodes() {
remove_shortcode( 'the_ad' );
remove_shortcode( 'the_ad_group' );
remove_shortcode( 'the_ad_placement' );
}
/**
* Add new shortcodes
*
* @return void
*/
public function add_shortcodes() {
add_shortcode( 'the_ad', [ $this, 'shortcode_display_ad' ] );
add_shortcode( 'the_ad_group', [ $this, 'shortcode_display_ad_group' ] );
add_shortcode( 'the_ad_placement', [ $this, 'shortcode_display_ad_placement' ] );
}
/**
* Enqueue front end script.
*/
public function wp_enqueue_scripts() {
// Do not enqueue on AMP pages.
if ( function_exists( 'advads_is_amp' ) && advads_is_amp() ) {
return;
}
wp_enqueue_script(
self::FRONTEND_SCRIPT_HANDLE,
sprintf( '%sassets/js/advanced-ads-pro%s.js', AAP_BASE_URL, defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min' ),
[ 'jquery', ADVADS_SLUG . '-advanced-js' ],
AAP_VERSION,
true
);
wp_localize_script(
self::FRONTEND_SCRIPT_HANDLE,
'advanced_ads_cookies',
[
'cookie_path' => COOKIEPATH,
'cookie_domain' => COOKIE_DOMAIN,
]
);
}
/**
* Front end header script.
*/
public function wp_head() {
// Do not enqueue on AMP pages.
if ( function_exists( 'advads_is_amp' ) && advads_is_amp() ) {
return;
}
?><script type="text/javascript">
var advadsCfpQueue = [];
var advadsCfpAd = function( adID ){
if ( 'undefined' == typeof advadsProCfp ) { advadsCfpQueue.push( adID ) } else { advadsProCfp.addElement( adID ) }
};
</script>
<?php
}
/**
* Return Advanced Ads Pro options
*
* @return array
*/
public function get_options() {
if ( ! isset( $this->options ) ) {
$default_options = [];
$this->options = get_option( self::OPTION_KEY, $default_options );
// Handle previous option key.
if ( $this->options === [] ) {
$old_options = get_option( self::OPTION_KEY . '-modules', false );
if ( $old_options ) {
// Update old options.
$this->update_options( $old_options );
delete_option( self::OPTION_KEY . '-modules' );
}
}
if ( ! isset( $this->options['placement-positioning'] ) ) {
$this->options['placement-positioning'] = 'php';
}
}
return $this->options;
}
/**
* Set a specific option.
*
* @param string $key key to identify the option.
* @param mixed $value value of the option.
*/
public function set_option( $key, $value ) {
$options = $this->get_options();
$options[ $key ] = $value;
$this->update_options( $options );
}
/**
* Update all Advanced Ads Pro options.
*
* @param array $options
*/
public function update_options( array $options ) {
$updated = update_option( self::OPTION_KEY, $options );
if ( $updated ) {
$this->options = $options;
}
}
/**
* Add autoptimize support
*
* @param string $ad_content ad content.
* @return string output that should not be changed by Autoptimize.
*/
public function autoptimize_support( $ad_content = '' ) {
return '<!--noptimize-->' . $ad_content . '<!--/noptimize-->';
}
/**
* Return internal plugin options, these are options set by the plugin
*
* @param bool $set_defaults true if we set default options.
* @return array $options
*/
public function internal_options( $set_defaults = true ) {
if ( ! $set_defaults ) {
return get_option( 'Advanced Ads Pro' . '-internal', [] );
}
if ( ! isset( $this->internal_options ) ) {
$defaults = [
'version' => AAP_VERSION,
];
$this->internal_options = get_option( 'Advanced Ads Pro' . '-internal', [] );
// Save defaults.
if ( $this->internal_options === [] ) {
$this->internal_options = $defaults;
$this->update_internal_options( $this->internal_options );
}
}
return $this->internal_options;
}
/**
* Update internal plugin options
*
* @param array $options new internal options.
*/
public function update_internal_options( array $options ) {
$this->internal_options = $options;
update_option( 'Advanced Ads Pro' . '-internal', $options );
}
/**
* Update capabilities and warn user if needed
*/
public function maybe_update_capabilities() {
$internal_options = $this->internal_options( false );
if ( ! isset( $internal_options['version'] ) ) {
$roles = [ 'advanced_ads_admin', 'advanced_ads_manager' ];
// Add notice if there is at least 1 user with that role.
foreach ( $roles as $role ) {
$users_query = new WP_User_Query(
[
'fields' => 'ID',
'number' => 1,
'role' => $role,
]
);
if ( count( $users_query->get_results() ) ) {
Advanced_Ads_Admin_Notices::get_instance()->add_to_queue( 'pro_changed_caps' );
break;
}
}
$admin_role = get_role( 'advanced_ads_admin' );
if ( $admin_role ) {
$admin_role->add_cap( 'upload_files' );
$admin_role->add_cap( 'unfiltered_html' );
}
$manager_role = get_role( 'advanced_ads_manager' );
if ( $manager_role ) {
$manager_role->add_cap( 'upload_files' );
$manager_role->add_cap( 'unfiltered_html' );
}
// Save new version.
$this->internal_options();
}
}
/**
* Add potential warning to global array of notices.
*
* @param array $notices existing notices.
*
* @return mixed
*/
public function add_notices( $notices ) {
$notices['pro_changed_caps'] = [
'type' => 'update',
'text' => __( 'Please note, the “Ad Admin“ and the “Ad Manager“ roles have the “upload_files“ and the “unfiltered_html“ capabilities', 'advanced-ads-pro' ),
'global' => true,
];
$message = wp_kses(
sprintf(
/* translators: 1 is the opening link to the Advanced Ads website, 2 the closing link */
__(
'We have renamed the Responsive Ads add-on to Advanced Ads AMP Ads. With this change, the Browser Width visitor condition moved from that add-on into Advanced Ads Pro. You can deactivate Advanced Ads AMP Ads if you dont utilize AMP ads or the custom sizes feature for responsive AdSense ad units. %1$sRead more%2$s.',
'advanced-ads-pro'
),
'<a href="https://wpadvancedads.com/responsive-ads-add-on-becomes-amp-ads" target="_blank" class="advads-manual-link">',
'</a>'
),
[
'a' => [
'href' => true,
'target' => true,
'class' => true,
],
]
);
$notices['pro_responsive_migration'] = [
'type' => 'info',
'text' => $message,
'global' => true,
];
return $notices;
}
/**
* Check if the ad can be displayed based on display limit
*
* @param bool $can_display Existing value.
* @param Ad $ad Ad instance.
* @param array $check_options Options to check.
*
* @return bool true if limit is not reached, false otherwise
*/
public function can_display_by_display_limit( $can_display, Ad $ad, $check_options ) {
if ( ! $can_display ) {
return false;
}
if ( empty( $check_options['passive_cache_busting'] ) && $ad->get_prop( 'once_per_page' ) ) {
foreach ( Stats::get()->entities as $item ) {
if ( $item['type'] === 'ad' && absint( $item['id'] ) === $ad->get_id() ) {
return false;
}
}
}
return true;
}
/**
* Get offsets for Content placement.
*
* @param array $offsets Existing Offsets.
* @param array $options Injection options.
* @param array $placement_opts Placement options.
* @param object $xpath DOMXpath object.
* @param array $items Selected items.
* @param object $dom DOMDocument object.
* @return array $offsets New offsets.
*/
public function placement_content_offsets( $offsets, $options, $placement_opts, $xpath = null, $items = null, $dom = null ) {
if ( ! isset( $options['paragraph_count'] ) ) {
return $offsets;
}
if ( isset( $placement_opts['placement']['type'] ) ) {
if ( 'post_content_random' === $placement_opts['placement']['type'] ) {
$max = absint( $options['paragraph_count'] - 1 );
// Skip if have only one paragraph since `wp_rand( 0, 0)` generates large number.
if ( $max > 0 ) {
$rand = wp_rand( 0, $max );
$offsets = [ $rand ];
}
}
if ( 'post_content_middle' === $placement_opts['placement']['type'] ) {
$middle = absint( ( $options['paragraph_count'] - 1 ) / 2 );
$offsets = [ $middle ];
}
}
// "Content" placement, repeat position.
if ( ! empty( $placement_opts['repeat'] ) || ! empty( $options['repeat'] )
&& isset( $options['paragraph_id'] )
&& isset( $options['paragraph_select_from_bottom'] ) ) {
$offsets = [];
for ( $i = $options['paragraph_id'] - 1; $i < $options['paragraph_count']; $i++ ) {
// Select every X number.
if ( ( $i + 1 ) % $options['paragraph_id'] === 0 ) {
$offsets[] = $options['paragraph_select_from_bottom'] ? $options['paragraph_count'] - 1 - $i : $i;
}
}
}
if ( ! empty( $placement_opts['words_between_repeats'] )
&& $xpath && $items && $dom ) {
$options['words_between_repeats'] = absint( $placement_opts['words_between_repeats'] );
$offset_shifter = new Advanced_Ads_Pro_Offset_Shifter( $dom, $xpath, $options );
$offsets = $offset_shifter->calc_offsets( $offsets, $items );
}
return $offsets;
}
/**
* Add custom code after the ad.
*
* Note: this wont work for the Background ad placement. There is a custom solution for that in Advanced_Ads_Pro_Module_Background_Ads:ad_output
*
* @param string $ad_content Ad content.
* @param Ad $ad Ad instance.
* @return string $ad_content Ad content.
*/
public function add_custom_code( $ad_content, Ad $ad ) {
$custom_code = $this->get_custom_code($ad);
if ( empty( $custom_code ) ) {
return $ad_content;
}
$privacy = Advanced_Ads_Privacy::get_instance();
if ( $privacy->is_ad_output_encoded( $ad_content ) ) {
// If the ad_content is already encoded, do not append the custom code in plain text after it.
return $privacy->encode_ad( $this->decode_output( trim( $ad_content ) ) . $custom_code, $ad );
}
return $ad_content . $custom_code;
}
/**
* Retrieve the original ad content from an encoded script tag
*
* @param string $output the encoded output.
*
* @return string
*/
private function decode_output( $output ) {
// Strips the <script ...> and the </script> then base64_decode the remaining characters.
return base64_decode( substr( $output, strpos( $output, '>' ) + 1, -9 ) );
}
/**
* If this ad has custom code, encode the output.
*
* @param string $output The output string.
* @param Ad $ad The ad object.
*
* @return string
*/
public function encode_ad_custom_code( $output, Ad $ad ) {
$privacy = Advanced_Ads_Privacy::get_instance();
if (
// don't encode if AMP.
( function_exists( 'advads_is_amp' ) && advads_is_amp() )
// privacy module is either not enabled, or shows all ads without consent.
|| ( empty( $privacy->options()['enabled'] ) )
// Ad is already encoded.
|| ( ! method_exists( $privacy, 'is_ad_output_encoded' ) || $privacy->is_ad_output_encoded( $output ) )
// Consent is overridden, and this is not an AdSense ad, don't encode it.
|| ( ! $ad->is_type( 'adsense' ) && $ad->get_prop( 'privacy.ignore-consent' ) )
) {
return $output;
}
// If we have custom code, encode the ad.
if ( ! empty( $this->get_custom_code( $ad ) ) ) {
$output = $privacy->encode_ad( $output, $ad );
}
return $output;
}
/**
* Get the custom code for this ad.
*
* @param Ad $ad The ad object.
*
* @return string
*/
public function get_custom_code( Ad $ad ) {
$custom_code = $ad->get_prop( 'custom-code' ) ?? '';
return (string) apply_filters( 'advanced_ads_pro_output_custom_code', $custom_code, $ad );
}
/**
* Enable placement test emails
*/
public static function enable_placement_test_emails() {
$placement_tests = get_option( 'advads-ads-placement-tests', [] );
if ( ! wp_next_scheduled( 'advanced-ads-placement-tests-emails' ) && count($placement_tests) > 0 ) {
// Only schedule if not yet scheduled & tests exists.
wp_schedule_event( time(), 'daily', 'advanced-ads-placement-tests-emails' );
} elseif ( wp_next_scheduled( 'advanced-ads-placement-tests-emails' ) && count($placement_tests) <= 0 ) {
// deactivate if running and tests empty.
self::disable_placement_test_emails();
}
}
/**
* Disable placement test emails
*/
public static function disable_placement_test_emails() {
wp_clear_scheduled_hook( 'advanced-ads-placement-tests-emails' );
}
/**
* Shortcode to include ad in frontend
*
* @param array $atts shortcode attributes.
* @return string content as generated by the shortcode.
*/
public function shortcode_display_ad( $atts ) {
return $this->do_shortcode( $atts, 'render_ad' );
}
/**
* Shortcode to include ad from an ad group in frontend
*
* @param array $atts shortcode attributes.
* @return string content as generated by the shortcode.
*/
public function shortcode_display_ad_group( $atts ) {
return $this->do_shortcode( $atts, 'render_group' );
}
/**
* Shortcode to display content of an ad placement in frontend
*
* @param array $atts shortcode attributes.
* @return string content as generated by the shortcode.
*/
public function shortcode_display_ad_placement( $atts ) {
return $this->do_shortcode( $atts, 'render_placement' );
}
/**
* Create shortcode output.
*
* @param array $atts shortcode attributes.
* @param string $function_name function to be executed by the shortcode.
*
* @return string content as generated by the shortcode.
*/
private function do_shortcode( $atts, $function_name ) {
$blog_id = isset( $atts['blog_id'] ) ? absint( $atts['blog_id'] ) : 0;
if ( $blog_id && $blog_id !== get_current_blog_id() && is_multisite() ) {
// Prevent database error.
if ( ! Advanced_Ads_Pro_Utils::blog_exists( $blog_id ) ) {
return '';
}
if ( is_multisite() ) {
switch_to_blog( $blog_id );
}
// Use the public available function here.
$result = call_user_func( [ wp_advads()->shortcodes, $function_name ], $atts );
if ( is_multisite() ) {
restore_current_blog();
}
return $result;
}
// Use the public available function here.
return call_user_func( [ wp_advads()->shortcodes, $function_name ], $atts );
}
/**
* Plugin update.
*
* @return void
*/
private function plugin_updates(): void {
$pro_options = $this->get_options();
$free_options = Advanced_Ads::get_instance()->options();
if ( isset( $pro_options['responsive-ads'] ) || ! isset( $free_options['responsive-ads'] ) ) {
return;
}
$this->set_option( 'responsive-ads', $free_options['responsive-ads'] );
}
}

View File

@@ -0,0 +1,558 @@
<?php // phpcs:ignore WordPress.Files.FileName
/**
* Calculates new offsets based on amount of words between ads.
*/
class Advanced_Ads_Pro_Offset_Shifter {
/**
* Hold dom.
*
* @var DOMDocument
*/
private $dom = null;
/**
* Hold xpath.
*
* @var DOMXPath
*/
private $xpath = null;
/**
* Hold options.
*
* @var array
*/
private $options = null;
/**
* Default options.
*
* @var array
*/
protected $default_options = [
// Required amount of words between ads.
'words_between_repeats' => 20,
// Whether to check the required amount of words before the first item.
'require_before_first' => false,
// Whether to check the required amount of words after the last item.
'require_after_last' => false,
'debug' => false,
'before' => false,
'paragraph_select_from_bottom' => false,
];
/**
* Amount of words between items.
*
* @var array
*/
protected $words_between = [];
/**
* Previous offset.
*
* @var false/int
*/
protected $previous_offset = false;
const START_EXISTING_AD = 'advads_amount_of_words_s';
const END_EXISTING_AD = 'advads_amount_of_words_e';
const INSERTION_POINT = 'advads_amount_of_words_i';
const SPLIT_REGEXP = '/(advads_amount_of_words_.)/';
const PREV_WORDS = 'prev_words';
const PREV_NUMBER = 'prev_number';
const NEXT_WORDS = 'next_words';
const NEXT_NUMBER = 'next_number';
const PREV_IS_AD = 'prev_is_ad';
const NEXT_IS_AD = 'next_is_ad';
/**
* Create an object of the class.
*
* @param string $html HTML string.
* @param array $options Options.
* @return object Object of this class.
*/
public static function from_html( $html, $options ) {
$libxml_previous_state = libxml_use_internal_errors( true );
$dom = new DOMDocument( '1.0', 'UTF-8' );
$success = $dom->loadHtml( '<!DOCTYPE html><html><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><body>' . $html );
libxml_clear_errors();
libxml_use_internal_errors( $libxml_previous_state );
$xpath = new DOMXPath( $dom );
return new static( $dom, $xpath, $options );
}
/**
* Constructor.
*
* @param DOMDocument $dom DOMDocument object.
* @param DOMXPath $xpath DOMXpath object.
* @param array $options Options.
*/
public function __construct( DOMDocument $dom, DOMXPath $xpath, array $options ) {
$this->dom = $dom;
$this->xpath = $xpath;
$this->options = array_merge( $this->default_options, $options );
}
/**
* Prepare HTML for parsing.
*
* @param array $items Existing selected items.
* @return string $r String with injected patterns.
*/
private function prepare_for_parsing( $items ) {
$expr = $this->get_expression_for_existing_ads();
$existing = $this->xpath->query( $expr );
$created = [];
// Excel existing ads.
foreach ( $existing as $node ) {
$start = $this->dom->createTextNode( self::START_EXISTING_AD );
$node->parentNode->insertBefore( $start, $node ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$created[] = $start;
$end = $this->dom->createTextNode( self::END_EXISTING_AD );
$node->parentNode->insertBefore( $end, $node->nextSibling ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$created[] = $end;
}
// Excel items, e.g. paragraphs.
foreach ( $items as $node ) {
$point = $this->dom->createTextNode( self::INSERTION_POINT );
$created[] = $point;
if ( $this->options['before'] ) {
$node->parentNode->insertBefore( $point, $node ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
} else {
$node->parentNode->insertBefore( $point, $node->nextSibling ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
}
}
// Select all text nodes.
$nodes = $this->xpath->query( '//text()[not(parent::script or parent::style)]' );
$r = '';
foreach ( $nodes as $n ) {
$r .= $n->data;
}
foreach ( $created as $node ) {
$node->parentNode->removeChild( $node ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
}
return $r;
}
/**
* Prepare the array that contains calculated amount of words between items.
*
* @param str $parts Post text splited by regexp.
* @return $r Word amounts between items.
*/
protected function prepare_words_between( $parts ) {
$r = [];
$prev_is_ad = false;
$doing_ad = 0;
$prev_text = [];
$prev_ip = false;
foreach ( $parts as $part ) {
if ( self::START_EXISTING_AD === $part ) {
if ( $prev_ip ) {
$r = $this->add_after_insertion_point( $r, $prev_text, true );
$prev_ip = false;
}
$prev_text = [];
$prev_is_ad = true;
++$doing_ad;
} elseif ( self::END_EXISTING_AD === $part ) {
--$doing_ad;
} elseif ( self::INSERTION_POINT === $part ) {
if ( $prev_ip ) {
$r = $this->add_after_insertion_point( $r, $prev_text );
$prev_ip = false;
}
$data = [
self::PREV_NUMBER => self::calc_words( $prev_text ),
self::PREV_IS_AD => $prev_is_ad,
];
if ( $this->options['debug'] ) {
$data[ self::PREV_WORDS ] = $prev_text;
}
$r[] = $data;
$prev_text = [];
$prev_is_ad = false;
$prev_ip = true;
} else {
$prev_text[] = self::prepare_text( $part );
}
}
if ( $prev_ip ) {
$r = $this->add_after_insertion_point( $r, $prev_text );
$prev_ip = false;
}
return $r;
}
/**
* Add words after insertion points.
*
* @param array $r Word amounts between items.
* @param array $prev_text Word amounts between items.
* @param bool $next_is_ad Whether or an ad is placed after the point.
* @return $r Word amounts between items.
*/
private function add_after_insertion_point( $r, $prev_text, $next_is_ad = false ) {
$el = array_pop( $r );
$el[ self::NEXT_NUMBER ] = self::calc_words( $prev_text );
$el[ self::NEXT_IS_AD ] = $next_is_ad;
if ( $this->options['debug'] ) {
$el[ self::NEXT_WORDS ] = $prev_text;
}
$r[] = $el;
return $r;
}
/**
* Get amount of words between items.
*
* @param string/array $items An array of `DOMElement`s or `XPath` query.
* @return array
*/
public function get_words_between( $items ) {
if ( is_string( $items ) ) {
$items = iterator_to_array( $this->xpath->query( $items ) );
}
$text = $this->prepare_for_parsing( $items );
$parts = preg_split( self::SPLIT_REGEXP, $text, -1, PREG_SPLIT_DELIM_CAPTURE );
$this->words_between = $this->prepare_words_between( $parts );
return $this->words_between;
}
/**
* Calculate new offsets.
*
* They may be shifted to provide the minimum amount of words before and after or deleted,
* if shifting is not possible
*
* @param array $offsets Existing offsets.
* @param string/array $items An array of `DOMElement`s or `XPath` query.
* @return array $offsets New offsets.
*/
public function calc_offsets( array $offsets, $items ) {
$words_between = $this->get_words_between( $items );
$from_bottom = $this->options['paragraph_select_from_bottom'];
if ( $from_bottom ) {
rsort( $offsets );
} else {
sort( $offsets );
}
$new_offsets = [];
$this->previous_offset = false;
$this->debug( '///// debug start: ' . implode( ', ', $offsets ) . ' /////' );
foreach ( $offsets as $k => $offset ) {
if ( $from_bottom ) {
$new_offset = $this->calc_offset_from_bottom( $offset );
} else {
$new_offset = $this->calc_offset( $offset );
}
if ( false === $new_offset ) {
break;
}
$new_offsets[] = $new_offset;
}
$this->debug( '///// debug end /////' );
return $new_offsets;
}
/**
* Calculate offset.
*
* @param int $offset Offset.
*
* @return int/false New shifted offset or false.
*/
private function calc_offset( $offset ) {
$l = count( $this->words_between );
if ( false !== $this->previous_offset ) {
$offset = max( $offset, $this->previous_offset + 1 );
}
$this->debug( "///// offset: $offset /////" );
for ( $i = $offset; $i < $l; $i++ ) {
if ( ! isset( $this->words_between[ $i ] ) ) {
continue;
}
$prev_num = 0;
$next_num = 0;
$prev_has_ad = false;
for ( $j = $i; $j >= 0; $j-- ) {
if ( $this->options['debug'] ) {
$this->debug( implode( ' / ', $this->words_between[ $j ][ self::PREV_WORDS ] ) );
}
$prev_num += $this->words_between[ $j ][ self::PREV_NUMBER ];
if ( $this->words_between[ $j ][ self::PREV_IS_AD ] ) {
$prev_has_ad = true;
break;
}
}
$this->debug( $i . ': prev: ' . $prev_num );
if ( $prev_num < $this->options['words_between_repeats']
// Check if this is the first item and there are no ads before it.
&& ( $this->options['require_before_first'] || $prev_has_ad || false !== $this->previous_offset ) ) {
continue;
}
$next_has_ad = false;
for ( $k = $i; $k < $l; $k++ ) {
$next_num += $this->words_between[ $k ][ self::NEXT_NUMBER ];
if ( $this->words_between[ $k ][ self::NEXT_IS_AD ] ) {
$next_has_ad = true;
break;
}
}
if ( $next_num < $this->options['words_between_repeats']
// Check if there are no ads after the last item.
&& ( $this->options['require_after_last'] || $next_has_ad ) ) {
continue;
}
$this->previous_offset = $i;
$this->words_between[ $i ][ self::NEXT_IS_AD ] = true;
if ( isset( $this->words_between[ $i + 1 ] ) ) {
$this->words_between[ $i + 1 ][ self::PREV_IS_AD ] = true;
}
$this->debug( "found $i" );
return $i;
}
return false;
}
/**
* Calculate offset from bottom.
*
* @param int $offset Offset.
* @return int/false New shifted offset or false.
*/
private function calc_offset_from_bottom( $offset ) {
$l = count( $this->words_between );
if ( false !== $this->previous_offset ) {
$offset = min( $offset, $this->previous_offset - 1 );
}
$this->debug( "///// offset: $offset /////" );
for ( $i = $offset; $i >= 0; $i-- ) {
if ( ! isset( $this->words_between[ $i ] ) ) {
continue;
}
$prev_num = 0;
$next_num = 0;
$prev_has_ad = false;
for ( $j = $i; $j < $l; $j++ ) {
if ( $this->options['debug'] ) {
$this->debug( implode( ' / ', $this->words_between[ $j ][ self::NEXT_WORDS ] ) );
}
$prev_num += $this->words_between[ $j ][ self::NEXT_NUMBER ];
if ( $this->words_between[ $j ][ self::NEXT_IS_AD ] ) {
$prev_has_ad = true;
break;
}
}
$this->debug( $i . ': prev: ' . $prev_num );
if ( $prev_num < $this->options['words_between_repeats']
// Check if this is the first item and there are no ads before it.
&& ( $this->options['require_before_first'] || $prev_has_ad || false !== $this->previous_offset ) ) {
continue;
}
$next_has_ad = false;
for ( $k = $i; $k >= 0; $k-- ) {
$next_num += $this->words_between[ $k ][ self::NEXT_NUMBER ];
if ( $this->words_between[ $k ][ self::PREV_IS_AD ] ) {
$next_has_ad = true;
break;
}
}
if ( $next_num < $this->options['words_between_repeats']
// Check if there are no ads after the last item.
&& ( $this->options['require_after_last'] || $next_has_ad ) ) {
continue;
}
$this->previous_offset = $i;
$this->words_between[ $i ][ self::PREV_IS_AD ] = true;
if ( isset( $this->words_between[ $i - 1 ] ) ) {
$this->words_between[ $i - 1 ][ self::NEXT_IS_AD ] = true;
}
$this->debug( "found $i" );
return $i;
}
return false;
}
/**
* Get xpath expression for selecting existing ads.
*
* @return string XPath expression.
*/
protected function get_expression_for_existing_ads() {
$expr = [
// The assumption is that a `div` that has a class starting with the frontend prefix is ad.
"//div[@class and contains(concat(' ', normalize-space(@class), ' '), ' %s')]",
// Waiting for consent ads (Privacy module): `<script type="text/plain" data-tcf="waiting-for-consent" data-id="..." data-bid="..."`.
"//comment()[contains(.,'data-tcf=\"waiting-for-consent')]",
];
return sprintf(
implode( ' | ', $expr ),
sanitize_html_class( wp_advads()->get_frontend_prefix() )
);
}
/**
* Prepare text for counting words.
*
* @param str $text Text.
* @return str $text Text.
*/
protected static function prepare_text( $text ) {
$text = normalize_whitespace( $text );
$text = str_replace( "\n", ' ', $text );
// Replace punctuation.
$text = preg_replace( '/[.(),;:!?%#$¿\'"_+=\\/-]+/', '', $text );
return $text;
}
/**
* Calculate words.
*
* @param str $text Text.
* @return int Word count.
*/
protected static function calc_words( $text ) {
if ( is_array( $text ) ) {
$text = implode( ' ', $text );
}
$r = count( preg_split( '/\ +/', $text, -1, PREG_SPLIT_NO_EMPTY ) );
return $r;
}
/**
* Print info for debugging.
*
* @param str $str String.
*/
protected function debug( $str ) {
if ( $this->options['debug'] ) {
echo "\n" . esc_html( $str );
}
}
/**
* Check if 'Before Content' placement can be used.
*
* @return bool
*/
public function can_inject_before_content_placement() {
$query = '(' . $this->get_expression_for_existing_ads() . ')[1]';
$existing = $this->xpath->query( $query );
$existing = iterator_to_array( $existing );
if ( $existing ) {
$last = end( $existing );
// Select all text before the first ad.
$texts = $this->xpath->query( './/preceding::text()[not(parent::script or parent::style)]', $last );
} else {
// Select all text.
$texts = $this->xpath->query( '//text()[not(parent::script or parent::style)]' );
}
$texts = iterator_to_array( $texts );
$l = 0;
foreach ( $texts as $text ) {
$l += $this->calc_words( $this->prepare_text( $text->data ) );
}
return $l >= $this->options['words_between_repeats'];
}
/**
* Check if 'After Content' placement can be used.
*
* @return bool
*/
public function can_inject_after_content_placement() {
// Insert a node to guarantee that there is a following node after the last ad.
if ( isset( $this->dom->documentElement ) && isset( $this->dom->documentElement->lastChild ) ) {
$last_node = $this->dom->createTextNode( '/' );
$this->dom->documentElement->lastChild->appendChild( $last_node );
}
// Select following nodes of the ads.
$query = $this->get_expression_for_existing_ads() . '/following::node()[1]';
$existing = $this->xpath->query( $query );
$existing = iterator_to_array( $existing );
if ( $existing ) {
$last = end( $existing );
// Select all text before the first ad.
$texts = $this->xpath->query( './/following::text()[not(parent::script or parent::style)]', $last );
$texts = iterator_to_array( $texts );
if ( $last instanceof DOMText ) {
array_unshift( $texts, $last );
}
} else {
// Select all text.
$texts = $this->xpath->query( '//text()[not(parent::script or parent::style)]' );
$texts = iterator_to_array( $texts );
}
$l = 0;
foreach ( $texts as $text ) {
$l += $this->calc_words( $this->prepare_text( $text->data ) );
}
return $l >= $this->options['words_between_repeats'];
}
}

View File

@@ -0,0 +1,132 @@
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
use AdvancedAds\Framework\Utilities\Params;
/**
* Compatibility fixes with other plugins.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
*/
/**
* Handles compatibility with various plugins and themes.
*/
class Advanced_Ads_Pro_Compatibility {
/**
* Constructor
*/
public function __construct() {
add_action( 'after_setup_theme', [ $this, 'after_setup_theme' ] );
// Set WPML Language.
// Note: the "Language filtering for AJAX operations" feature of WPML does not work
// because it sets cookie later then our ajax requests are sent.
if (
wp_doing_ajax() &&
defined( 'ICL_SITEPRESS_VERSION' ) &&
! empty( Params::request( 'wpml_lang' ) )
) {
do_action( 'wpml_switch_language', Params::request( 'wpml_lang' ) );
}
// Weglot plugin.
if ( function_exists( 'weglot_get_current_full_url' ) ) {
add_filter( 'advanced-ads-pro-display-condition-url-string', [ $this, 'weglot_get_current_full_url' ], 0 );
}
// Gravity forms plugin.
add_action( 'wp_loaded', [ $this, 'gravity_forms_init' ] );
}
/**
* After the theme is loaded.
*/
public function after_setup_theme() {
// Newspaper theme.
if ( defined( 'TD_THEME_NAME' ) && 'Newspaper' === TD_THEME_NAME ) {
$options = get_option( 'td_011' );
// Check if lazy load is enabled (non-existent key or '').
if ( empty( $options['tds_animation_stack'] ) ) {
add_filter( 'advanced-ads-ad-image-tag-style', [ $this, 'newspaper_theme_disable_lazy_load' ] );
}
}
}
/**
* Newspaper theme: disable lazy load of the theme to prevent conflict with cache-busting/lazy-load of the Pro add-on.
*
* @param string $style Styles.
*
* @return string
*/
public function newspaper_theme_disable_lazy_load( $style ) {
$style .= 'opacity: 1 !important;';
return $style;
}
/**
* Weglot plugin: Get the current full url that contains a lauguage.
*
* @param string $url_parameter Current URI string.
*
* @return string The modified URL parameter.
*/
public function weglot_get_current_full_url( $url_parameter ) {
if ( wp_doing_ajax() ) {
return $url_parameter;
}
$url_parsed = wp_parse_url( weglot_get_current_full_url() );
$url_parameter = $url_parsed['path'];
if ( isset( $url_parsed['query'] ) ) {
$url_parameter .= '?' . $url_parsed['query'];
}
return $url_parameter;
}
/**
* Gravity Forms plugin: Do JS initialization
*
* @return void
*/
public function gravity_forms_init() {
if ( is_admin() || ! function_exists( 'gravity_form_enqueue_scripts' ) ) {
return;
}
$has_ajaxcb_placement = false;
$gravity_form_ads = wp_advads_ad_query(
[
's' => '[gravityform id=',
'meta_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'key' => 'allow_shortcodes',
'value' => '1',
'compare' => 'LIKE',
],
]
)->posts;
if ( empty( $gravity_form_ads ) ) {
return;
}
foreach ( wp_advads_get_placements() as $placement ) {
if ( 'on' === $placement->get_prop( 'cache-busting' ) ) {
$has_ajaxcb_placement = true;
break;
}
}
if ( ! $has_ajaxcb_placement ) {
return;
}
foreach ( $gravity_form_ads as $gravity_form_ad ) {
if ( preg_match( '#gravityform id="([0-9]+)".*#', $gravity_form_ad->post_content, $form_ids ) ) {
gravity_form_enqueue_scripts( $form_ids[1], true );
}
}
}
}

View File

@@ -0,0 +1,98 @@
<?php // phpcs:ignore WordPress.Files.FileName
use AdvancedAds\Framework\Utilities\Params;
/**
* Utils class for Advanced Ads Pro.
*/
class Advanced_Ads_Pro_Utils {
/**
* Generate unique wrapper id
*
* @return string
*/
public static function generate_wrapper_id() {
static $count = 0;
return wp_advads()->get_frontend_prefix() . ( ++$count ) . wp_rand();
}
/**
* Checks if a blog exists and is not marked as deleted.
*
* @link http://wordpress.stackexchange.com/q/138300/73
*
* @param int $blog_id Blog ID.
* @param int $site_id Site ID.
*
* @return bool
*/
public static function blog_exists( $blog_id, $site_id = 0 ) {
global $wpdb;
static $cache = [];
$site_id = absint( $site_id );
if ( 0 === $site_id ) {
$site_id = get_current_site()->id;
}
if ( empty( $cache[ $site_id ] ) ) {
// we do not test large sites.
if ( wp_is_large_network() ) {
return true;
}
$query = $wpdb->prepare( "SELECT `blog_id` FROM $wpdb->blogs WHERE site_id = %d AND deleted = 0", $site_id );
$result = $wpdb->get_col( $query ); // phpcs:ignore
// Make sure the array is always filled with something.
$cache[ $site_id ] = empty( $result ) ? [ 'checked' ] : $result;
}
return in_array( (string) $blog_id, $cache[ $site_id ], true );
}
/**
* Convert a value to non-negative integer.
*
* @param mixed $maybeint Data you wish to have converted to a non-negative integer.
* @param int $min A minimum.
* @param int $max A maximum.
*
* @return int A non-negative integer.
*/
public static function absint( $maybeint, $min = null, $max = null ) {
$int = abs( (int) $maybeint );
if ( null !== $min && $int < $min ) {
return $min;
}
if ( null !== $max && $int > $max ) {
return $max;
}
return $int;
}
/**
* Retrieve a post given a post ID
*
* Used for display conditions during `advads_ad_select` (ajax ads).
*
* @return array|WP_Post|null
*/
public static function get_post() {
$post_object = get_post();
if (
! $post_object
&& wp_doing_ajax()
&& isset( $_REQUEST['action'], $_REQUEST['theId'], $_REQUEST['isSingular'] )
&& Params::request( 'action' ) === 'advads_ad_select'
&& Params::request( 'isSingular' )
) {
$post_object = get_post( Params::request( 'theId', 0, FILTER_VALIDATE_INT ) );
}
return $post_object;
}
}

View File

@@ -0,0 +1,90 @@
<?php
/**
* Admin Ad List Table.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Admin;
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Admin Ad List Table.
*/
class Ad_List_Table implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_filter( 'advanced-ads-ad-columns', [ $this, 'add_columns' ] );
add_action( 'advanced-ads-ad-render-columns', [ $this, 'render_columns' ], 10, 2 );
add_filter( 'default_hidden_columns', [ $this, 'set_hidden_columns' ], 10, 2 );
add_filter( 'advanced_ads_optional_filters', [ $this, 'add_optional_filters' ] );
}
/**
* Add columns to view.
*
* @param array $columns columns.
*
* @return array
*/
public function add_columns( array $columns ): array {
$columns['ad_displayonce'] = __( 'Display Once', 'advanced-ads-pro' );
return $columns;
}
/**
* Render columns.
*
* @param string $column column name.
* @param Ad $ad Ad instance.
*
* @return void
*/
public function render_columns( string $column, $ad ): void {
if ( 'ad_displayonce' === $column ) {
$display_once = $ad->get_prop( 'once_per_page' ) ?? false;
include AA_PRO_ABSPATH . 'views/admin/tables/ads/column-displayonce.php';
}
}
/**
* Hidden columns.
*
* @param array $hidden default hidden columns.
* @param WP_Screen $screen current screen.
*
* @return array|mixed
*/
public function set_hidden_columns( $hidden, $screen ) {
if ( isset( $screen->id ) && 'edit-advanced_ads' === $screen->id ) {
$hidden = array_merge( $hidden, [ 'ad_displayonce' ] );
}
return $hidden;
}
/**
* Adds an optional filter to the list of filters.
*
* @param array $optional_filters The array of optional filters.
*
* @return array
*/
public function add_optional_filters( $optional_filters ): array {
$optional_filters['all_displayonce'] = __( 'Display Once', 'advanced-ads-pro' );
return $optional_filters;
}
}

View File

@@ -0,0 +1,136 @@
<?php
/**
* Backend helper for the AdSense fallback
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
*/
namespace AdvancedAds\Pro\Admin;
use AdvancedAds\Options;
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
/**
* Backend class
*/
class Adsense implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'advanced-ads-gadsense-extra-ad-param', [ $this, 'ad_parameter_setting' ], 10, 3 );
add_action( 'advanced-ads-ad-pre-save', [ $this, 'save_ad_callback' ], 10, 2 );
add_action( 'admin_init', [ $this, 'add_setting_field' ] );
}
/**
* Add setting field on the AdSense tab of the settings page
*
* @return void
*/
public function add_setting_field() {
add_settings_field(
'adsense-fallback',
__( 'Fallback ad/group', 'advanced-ads-pro' ),
[ $this, 'render_adsense_tab_setting' ],
'advanced-ads-adsense-settings-page',
'advanced_ads_adsense_setting_section'
);
}
/**
* Save fallback option when saving the ad
*
* @param Ad $ad the ad.
* @param array $post_data content of $_POST (without the post content).
*
* @return void
*/
public function save_ad_callback( $ad, $post_data ) {
if ( empty( $post_data['adsense_fallback'] ) ) {
return;
}
preg_match( '/^none|default|ad_.+|group_.+|$/', $post_data['adsense_fallback'], $m );
if ( empty( $m[0] ) ) {
return;
}
$ad->set_prop( 'adsense_fallback', $m[0] );
}
/**
* Add fallback settings on the ad edit page
*
* @param array $params extra template parameters.
* @param string $content ad content.
* @param Ad $ad the ad.
*
* @return void
*/
public function ad_parameter_setting( $params, $content, $ad ) {
$this->render_fallback_setting( false, $ad );
}
/**
* Render setting field on the AdSense tab of the settings page
*
* @return void
*/
public function render_adsense_tab_setting() {
$this->render_fallback_setting( true );
}
/**
* Render the fallback setting markup
*
* @param bool $is_global whether it's the global fallback or not.
* @param Ad $ad the adsense ad.
*
* @return void
*/
public function render_fallback_setting( $is_global, $ad = null ) {
$global_fallback = \AdvancedAds\Pro\Adsense::get_global_fallback();
$global_fallback_object = \AdvancedAds\Pro\Adsense::get_global_fallback_object();
$fallback = $is_global ? $global_fallback : \AdvancedAds\Pro\Adsense::get_fallback( $ad );
$cache_busting = Options::instance()->get( 'pro.cache-busting' );
$items = self::get_fallback_items();
require_once AA_PRO_ABSPATH . 'views/admin/tables/ads/adsense-fallback.php';
}
/**
* List of available fallback ad or group for an unfilled AdSense ad
*
* @return array[]
*/
private static function get_fallback_items() {
static $result;
if ( null !== $result ) {
return $result;
}
$result = [
'ads' => [],
'groups' => [],
];
foreach ( wp_advads_get_all_ads() as $ad ) {
if ( ! $ad->is_type( 'adsense' ) && $ad->is_status( 'publish' ) ) {
$result['ads'][ $ad->get_id() ] = $ad;
}
}
foreach ( wp_advads_get_all_groups() as $group ) {
$result['groups'][ $group->get_id() ] = $group;
}
return $result;
}
}

View File

@@ -0,0 +1,107 @@
<?php
/**
* Admin Duplicate Placement.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Admin;
use AdvancedAds\Constants;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Utilities\Conditional;
defined( 'ABSPATH' ) || exit;
/**
* Admin Duplicate Placement.
*/
class Duplicate_Placement implements Integration_Interface {
/**
* Admin action
*
* @var string
*/
private const ACTION = 'advanced_ads_duplicate_placement';
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_filter( 'post_row_actions', [ $this, 'action_link' ], 10, 2 );
add_action( 'admin_action_' . self::ACTION, [ $this, 'duplicate' ] );
}
/**
* Duplicate a placement
*
* @return void
*/
public function duplicate() {
// Early bail!!
$placement_id = Params::get( 'id', 0, FILTER_VALIDATE_INT );
if ( ! $placement_id ) {
return;
}
check_admin_referer( 'duplicate-placement-' . $placement_id );
if ( self::ACTION !== Params::get( 'action' ) || ! Conditional::user_cap( 'advanced_ads_manage_placements' ) ) {
return;
}
$placement = wp_advads_get_placement( $placement_id );
if ( ! $placement && wp_safe_redirect( admin_url( 'edit.php?post_type=advanced_ads_plcmnt' ) ) ) {
exit;
}
$copy_suffix = ' (' . _x( 'copy', 'noun', 'advanced-ads-pro' ) . ' at ' . current_time( 'Y-m-d H:i:s' ) . ')';
$new_placement = clone $placement;
$new_placement->set_id( null ); // reset the ID to save as a new placement.
$new_placement->set_title( $new_placement->get_title() . $copy_suffix );
$new_placement->set_item( '' );
$new_placement->set_prop( 'test_id', null );
$new_id = $new_placement->save();
if ( wp_safe_redirect( admin_url( 'edit.php?post_type=advanced_ads_plcmnt#modal-placement-edit-' . $new_id ) ) ) {
exit;
}
}
/**
* Add duplicate links on placement table
*
* @param array $actions existing actions.
* @param \WP_Post $post the post.
*
* @return array the modified actions list.
*/
public function action_link( $actions, $post ) {
if ( Constants::POST_TYPE_PLACEMENT !== $post->post_type || ! Conditional::user_cap( 'advanced_ads_manage_placements' ) ) {
return $actions;
}
$action = add_query_arg(
[
'action' => self::ACTION,
'id' => $post->ID,
],
admin_url( 'admin.php' )
);
$actions['duplicate-placement'] = sprintf(
'<a href="%s" title="%s">%s</a>',
wp_nonce_url( $action, 'duplicate-placement-' . $post->ID ),
esc_attr__( 'Create a copy of this placement', 'advanced-ads-pro' ),
esc_html__( 'Duplicate', 'advanced-ads-pro' )
);
return $actions;
}
}

View File

@@ -0,0 +1,131 @@
<?php
/**
* Admin Groups Listing Page.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Admin;
use AdvancedAds\Constants;
use AdvancedAds\Abstracts\Group;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Duplicate Group.
*/
class Group_Duplication implements Integration_Interface {
private const DUPLICATE_ACTION_NAME = 'advanced_ads_duplicate_group';
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_filter( Constants::TAXONOMY_GROUP . '_row_actions', [ $this, 'render_row_actions' ], 10, 2 );
add_action( 'admin_action_' . self::DUPLICATE_ACTION_NAME, [ $this, 'duplicate_group' ] );
}
/**
* Renders the row actions for a group.
*
* @param array $actions An array of row actions.
* @param Group $group The group object.
*
* @return array The modified array of row actions.
*/
public function render_row_actions( $actions, $group ): array {
$actions = $this->render_duplicate_link( $actions, $group );
return $actions;
}
/**
* Renders the duplicate link for a group.
*
* This function checks if the current user has the permission to edit the group.
* If the user has the permission, it generates a duplicate link for the group.
* The link allows the user to create a copy of the group.
*
* @param array $actions An array of row actions for the group.
* @param Group $group The group object.
*
* @return array The updated array of actions with the duplicate link.
*/
public function render_duplicate_link( $actions, $group ): array {
if ( ! Group::can_current_user_edit_group() ) {
return $actions;
}
$action = add_query_arg(
[
'action' => self::DUPLICATE_ACTION_NAME,
'group_id' => $group->get_id(),
],
admin_url( 'admin.php' )
);
$actions['duplicate-group'] = sprintf(
'<a href="%s" title="%s">%s</a>',
wp_nonce_url( $action, 'duplicate-group-' . $group->get_id() ),
esc_attr__( 'Create a copy of this group', 'advanced-ads-pro' ),
esc_html__( 'Duplicate', 'advanced-ads-pro' )
);
return $actions;
}
/**
* Duplicate a group.
*
* This method duplicates a group by creating a copy of it with a "(copy)" suffix in the title.
* If the duplication is successful, the user is redirected to the edit page of the new group.
* If the duplication fails, the user is redirected to the groups listing page.
*
* @since 2.26.0
*/
public function duplicate_group(): void {
// Early bail!!
$group_id = Params::get( 'group_id', 0, FILTER_VALIDATE_INT );
if ( ! $group_id ) {
return;
}
check_admin_referer( 'duplicate-group-' . $group_id );
if (
self::DUPLICATE_ACTION_NAME !== Params::get( 'action' ) ||
! Group::can_current_user_edit_group()
) {
return;
}
$group = wp_advads_get_group( $group_id );
if ( ! $group && wp_safe_redirect( admin_url( 'admin.php?page=advanced-ads-groups' ) ) ) {
exit;
}
$copy_suffix = ' (' . _x( 'copy', 'noun', 'advanced-ads-pro' ) . ' at ' . current_time( 'Y-m-d H:i:s' ) . ')';
$new_group = clone $group;
$new_group->set_id( null ); // reset the ID to save as a new group.
$new_group->set_name( $new_group->get_name() . $copy_suffix );
$new_group_id = $new_group->save();
// Set the taxonomy relationships on the new object.
$ad_ids = get_objects_in_term( $group_id, Constants::TAXONOMY_GROUP );
foreach ( $ad_ids as $ad_id ) {
wp_set_object_terms( $ad_id, $new_group_id, Constants::TAXONOMY_GROUP, true );
}
if ( wp_safe_redirect( $new_group->get_edit_link() ) ) {
exit;
}
}
}

View File

@@ -0,0 +1,88 @@
<?php
/**
* Bulk edit for placements
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
*/
namespace AdvancedAds\Pro\Admin\Placements;
use AdvancedAds\Options;
use AdvancedAds\Abstracts\Placement;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Placements Bulk Edit.
*/
class Bulk_Edit implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'advanced-ads-placement-bulk-edit-fields', [ $this, 'add_bulk_edit_fields' ] );
add_filter( 'advanced-ads-placement-bulk-edit-has-change', [ $this, 'bulk_edit_has_changes' ] );
add_filter( 'advanced-ads-placement-bulk-edit-save', [ $this, 'save_bulk_edit' ] );
}
/**
* Add the bulk edit inputs
*
* @return void
*/
public function add_bulk_edit_fields(): void {
include_once AA_PRO_ABSPATH . 'views/admin/placements/bulk-edit.php';
}
/**
* Check if bulk edit fields have changes.
*
* @param bool $has_change whether some ads have been changed.
*
* @return bool
*/
public function bulk_edit_has_changes( $has_change ): bool {
$cache_busting = Params::get( 'cache_busting' );
$lazy_loading = Params::get( 'lazy_loading' );
$cache_busting_empty = Params::get( 'cache_busting_empty' );
if ( ! empty( $cache_busting ) || ! empty( $lazy_loading ) || ! empty( $cache_busting_empty ) ) {
$has_change = true;
}
return $has_change;
}
/**
* Save changes made during bulk edit
*
* @param Placement $placement current placement being saved.
*
* @return Placement
*/
public function save_bulk_edit( $placement ): Placement {
$cache_busting = Params::get( 'cache_busting' );
$lazy_loading = Params::get( 'lazy_loading' );
$cache_busting_empty = Params::get( 'cache_busting_empty', false, FILTER_VALIDATE_BOOLEAN );
if ( ! empty( $cache_busting ) ) {
$placement->set_prop( 'cache-busting', $cache_busting );
}
if ( ! empty( $lazy_loading ) ) {
$placement->set_prop( 'lazy_load', $lazy_loading );
}
if ( ! empty( $cache_busting_empty ) ) {
$placement->set_prop( 'cache_busting_empty', 1 === $cache_busting_empty );
}
return $placement;
}
}

View File

@@ -0,0 +1,261 @@
<?php
/**
* Frontend helper for the AdSense fallback
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
*/
namespace AdvancedAds\Pro;
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Abstracts\Group;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
use AdvancedAds\Options;
/**
* Frontend class
*/
class Adsense implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
$cache_busting = Options::instance()->get( 'pro.cache-busting' );
// No CB, abort.
if ( empty( $cache_busting['enabled'] ) ) {
return;
}
add_filter( 'advanced-ads-cache-busting-item', [ $this, 'filter_cache_busting_item' ], 10, 2 );
add_filter( 'advanced-ads-pro-ad-needs-backend-request', [ $this, 'filter_cache_busting_method' ], 10, 2 );
add_filter( 'advanced-ads-output-wrapper-options', [ $this, 'filter_ad_wrapper_options' ], 10, 2 );
add_action( 'wp_loaded', [ $this, 'inline_script' ] );
}
/**
* Add inline JS variables
*
* @return void
*/
public function inline_script() {
wp_advads()->json->add(
[
'adHealthNotice' =>
[
'enabled' => \Advanced_Ads_Ad_Health_Notices::notices_enabled(),
'pattern' => sprintf(
/* translators: ad title. */
__( 'AdSense fallback was loaded for empty AdSense ad "%s"', 'advanced-ads-pro' ),
'[ad_title]'
),
],
]
);
}
/**
* Add CSS classes with data about AdSense fallback
*
* @param array $options wrapper options.
* @param Ad $ad the current ad being rendered.
*
* @return mixed
*/
public function filter_ad_wrapper_options( $options, $ad ) {
// Not AdSense, abort.
if ( ! $ad->is_type( 'adsense' ) ) {
return $options;
}
// No CB, abort.
$placement = $ad->get_root_placement();
if ( ! $placement || 'off' === $placement->get_prop( 'cache-busting' ) ) {
return $options;
}
$fallback = $this->get_practical_fallback( $ad );
// No fallback, abort.
if ( 'none' === $fallback ) {
return $options;
}
$options['class'] = [ 'gas_fallback-ad_' . $ad->get_id() . '-' . $fallback ];
return $options;
}
/**
* Add cache busting item data about fallbacks if needed
*
* @param array $item current item data.
* @param array $ad_arguments ad arguments and method.
*
* @return mixed
*/
public function filter_cache_busting_item( $item, $ad_arguments ) {
if ( empty( $ad_arguments['args']['item'] ) ) {
return $item;
}
$exploded_item = explode( '_', $ad_arguments['args']['item'] );
$placement_item = [
'type' => $exploded_item[0],
'id' => (int) $exploded_item[1],
];
// Works only on a placement with an AdSense ad for now (not on an AdSense ad within a group).
if ( 'ad' !== $placement_item['type'] ) {
return $item;
}
$ad = wp_advads_get_ad( $placement_item['id'] );
if ( ! $ad || ! $ad->is_type( 'adsense' ) ) {
return $item;
}
$fallback = $this->get_practical_fallback( $ad );
if ( ! $fallback || 'none' === $fallback ) {
return $item;
}
if ( empty( $placement_item['adsense_fallback'] ) || ! is_array( $placement_item['adsense_fallback'] ) ) {
$item['adsense_fallback'] = [];
}
$item['adsense_fallback'][ 'ad_' . $placement_item['id'] ] = $fallback;
$exploded = explode( '_', $fallback );
$fallback_object = 'ad' === $exploded[0]
? wp_advads_get_ad( (int) $exploded[1] )
: wp_advads_get_group( (int) $exploded[1] );
$item['ads'] += $this->collect_passive_ad_info( $fallback_object );
if ( is_a_group( $fallback_object ) ) {
$item['adsense_fallback_group_info'] = [
'id' => $fallback_object->get_id(),
'name' => $fallback_object->get_title(),
'weights' => $fallback_object->get_ad_weights(),
'type' => $fallback_object->get_type(),
'ordered_ad_ids' => $fallback_object->get_ordered_ad_ids(),
'ad_count' => $fallback_object->get_ad_count(),
];
}
return $item;
}
/**
* Collect passive cache busting info of all fallback ads.
*
* @param Ad|Group $fallback fallback obejct.
*
* @return array[]
*/
private function collect_passive_ad_info( $fallback ) {
$ads = [];
if ( is_an_ad( $fallback ) ) {
return [ $fallback->get_id() => \Advanced_Ads_Pro_Module_Cache_Busting::get_instance()->get_passive_cb_for_ad( $fallback ) ];
}
if ( is_a_group( $fallback ) ) {
foreach ( $fallback->get_ads() as $id => $ad ) {
$ads[ $id ] = \Advanced_Ads_Pro_Module_Cache_Busting::get_instance()->get_passive_cb_for_ad( $ad );
}
}
return $ads;
}
/**
* Force CB if the ad normally doesn't need CB
*
* @param string $method CB method.
* @param Ad $ad the ad.
*
* @return string
*/
public function filter_cache_busting_method( $method, $ad ) {
// "static" = NO CB. If it's not "static" no need to intervene or not AdSense, abort.
if ( 'static' !== $method || ! $ad->is_type( 'adsense' ) ) {
return $method;
}
// If still "static" and we have a fallback, force CB.
return $this->get_practical_fallback( $ad ) ? 'passive' : $method;
}
/**
* Get the actual fallback item for an AdSense ad (ad level if any otherwise return the site wide fallback)
*
* @param Ad $ad the current ad.
*
* @return false|string
*/
public function get_practical_fallback( $ad ) {
$ad_level_fallback = self::get_fallback( $ad );
if ( 'none' === $ad_level_fallback ) {
return false;
}
$global_fallback = self::get_global_fallback();
if ( 'default' === $ad_level_fallback ) {
return 'none' === $global_fallback ? false : $global_fallback;
}
return $ad_level_fallback;
}
/**
* Get the fallback CB item for a given AdSense ad
*
* @param Ad $ad the adsense ad.
*
* @return string
*/
public static function get_fallback( $ad ) {
$fallback = $ad->get_prop( 'adsense_fallback' );
return $fallback ?? 'default';
}
/**
* Get the site wide fallback CB item object
*
* @return Ad|Group|bool `false` if no global fallback found.
*/
public static function get_global_fallback_object() {
$item = self::get_global_fallback();
if ( ! $item || 'none' === $item ) {
return false;
}
$exploded_item = explode( '_', $item );
return 'ad' === $exploded_item[0]
? wp_advads_get_ad( (int) $exploded_item[1] )
: wp_advads_get_group( (int) $exploded_item[1] );
}
/**
* Get the global fallback item
*
* @return string
*/
public static function get_global_fallback() {
$fallback = Options::instance()->get( 'adsense.adsense_fallback' );
return $fallback ?? 'none';
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* Assets manager handles the registration of stylesheets and scripts required for plugin functionality.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
*/
namespace AdvancedAds\Pro;
use AdvancedAds\Framework\Assets_Registry;
/**
* Pro's assets manager
*/
class Assets_Manager extends Assets_Registry {
/**
* Base URL for plugin local assets.
*
* @return string
*/
public function get_base_url(): string {
return AA_PRO_BASE_URL;
}
/**
* Prefix to use in handle to make it unique.
*
* @return string
*/
public function get_prefix(): string {
return AA_PRO_SLUG;
}
/**
* Version for plugin local assets.
*
* @return string
*/
public function get_version(): string {
return AAP_VERSION;
}
/**
* Register assets
*
* @return void
*/
public function register_assets(): void {
}
}

View File

@@ -0,0 +1,198 @@
<?php
/**
* The class is responsible for locating and loading the autoloader file used in the plugin.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro;
defined( 'ABSPATH' ) || exit;
/**
* Autoloader.
*/
class Autoloader {
/**
* Hold autoloader.
*
* @var mixed
*/
private $autoloader;
/**
* Main instance
*
* Ensure only one instance is loaded or can be loaded.
*
* @return Autoloader
*/
public static function get(): Autoloader {
static $instance;
if ( null === $instance ) {
$instance = new Autoloader();
}
return $instance;
}
/**
* Get hold autoloader.
*
* @return mixed
*/
public function get_autoloader() {
return $this->autoloader;
}
/**
* Get plugin directory.
*
* @return string
*/
public function get_directory(): string {
return dirname( AAP_FILE );
}
/**
* Runs this initializer.
*
* @return void
*/
public function initialize(): void {
$locate = $this->locate();
if ( ! $locate ) {
add_action( 'admin_notices', [ $this, 'missing_autoloader' ] );
return;
}
$this->autoloader = require $locate;
}
/**
* Locate the autoload file
*
* This function searches for the autoload file in the packages directory and vendor directory.
*
* @return bool|string
*/
private function locate() {
$directory = $this->get_directory();
$packages = $directory . '/packages/autoload.php';
$vendors = $directory . '/vendor/autoload.php';
$is_debug = $this->is_debug() || 'local' === $this->get_environment_type();
if ( is_readable( $packages ) && ( ! $is_debug || ! is_readable( $vendors ) ) ) {
return $packages;
}
if ( is_readable( $vendors ) ) {
return $vendors;
}
return false;
}
/**
* If the autoloader is missing, add an admin notice.
*
* @return void
*/
protected function missing_autoloader(): void {
?>
<div class="notice notice-error">
<p>
<?php
printf(
/* translators: 1: is a link to a support document. 2: closing link */
esc_html__( 'Your installation of Advanced Ads is incomplete. If you installed Advanced Ads from GitHub, %1$s please refer to this document%2$s to set up your development environment.', 'advanced-ads-pro' ),
'<a href="' . esc_url( 'https://github.com/advanced-ads/advanced-ads/wiki/How-to-set-up-development-environment' ) . '" target="_blank" rel="noopener noreferrer">',
'</a>'
);
?>
</p>
</div>
<?php
}
/**
* Retrieves the current environment type.
*
* @return string
*/
public function get_environment_type(): string {
return function_exists( 'wp_get_environment_type' )
? wp_get_environment_type()
: $this->get_environment_type_fallback();
}
/**
* Retrieves the current environment type.
*
* @return string
*/
private function get_environment_type_fallback(): string {
static $current_env = '';
if ( ! defined( 'WP_RUN_CORE_TESTS' ) && $current_env ) {
return $current_env;
}
$wp_environments = [
'local',
'development',
'staging',
'production',
];
// Add a note about the deprecated WP_ENVIRONMENT_TYPES constant.
if ( defined( 'WP_ENVIRONMENT_TYPES' ) && function_exists( '_deprecated_argument' ) ) {
if ( function_exists( '__' ) ) {
/* translators: %s: WP_ENVIRONMENT_TYPES */
$message = sprintf( __( 'The %s constant is no longer supported.', 'advanced-ads-pro' ), 'WP_ENVIRONMENT_TYPES' );
} else {
$message = sprintf( 'The %s constant is no longer supported.', 'WP_ENVIRONMENT_TYPES' );
}
_deprecated_argument(
'define()',
'5.5.1',
$message // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
);
}
// Check if the environment variable has been set, if `getenv` is available on the system.
if ( function_exists( 'getenv' ) ) {
$has_env = getenv( 'WP_ENVIRONMENT_TYPE' );
if ( false !== $has_env ) {
$current_env = $has_env;
}
}
// Fetch the environment from a constant, this overrides the global system variable.
if ( defined( 'WP_ENVIRONMENT_TYPE' ) && WP_ENVIRONMENT_TYPE ) {
$current_env = WP_ENVIRONMENT_TYPE;
}
// Make sure the environment is an allowed one, and not accidentally set to an invalid value.
if ( ! in_array( $current_env, $wp_environments, true ) ) {
$current_env = 'production';
}
return $current_env;
}
/**
* Is WordPress debug mode enabled
*
* @return bool
*/
private function is_debug(): bool {
return defined( 'WP_DEBUG' ) && WP_DEBUG;
}
}

View File

@@ -0,0 +1,243 @@
<?php
/**
* Bootstrap.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro;
defined( 'ABSPATH' ) || exit;
/**
* Bootstrap.
*/
class Bootstrap {
/**
* Whether the plugin has been started.
*
* @var bool
*/
private $done = false;
/**
* Get singleton instance.
*
* @return Bootstrap
*/
public static function get() {
static $instance = null;
if ( null === $instance ) {
$instance = new self();
}
return $instance;
}
/**
* Start the plugin.
*
* @return void
*/
public function start(): void {
// Early bail!!
if ( $this->done ) {
return;
}
add_action( 'plugins_loaded', [ $this, 'halt_code' ], -10 );
$this->done = true;
}
/**
* Halt code for new release.
*
* @return void
*/
public function halt_code(): void {
global $advads_halt_notices;
if ( ! isset( $advads_halt_notices ) ) {
$advads_halt_notices = [];
}
// Early bail!!
if ( ! defined( 'ADVADS_VERSION' ) ) {
$advads_halt_notices[] = __( 'Advanced Ads - Pro', 'advanced-ads-pro' );
add_action( 'all_admin_notices', [ $this, 'print_missing_notices' ] );
add_action( 'after_plugin_row_' . plugin_basename( AAP_FILE ), [ $this, 'missing_row' ] );
return;
}
if ( version_compare( ADVADS_VERSION, '2.0.0', '<' ) ) {
$advads_halt_notices[] = __( 'Advanced Ads - Pro', 'advanced-ads-pro' );
add_action( 'all_admin_notices', [ $this, 'print_halt_notices' ] );
add_action( 'after_plugin_row_' . plugin_basename( AAP_FILE ), [ $this, 'compatible_row' ] );
return;
}
// Start it.
add_action( 'advanced-ads-loaded', 'wp_advads_pro' );
}
/**
* Display missing notice row.
*
* @return void
*/
public function missing_row(): void {
$this->print_row(
__( '<strong>Advanced Ads - Pro</strong> requires the <strong>Advanced Ads free</strong> plugin to be installed and activated on your site.', 'advanced-ads-pro' )
. '&nbsp;' . $this->get_button( 'button-link' )
);
}
/**
* Display compatible notice row.
*
* @return void
*/
public function compatible_row(): void {
$this->print_row(
sprintf(
/* translators: %s: Plugin name */
__( 'Your version of <strong>Advanced Ads - Pro</strong> is incompatible with <strong>Advanced Ads %s</strong> and has been deactivated. Please update the plugin to the latest version.', 'advanced-ads-pro' ),
ADVADS_VERSION
)
);
}
/**
* Display missing notices.
*
* @return void
*/
public function print_missing_notices(): void {
global $advads_halt_notices;
// Early bail!!
if ( 'plugins' === get_current_screen()->base || empty( $advads_halt_notices ) ) {
return;
}
$this->print_notices(
__( 'Important Notice', 'advanced-ads-pro' ),
__( 'Addons listed below requires the <strong><a href="https://wpadvancedads.com/?utm_source=advanced-ads&utm_medium=link&utm_campaign=activate-advanced-ads-pro" target="_blank">Advanced Ads</a></strong> plugin to be installed and activated on your site.', 'advanced-ads-pro' )
. '&nbsp;' . $this->get_button(),
$advads_halt_notices
);
$advads_halt_notices = [];
}
/**
* Display halt notices.
*
* @return void
*/
public function print_halt_notices(): void {
global $advads_halt_notices;
// Early bail!!
if ( 'plugins' === get_current_screen()->base || empty( $advads_halt_notices ) ) {
return;
}
$this->print_notices(
__( 'Important Notice', 'advanced-ads-pro' ),
sprintf(
/* translators: %s: Plugin name */
__( 'Your versions of the Advanced Ads addons listed below are incompatible with <strong>Advanced Ads %s</strong> and have been deactivated. Please update the plugin to the latest version.', 'advanced-ads-pro' ),
ADVADS_VERSION
),
$advads_halt_notices
);
$advads_halt_notices = [];
}
/**
* Display notices.
*
* @param string $title Title.
* @param string $description Description.
* @param array $notices Notices.
*
* @return void
*/
private function print_notices( $title, $description, $notices ): void {
?>
<div class="notice notice-error">
<h2><?php echo esc_html( $title ); ?></h2>
<p>
<?php echo wp_kses_post( $description ); ?>
</p>
<h3><?php esc_html_e( 'The following addons are affected:', 'advanced-ads-pro' ); ?></h3>
<ul>
<?php foreach ( $notices as $notice ) : ?>
<li><strong><?php echo esc_html( $notice ); ?></strong></li>
<?php endforeach; ?>
</ul>
</div>
<?php
}
/**
* Print row.
*
* @param string $message Message to print.
*
* @return void
*/
private function print_row( $message ): void {
?>
<tr class="active">
<td colspan="5" class="plugin-update colspanchange">
<div class="notice notice-error notice-alt inline update-message">
<p>
<?php echo wp_kses_post( $message ); ?>
</p>
</div>
</td>
</tr>
<?php
}
/**
* Get button.
*
* @param string $type Button type.
*
* @return string
*/
private function get_button( $type = 'button-primary' ): string {
$plugins = get_plugins();
$link = wp_nonce_url(
self_admin_url( 'update.php?action=install-plugin&plugin=advanced-ads' ),
'install-plugin_advanced-ads'
);
$text = __( 'Install Now', 'advanced-ads-pro' );
// Check if Advanced Ads is already installed.
if ( isset( $plugins['advanced-ads/advanced-ads.php'] ) ) {
$link = wp_nonce_url(
self_admin_url( 'plugins.php?action=activate&plugin=advanced-ads/advanced-ads.php' ),
'activate-plugin_advanced-ads/advanced-ads.php'
);
$text = __( 'Activate Now', 'advanced-ads-pro' );
}
return sprintf(
'<a class="button %s" href="%s">%s</a>',
$type,
$link,
$text
);
}
}

View File

@@ -0,0 +1,165 @@
<?php
/**
* The plugin bootstrap.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro;
use Advanced_Ads_Pro;
use AdvancedAds\Pro\Admin;
use AdvancedAds\Framework\Loader;
use AdvancedAds\Pro\Placements\Placement_Types;
defined( 'ABSPATH' ) || exit;
/**
* Plugin.
*/
class Plugin extends Loader {
/**
* Main instance
*
* Ensure only one instance is loaded or can be loaded.
*
* @return Plugin
*/
public static function get(): Plugin {
static $instance;
if ( null === $instance ) {
$instance = new Plugin();
$instance->setup();
}
return $instance;
}
/**
* Get plugin version
*
* @return string
*/
public function get_version(): string {
return AAP_VERSION;
}
/**
* Bootstrap plugin.
*
* @return void
*/
private function setup(): void {
$this->define_constants();
$this->includes();
$this->includes_admin();
/**
* Old loading strategy
*
* TODO: need to remove it in future.
*/
Advanced_Ads_Pro::get_instance();
add_action( 'init', [ $this, 'load_textdomain' ] );
$this->load();
}
/**
* Define Advanced Ads constant
*
* @return void
*/
private function define_constants(): void {
$this->define( 'AA_PRO_ABSPATH', dirname( AAP_FILE ) . '/' );
$this->define( 'AA_PRO_BASENAME', plugin_basename( AAP_FILE ) );
$this->define( 'AA_PRO_BASE_URL', plugin_dir_url( AAP_FILE ) );
$this->define( 'AA_PRO_SLUG', 'advanced-ads-pro' );
// Deprecated Constants.
/**
* AAP_PATH
*
* @deprecated 2.26.0 use AA_PRO_ABSPATH now.
*/
define( 'AAP_PATH', AA_PRO_ABSPATH );
/**
* AAP_BASE_PATH
*
* @deprecated 2.26.0 use AA_PRO_ABSPATH now.
*/
define( 'AAP_BASE_PATH', AA_PRO_ABSPATH );
/**
* AAP_BASE
*
* @deprecated 2.26.0 use AA_PRO_BASENAME now.
*/
define( 'AAP_BASE', AA_PRO_BASENAME );
/**
* AAP_BASE_URL
*
* @deprecated 2.26.0 use AA_PRO_BASE_URL now.
*/
define( 'AAP_BASE_URL', AA_PRO_BASE_URL );
/**
* AAP_SLUG
*
* @deprecated 2.26.0 use AA_PRO_SLUG now.
*/
define( 'AAP_SLUG', AA_PRO_SLUG );
}
/**
* Load the plugin text domain for translation.
*
* @return void
*/
public function load_textdomain(): void {
$locale = apply_filters( 'plugin_locale', determine_locale(), 'advanced-ads-pro' );
unload_textdomain( 'advanced-ads-pro' );
if ( false === load_textdomain( 'advanced-ads-pro', WP_LANG_DIR . '/plugins/advanced-ads-pro-' . $locale . '.mo' ) ) {
load_textdomain( 'advanced-ads-pro', WP_LANG_DIR . '/advanced-ads-pro/advanced-ads-pro-' . $locale . '.mo' );
}
load_plugin_textdomain( 'advanced-ads-pro', false, dirname( AA_PRO_BASENAME ) . '/languages' );
}
/**
* Includes core files used in admin and on the frontend.
*
* @return void
*/
private function includes(): void {
$this->register_integration( Adsense::class );
$this->register_integration( Assets_Manager::class, 'registry' );
$this->register_integration( Placement_Types::class );
}
/**
* Includes the necessary files for the admin section of the plugin.
*
* @return void
*/
private function includes_admin(): void {
// Early bail!!
if ( ! is_admin() ) {
return;
}
$this->register_initializer( Upgrades::class, 'upgrades' );
$this->register_integration( Admin\Adsense::class );
$this->register_integration( Admin\Ad_List_Table::class );
$this->register_integration( Admin\Group_Duplication::class );
$this->register_integration( Admin\Duplicate_Placement::class );
$this->register_integration( Admin\Placements\Bulk_Edit::class );
}
}

View File

@@ -0,0 +1,87 @@
<?php
/**
* Upgrades.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro;
use AdvancedAds\Framework\Updates;
use AdvancedAds\Framework\Interfaces\Initializer_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Upgrades.
*/
class Upgrades extends Updates implements Initializer_Interface {
const DB_VERSION = '1.1';
/**
* Get updates that need to run.
*
* @since 2.26.0
*
* @return array
*/
public function get_updates(): array {
return [
'1.1' => 'upgrade-1-1.php',
];
}
/**
* Get folder path
*
* @since 2.26.0
*
* @return string
*/
public function get_folder(): string {
return AA_PRO_ABSPATH . 'upgrades/';
}
/**
* Get plugin version number
*
* @since 2.26.0
*
* @return string
*/
public function get_version(): string {
return self::DB_VERSION;
}
/**
* Get plugin option name.
*
* @since 2.26.0
*
* @return string
*/
public function get_option_name(): string {
return 'advanced_ads_pro_db_version';
}
/**
* Runs this initializer.
*
* @since 2.26.0
*
* @return void
*/
public function initialize(): void {
// Force run the upgrades.
$is_first_time = empty( $this->get_installed_version() );
$this->hooks();
if ( $is_first_time ) {
update_option( $this->get_option_name(), '1.0.0' );
add_action( 'admin_init', [ $this, 'perform_updates' ] );
}
}
}

View File

@@ -0,0 +1,4 @@
<?php
/**
* Silence is golden.
*/

View File

@@ -0,0 +1,190 @@
<?php
/**
* The class provides plugin installation routines.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Installation;
use Advanced_Ads_Pro;
defined( 'ABSPATH' ) || exit;
/**
* Install.
*/
class Install {
/**
* Runs this initializer.
*
* @return void
*/
public function initialize(): void {
if ( null !== AAP_FILE ) {
register_activation_hook( AAP_FILE, [ $this, 'activation' ] );
register_deactivation_hook( AAP_FILE, [ $this, 'deactivation' ] );
add_action( 'wp_initialize_site', [ $this, 'initialize_site' ] );
}
}
/**
* Activation routine.
*
* @param bool $network_wide Whether the plugin is being activated network-wide.
*
* @return void
*/
public function activation( $network_wide = false ): void {
register_uninstall_hook( AAP_FILE, [ static::class, 'uninstall' ] );
if ( ! is_multisite() || ! $network_wide ) {
$this->activate();
return;
}
$this->network_activate_deactivate( 'activate' );
}
/**
* Deactivation routine.
*
* @param bool $network_wide Whether the plugin is being activated network-wide.
*
* @return void
*/
public function deactivation( $network_wide = false ): void {
if ( ! is_multisite() || ! $network_wide ) {
$this->deactivate();
return;
}
$this->network_activate_deactivate( 'deactivate' );
}
/**
* Fired when a new site is activated with a WPMU environment.
*
* @param WP_Site $site The new site's object.
*
* @return void
*/
public function initialize_site( $site ): void {
switch_to_blog( $site->blog_id );
$this->activate();
restore_current_blog();
}
/**
* Run network-wide activation/deactivation of the plugin.
*
* @param string $action Action to perform.
*
* @return void
*/
private function network_activate_deactivate( $action ): void {
global $wpdb;
$site_ids = self::get_sites();
if ( empty( $site_ids ) ) {
return;
}
foreach ( $site_ids as $site_id ) {
switch_to_blog( $site_id );
$this->$action();
restore_current_blog();
}
}
/**
* Get network sites
*
* @return array|int
*/
public static function get_sites() {
global $wpdb;
return get_sites(
[
'archived' => 0,
'spam' => 0,
'deleted' => 0,
'network_id' => $wpdb->siteid,
'fields' => 'ids',
]
);
}
/**
* Plugin activation callback.
*
* @return void
*/
protected function activate(): void {
add_role(
'advanced_ads_admin',
__( 'Ad Admin', 'advanced-ads-pro' ),
[
'read' => true,
'advanced_ads_manage_options' => true,
'advanced_ads_see_interface' => true,
'advanced_ads_edit_ads' => true,
'advanced_ads_manage_placements' => true,
'advanced_ads_place_ads' => true,
'upload_files' => true,
'unfiltered_html' => true,
]
);
add_role(
'advanced_ads_manager',
__( 'Ad Manager', 'advanced-ads-pro' ),
[
'read' => true,
'advanced_ads_see_interface' => true,
'advanced_ads_edit_ads' => true,
'advanced_ads_manage_placements' => true,
'advanced_ads_place_ads' => true,
'upload_files' => true,
'unfiltered_html' => true,
]
);
add_role(
'advanced_ads_user',
__( 'Ad User', 'advanced-ads-pro' ),
[
'read' => true,
'advanced_ads_place_ads' => true,
]
);
Advanced_Ads_Pro::get_instance()
->enable_placement_test_emails();
}
/**
* Plugin deactivation callback.
*
* @return void
*/
protected function deactivate(): void {
remove_role( 'advanced_ads_admin' );
remove_role( 'advanced_ads_manager' );
remove_role( 'advanced_ads_user' );
Advanced_Ads_Pro::get_instance()
->disable_placement_test_emails();
}
/**
* Plugin uninstall callback.
*
* @return void
*/
public static function uninstall(): void {}
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* This class represents the "Above Headline" placement
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements;
use AdvancedAds\Abstracts\Placement;
use AdvancedAds\Interfaces\Placement_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Above Headline.
*/
class Placement_Above_Headline extends Placement implements Placement_Interface {
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* This class represents the "Archive Pages" placement
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements;
use AdvancedAds\Abstracts\Placement;
use AdvancedAds\Interfaces\Placement_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Archive Pages.
*/
class Placement_Archive_Page extends Placement implements Placement_Interface {
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* This class represents the "Background Ad" placement.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements;
use AdvancedAds\Abstracts\Placement;
use AdvancedAds\Interfaces\Placement_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Background Ad.
*/
class Placement_Background_Ad extends Placement implements Placement_Interface {
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* This class represents the "BuddyPress" & "BuddyBoss" Comment placement
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements;
use AdvancedAds\Abstracts\Placement;
use AdvancedAds\Interfaces\Placement_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Placements Placement Bbpress Comment.
*/
class Placement_Bbpress_Comment extends Placement implements Placement_Interface {
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* This class represents the "BuddyPress" & "BuddyBoss" Static placement
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements;
use AdvancedAds\Abstracts\Placement;
use AdvancedAds\Interfaces\Placement_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Placements Placement Bbpress Static.
*/
class Placement_Bbpress_Static extends Placement implements Placement_Interface {
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* This class represents the "BuddyPress" & "BuddyBoss" placement
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements;
use AdvancedAds\Abstracts\Placement;
use AdvancedAds\Interfaces\Placement_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Placements Placement Buddypress.
*/
class Placement_Buddypress extends Placement implements Placement_Interface {
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* This class represents the "Post Content Middle" placement
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements;
use AdvancedAds\Abstracts\Placement;
use AdvancedAds\Interfaces\Placement_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Post Content Middle.
*/
class Placement_Content_Middle extends Placement implements Placement_Interface {
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* This class represents the "Post Content Random" placement
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements;
use AdvancedAds\Abstracts\Placement;
use AdvancedAds\Interfaces\Placement_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Post Content Random.
*/
class Placement_Content_Random extends Placement implements Placement_Interface {
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* This class represents the "Post Custom Position" placement
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements;
use AdvancedAds\Abstracts\Placement;
use AdvancedAds\Interfaces\Placement_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Post Custom Position.
*/
class Placement_Custom_Position extends Placement implements Placement_Interface {
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* This class represents the "Server" placement
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements;
use AdvancedAds\Abstracts\Placement;
use AdvancedAds\Interfaces\Placement_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Server.
*/
class Placement_Server extends Placement implements Placement_Interface {
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Placements type manager
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements;
use Advanced_Ads_Pro;
use AdvancedAds\Pro\Placements\Types;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Placements Placement Types.
*/
class Placement_Types implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'advanced-ads-placement-types-manager', [ $this, 'add_pro_placements' ] );
}
/**
* Add pro placement to list of placements
*
* @since 2.26.0
*
* @param Types $manager Placement types manager.
*
* @return void
*/
public function add_pro_placements( $manager ) {
$manager->register_type( Types\Above_Headline::class );
$manager->register_type( Types\Archive_Pages::class );
$manager->register_type( Types\Content_Middle::class );
$manager->register_type( Types\Content_Random::class );
$manager->register_type( Types\Custom_Position::class );
$manager->register_type( Types\Background_Ad::class );
$options = Advanced_Ads_Pro::get_instance()->get_options();
if ( ! empty( $options['ad-server']['enabled'] ) ) {
$manager->register_type( Types\Server::class );
}
if ( class_exists( 'bbPress', false ) ) {
$manager->register_type( Types\Bbpress_Static::class );
$manager->register_type( Types\Bbpress_Comment::class );
}
if ( class_exists( 'BuddyPress', false ) ) {
$manager->register_type( Types\Buddypress::class );
}
}
}

View File

@@ -0,0 +1,101 @@
<?php
/**
* Placements Types Above Headline.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements\Types;
use Advanced_Ads_Pro;
use AdvancedAds\Interfaces\Placement_Type;
use AdvancedAds\Abstracts\Placement_Type as Base;
use AdvancedAds\Pro\Placements\Placement_Above_Headline;
defined( 'ABSPATH' ) || exit;
/**
* Placements Types Above Headline.
*/
class Above_Headline extends Base implements Placement_Type {
/**
* Get the unique identifier (ID) of the placement type.
*
* @return string The unique ID of the placement type.
*/
public function get_id(): string {
return 'post_above_headline';
}
/**
* Get the class name of the object as a string.
*
* @return string
*/
public function get_classname(): string {
return Placement_Above_Headline::class;
}
/**
* Get the title or name of the placement type.
*
* @return string The title of the placement type.
*/
public function get_title(): string {
return __( 'Above Headline', 'advanced-ads-pro' );
}
/**
* Get a description of the placement type.
*
* @return string The description of the placement type.
*/
public function get_description(): string {
return __( 'Above the main headline on the page (&lt;h1&gt;).', 'advanced-ads-pro' );
}
/**
* Check if this placement type requires premium.
*
* @return bool True if premium is required; otherwise, false.
*/
public function is_premium(): bool {
return false;
}
/**
* Get the URL for upgrading to this placement type.
*
* @return string The upgrade URL for the placement type.
*/
public function get_image(): string {
return AA_PRO_BASE_URL . 'modules/inject-content/assets/img/content-above-headline.png';
}
/**
* Get order number for this placement type.
*
* @return int The order number.
*/
public function get_order(): int {
return 90;
}
/**
* Get options for this placement type.
*
* @return array The options array.
*/
public function get_options(): array {
return $this->apply_filter_on_options(
[
'show_position' => true,
'uses_the_content' => true,
'show_lazy_load' => 'php' === Advanced_Ads_Pro::get_instance()->get_options()['placement-positioning'],
]
);
}
}

View File

@@ -0,0 +1,99 @@
<?php
/**
* Placements Types Archive Pages.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements\Types;
use AdvancedAds\Interfaces\Placement_Type;
use AdvancedAds\Abstracts\Placement_Type as Base;
use AdvancedAds\Pro\Placements\Placement_Archive_Page;
defined( 'ABSPATH' ) || exit;
/**
* Placements Types Archive Pages.
*/
class Archive_Pages extends Base implements Placement_Type {
/**
* Get the unique identifier (ID) of the placement type.
*
* @return string The unique ID of the placement type.
*/
public function get_id(): string {
return 'archive_pages';
}
/**
* Get the class name of the object as a string.
*
* @return string
*/
public function get_classname(): string {
return Placement_Archive_Page::class;
}
/**
* Get the title or name of the placement type.
*
* @return string The title of the placement type.
*/
public function get_title(): string {
return __( 'Post Lists', 'advanced-ads-pro' );
}
/**
* Get a description of the placement type.
*
* @return string The description of the placement type.
*/
public function get_description(): string {
return __( 'Display the ad between posts on post lists, e.g. home, archives, search etc.', 'advanced-ads-pro' );
}
/**
* Check if this placement type requires premium.
*
* @return bool True if premium is required; otherwise, false.
*/
public function is_premium(): bool {
return false;
}
/**
* Get the URL for upgrading to this placement type.
*
* @return string The upgrade URL for the placement type.
*/
public function get_image(): string {
return AA_PRO_BASE_URL . 'modules/inject-content/assets/img/post-list.png';
}
/**
* Get order number for this placement type.
*
* @return int The order number.
*/
public function get_order(): int {
return 105;
}
/**
* Get options for this placement type.
*
* @return array The options array.
*/
public function get_options(): array {
return $this->apply_filter_on_options(
[
'show_position' => true,
'show_lazy_load' => true,
]
);
}
}

View File

@@ -0,0 +1,98 @@
<?php
/**
* Placements Types Background Ad.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements\Types;
use AdvancedAds\Interfaces\Placement_Type;
use AdvancedAds\Abstracts\Placement_Type as Base;
use AdvancedAds\Pro\Placements\Placement_Background_Ad;
defined( 'ABSPATH' ) || exit;
/**
* Placements Types Background Ad.
*/
class Background_Ad extends Base implements Placement_Type {
/**
* Get the unique identifier (ID) of the placement type.
*
* @return string The unique ID of the placement type.
*/
public function get_id(): string {
return 'background';
}
/**
* Get the class name of the object as a string.
*
* @return string
*/
public function get_classname(): string {
return Placement_Background_Ad::class;
}
/**
* Get the title or name of the placement type.
*
* @return string The title of the placement type.
*/
public function get_title(): string {
return __( 'Background Ad', 'advanced-ads-pro' );
}
/**
* Get a description of the placement type.
*
* @return string The description of the placement type.
*/
public function get_description(): string {
return __( 'Background of the website behind the main wrapper.', 'advanced-ads-pro' );
}
/**
* Check if this placement type requires premium.
*
* @return bool True if premium is required; otherwise, false.
*/
public function is_premium(): bool {
return false;
}
/**
* Get the URL for upgrading to this placement type.
*
* @return string The upgrade URL for the placement type.
*/
public function get_image(): string {
return AA_PRO_BASE_URL . 'modules/background-ads/assets/img/background.png';
}
/**
* Get order number for this placement type.
*
* @return int The order number.
*/
public function get_order(): int {
return 80;
}
/**
* Get options for this placement type.
*
* @return array The options array.
*/
public function get_options(): array {
return $this->apply_filter_on_options(
[
'allowed_ad_types' => [ 'image', 'plain' ],
]
);
}
}

View File

@@ -0,0 +1,94 @@
<?php
/**
* Placements Types Bbpress Comment.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements\Types;
use AdvancedAds\Interfaces\Placement_Type;
use AdvancedAds\Abstracts\Placement_Type as Base;
use AdvancedAds\Pro\Placements\Placement_Bbpress_Comment;
defined( 'ABSPATH' ) || exit;
/**
* Bbpress Comment.
*/
class Bbpress_Comment extends Base implements Placement_Type {
/**
* Get the unique identifier (ID) of the placement type.
*
* @return string The unique ID of the placement type.
*/
public function get_id(): string {
return 'bbPress comment';
}
/**
* Get the class name of the object as a string.
*
* @return string
*/
public function get_classname(): string {
return Placement_Bbpress_Comment::class;
}
/**
* Get the title or name of the placement type.
*
* @return string The title of the placement type.
*/
public function get_title(): string {
return __( 'bbPress Reply Content', 'advanced-ads-pro' );
}
/**
* Get a description of the placement type.
*
* @return string The description of the placement type.
*/
public function get_description(): string {
return __( 'Display ads in bbPress replies.', 'advanced-ads-pro' );
}
/**
* Check if this placement type requires premium.
*
* @return bool True if premium is required; otherwise, false.
*/
public function is_premium(): bool {
return false;
}
/**
* Get the URL for upgrading to this placement type.
*
* @return string The upgrade URL for the placement type.
*/
public function get_image(): string {
return AA_PRO_BASE_URL . 'modules/bbpress/assets/img/bbpress-reply.png';
}
/**
* Get order number for this placement type.
*
* @return int The order number.
*/
public function get_order(): int {
return 33;
}
/**
* Get options for this placement type.
*
* @return array The options array.
*/
public function get_options(): array {
return $this->apply_filter_on_options( [] );
}
}

View File

@@ -0,0 +1,94 @@
<?php
/**
* Placements Types Bbpress Static.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements\Types;
use AdvancedAds\Interfaces\Placement_Type;
use AdvancedAds\Abstracts\Placement_Type as Base;
use AdvancedAds\Pro\Placements\Placement_Bbpress_Static;
defined( 'ABSPATH' ) || exit;
/**
* Bbpress Static.
*/
class Bbpress_Static extends Base implements Placement_Type {
/**
* Get the unique identifier (ID) of the placement type.
*
* @return string The unique ID of the placement type.
*/
public function get_id(): string {
return 'bbPress static';
}
/**
* Get the class name of the object as a string.
*
* @return string
*/
public function get_classname(): string {
return Placement_Bbpress_Static::class;
}
/**
* Get the title or name of the placement type.
*
* @return string The title of the placement type.
*/
public function get_title(): string {
return __( 'bbPress Static Content', 'advanced-ads-pro' );
}
/**
* Get a description of the placement type.
*
* @return string The description of the placement type.
*/
public function get_description(): string {
return __( 'Display ads on bbPress related pages.', 'advanced-ads-pro' );
}
/**
* Check if this placement type requires premium.
*
* @return bool True if premium is required; otherwise, false.
*/
public function is_premium(): bool {
return false;
}
/**
* Get the URL for upgrading to this placement type.
*
* @return string The upgrade URL for the placement type.
*/
public function get_image(): string {
return AA_PRO_BASE_URL . 'modules/bbpress/assets/img/bbpress-static.png';
}
/**
* Get order number for this placement type.
*
* @return int The order number.
*/
public function get_order(): int {
return 32;
}
/**
* Get options for this placement type.
*
* @return array The options array.
*/
public function get_options(): array {
return $this->apply_filter_on_options( [] );
}
}

View File

@@ -0,0 +1,104 @@
<?php
/**
* Placements Types Buddypress.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements\Types;
use AdvancedAds\Interfaces\Placement_Type;
use AdvancedAds\Abstracts\Placement_Type as Base;
use AdvancedAds\Pro\Modules\BuddyPress\BuddyPress as BuddyPress_Main;
use AdvancedAds\Pro\Placements\Placement_Buddypress;
defined( 'ABSPATH' ) || exit;
/**
* Buddypress.
*/
class Buddypress extends Base implements Placement_Type {
/**
* Get the unique identifier (ID) of the placement type.
*
* @return string The unique ID of the placement type.
*/
public function get_id(): string {
return 'buddypress';
}
/**
* Get the class name of the object as a string.
*
* @return string
*/
public function get_classname(): string {
return Placement_Buddypress::class;
}
/**
* Get the title or name of the placement type.
*
* @return string The title of the placement type.
*/
public function get_title(): string {
if ( BuddyPress_Main::is_buddyboss() ) {
return __( 'BuddyBoss Content', 'advanced-ads-pro' );
}
return __( 'BuddyPress Content', 'advanced-ads-pro' );
}
/**
* Get a description of the placement type.
*
* @return string The description of the placement type.
*/
public function get_description(): string {
if ( BuddyPress_Main::is_buddyboss() ) {
return __( 'Display ads on BuddyBoss related pages.', 'advanced-ads-pro' );
}
return __( 'Display ads on BuddyPress related pages.', 'advanced-ads-pro' );
}
/**
* Check if this placement type requires premium.
*
* @return bool True if premium is required; otherwise, false.
*/
public function is_premium(): bool {
return false;
}
/**
* Get the URL for upgrading to this placement type.
*
* @return string The upgrade URL for the placement type.
*/
public function get_image(): string {
if ( BuddyPress_Main::is_buddyboss() ) {
return AA_PRO_BASE_URL . 'modules/buddypress/assets/img/buddyboss.png';
}
return AA_PRO_BASE_URL . 'modules/buddypress/assets/img/buddypress-icon.png';
}
/**
* Get order number for this placement type.
*
* @return int The order number.
*/
public function get_order(): int {
return 31;
}
/**
* Get options for this placement type.
*
* @return array The options array.
*/
public function get_options(): array {
return $this->apply_filter_on_options( [ 'placement-display-conditions' => [ 'request_uri', 'buddypress_group' ] ] );
}
}

View File

@@ -0,0 +1,100 @@
<?php
/**
* Placements Types Content Middle.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements\Types;
use AdvancedAds\Interfaces\Placement_Type;
use AdvancedAds\Abstracts\Placement_Type as Base;
use AdvancedAds\Pro\Placements\Placement_Content_Middle;
defined( 'ABSPATH' ) || exit;
/**
* Placements Types Content Middle.
*/
class Content_Middle extends Base implements Placement_Type {
/**
* Get the unique identifier (ID) of the placement type.
*
* @return string The unique ID of the placement type.
*/
public function get_id(): string {
return 'post_content_middle';
}
/**
* Get the class name of the object as a string.
*
* @return string
*/
public function get_classname(): string {
return Placement_Content_Middle::class;
}
/**
* Get the title or name of the placement type.
*
* @return string The title of the placement type.
*/
public function get_title(): string {
return __( 'Content Middle', 'advanced-ads-pro' );
}
/**
* Get a description of the placement type.
*
* @return string The description of the placement type.
*/
public function get_description(): string {
return __( 'In the middle of the main content based on the number of paragraphs.', 'advanced-ads-pro' );
}
/**
* Check if this placement type requires premium.
*
* @return bool True if premium is required; otherwise, false.
*/
public function is_premium(): bool {
return false;
}
/**
* Get the URL for upgrading to this placement type.
*
* @return string The upgrade URL for the placement type.
*/
public function get_image(): string {
return AA_PRO_BASE_URL . 'modules/inject-content/assets/img/content-middle.png';
}
/**
* Get order number for this placement type.
*
* @return int The order number.
*/
public function get_order(): int {
return 95;
}
/**
* Get options for this placement type.
*
* @return array The options array.
*/
public function get_options(): array {
return $this->apply_filter_on_options(
[
'show_position' => true,
'uses_the_content' => true,
'amp' => true,
]
);
}
}

View File

@@ -0,0 +1,100 @@
<?php
/**
* Placements Types Content Random.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements\Types;
use AdvancedAds\Interfaces\Placement_Type;
use AdvancedAds\Abstracts\Placement_Type as Base;
use AdvancedAds\Pro\Placements\Placement_Content_Random;
defined( 'ABSPATH' ) || exit;
/**
* Placements Types Content Random.
*/
class Content_Random extends Base implements Placement_Type {
/**
* Get the unique identifier (ID) of the placement type.
*
* @return string The unique ID of the placement type.
*/
public function get_id(): string {
return 'post_content_random';
}
/**
* Get the class name of the object as a string.
*
* @return string
*/
public function get_classname(): string {
return Placement_Content_Random::class;
}
/**
* Get the title or name of the placement type.
*
* @return string The title of the placement type.
*/
public function get_title(): string {
return __( 'Random Paragraph', 'advanced-ads-pro' );
}
/**
* Get a description of the placement type.
*
* @return string The description of the placement type.
*/
public function get_description(): string {
return __( 'After a random paragraph in the main content.', 'advanced-ads-pro' );
}
/**
* Check if this placement type requires premium.
*
* @return bool True if premium is required; otherwise, false.
*/
public function is_premium(): bool {
return false;
}
/**
* Get the URL for upgrading to this placement type.
*
* @return string The upgrade URL for the placement type.
*/
public function get_image(): string {
return AA_PRO_BASE_URL . 'modules/inject-content/assets/img/content-random.png';
}
/**
* Get order number for this placement type.
*
* @return int The order number.
*/
public function get_order(): int {
return 85;
}
/**
* Get options for this placement type.
*
* @return array The options array.
*/
public function get_options(): array {
return $this->apply_filter_on_options(
[
'show_position' => true,
'uses_the_content' => true,
'amp' => true,
]
);
}
}

View File

@@ -0,0 +1,100 @@
<?php
/**
* Placements Types Custom Position.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements\Types;
use Advanced_Ads_Pro;
use AdvancedAds\Interfaces\Placement_Type;
use AdvancedAds\Abstracts\Placement_Type as Base;
use AdvancedAds\Pro\Placements\Placement_Custom_Position;
defined( 'ABSPATH' ) || exit;
/**
* Placements Types Custom Position.
*/
class Custom_Position extends Base implements Placement_Type {
/**
* Get the unique identifier (ID) of the placement type.
*
* @return string The unique ID of the placement type.
*/
public function get_id(): string {
return 'custom_position';
}
/**
* Get the class name of the object as a string.
*
* @return string
*/
public function get_classname(): string {
return Placement_Custom_Position::class;
}
/**
* Get the title or name of the placement type.
*
* @return string The title of the placement type.
*/
public function get_title(): string {
return __( 'Custom Position', 'advanced-ads-pro' );
}
/**
* Get a description of the placement type.
*
* @return string The description of the placement type.
*/
public function get_description(): string {
return __( 'Attach the ad to any element in the frontend.', 'advanced-ads-pro' );
}
/**
* Check if this placement type requires premium.
*
* @return bool True if premium is required; otherwise, false.
*/
public function is_premium(): bool {
return false;
}
/**
* Get the URL for upgrading to this placement type.
*
* @return string The upgrade URL for the placement type.
*/
public function get_image(): string {
return AA_PRO_BASE_URL . 'modules/inject-content/assets/img/custom-position.png';
}
/**
* Get order number for this placement type.
*
* @return int The order number.
*/
public function get_order(): int {
return 100;
}
/**
* Get options for this placement type.
*
* @return array The options array.
*/
public function get_options(): array {
return $this->apply_filter_on_options(
[
'show_position' => true ,
'show_lazy_load' => 'php' === Advanced_Ads_Pro::get_instance()->get_options()['placement-positioning'],
]
);
}
}

View File

@@ -0,0 +1,102 @@
<?php
/**
* Placements Types Server.
*
* @package AdvancedAds\Pro
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.26.0
*/
namespace AdvancedAds\Pro\Placements\Types;
defined( 'ABSPATH' ) || exit;
use AdvancedAds\Interfaces\Placement_Type;
use AdvancedAds\Pro\Placements\Placement_Server;
use AdvancedAds\Abstracts\Placement_Type as Base;
/**
* Placements Types Server.
*/
class Server extends Base implements Placement_Type {
/**
* Get the unique identifier (ID) of the placement type.
*
* @return string The unique ID of the placement type.
*/
public function get_id(): string {
return 'server';
}
/**
* Get the class name of the object as a string.
*
* @return string
*/
public function get_classname(): string {
return Placement_Server::class;
}
/**
* Get the title or name of the placement type.
*
* @return string The title of the placement type.
*/
public function get_title(): string {
return __( 'Ad Server', 'advanced-ads-pro' );
}
/**
* Get a description of the placement type.
*
* @return string The description of the placement type.
*/
public function get_description(): string {
return __( 'Display ads on external websites.', 'advanced-ads-pro' );
}
/**
* Check if this placement type requires premium.
*
* @return bool True if premium is required; otherwise, false.
*/
public function is_premium(): bool {
return false;
}
/**
* Get the URL for upgrading to this placement type.
*
* @return string The upgrade URL for the placement type.
*/
public function get_image(): string {
return AA_PRO_BASE_URL . 'modules/ad-server/assets/img/server.png';
}
/**
* Get order number for this placement type.
*
* @return int The order number.
*/
public function get_order(): int {
return 110;
}
/**
* Get options for this placement type.
*
* @return array The options array.
*/
public function get_options(): array {
return $this->apply_filter_on_options(
[
'placement-cache-busting' => false,
'placement-display-conditions' => false,
'placement-visitor-conditions' => false,
'placement-item-alternative' => false,
'placement-tests' => false,
]
);
}
}

View File

@@ -0,0 +1,4 @@
<?php
/**
* Silence is golden.
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More