PluginProbe ʕ •ᴥ•ʔ
Advanced Ads – Ad Manager & AdSense / 1.16.1
Advanced Ads – Ad Manager & AdSense v1.16.1
2.0.23 2.0.22 2.0.21 1.38.0 1.39.0 1.39.1 1.39.2 1.39.3 1.39.4 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.40.0 1.40.1 1.40.2 1.41.0 1.42.0 1.42.1 1.42.2 1.43.0 1.44.0 1.44.1 1.45.0 1.45.1 1.46.0 1.47.0 1.47.1 1.47.2 1.47.3 1.47.4 1.47.5 1.48.0 1.48.1 1.49.0 1.5.0 1.5.0.1 1.5.1 1.5.2 1.5.2.1 1.5.4 1.5.4.1 1.5.5 1.50.0 1.51.0 1.51.1 1.51.2 1.51.3 1.52.0 1.52.1 1.52.2 1.52.3 1.52.4 1.53.0 1.53.1 1.53.2 1.54.0 1.54.1 1.55.0 1.56.0 1.56.1 1.56.2 1.56.3 1.56.4 1.6 1.6.1 1.6.10 1.6.10.1 1.6.10.2 1.6.11 1.6.11.1 1.6.12 1.6.13 1.6.14 1.6.15 1.6.16 1.6.17 1.6.17.1 1.6.17.2 1.6.2 1.6.2.1 1.6.3 1.6.4 1.6.4.1 1.6.5 1.6.6 1.6.6.1 1.6.7 1.6.7.1 1.6.8 1.6.8.1 1.6.8.2 1.6.8.3 1.6.9 1.6.9.1 1.6.9.2 1.6.9.3 1.6.9.4 1.7 1.7.0.1 1.7.0.2 1.7.0.3 1.7.1 1.7.1.1 1.7.1.2 1.7.1.3 1.7.1.4 1.7.1.5 1.7.10 trunk 1.7.11 1.0.1 1.7.12 1.0.2 1.7.13 1.0.3 1.7.14 1.1.0 1.7.15 1.1.1 1.7.16 1.1.2 1.7.17 1.1.3 1.7.18 1.10 1.7.19 1.10.1 1.7.2 1.10.10 1.7.2.1 1.10.11 1.7.20 1.10.12 1.7.21 1.10.2 1.7.22 1.10.3 1.7.23 1.10.4 1.7.24 1.10.5 1.7.25 1.10.6 1.7.3 1.10.7 1.7.4 1.10.8 1.7.4.1 1.10.9 1.7.4.2 1.11 1.7.4.3 1.11.1 1.7.4.4 1.11.2 1.7.4.5 1.12 1.7.5 1.13 1.7.5.1 1.13.1 1.7.6 1.13.2 1.7.7 1.13.3 1.7.8 1.13.4 1.7.9 1.13.5 1.7.9.1 1.13.6 1.7.9.2 1.13.7 1.7.9.3 1.13.8 1.8 1.14 1.8.1 1.14.1 1.8.10 1.14.10 1.8.11 1.14.11 1.8.12 1.14.2 1.8.13 1.14.3 1.8.14 1.14.4 1.8.15 1.14.5 1.8.16 1.14.6 1.8.17 1.14.7 1.8.18 1.14.8 1.8.19 1.14.9 1.8.2 1.15 1.8.20 1.16 1.8.21 1.16.1 1.8.22 1.17 1.8.23 1.17.1 1.8.24 1.17.10 1.8.25 1.17.10-rc.1 1.8.26 1.17.11 1.8.27 1.17.12 1.8.28 1.17.12-rc.1 1.8.29 1.17.2 1.8.3 1.17.3 1.8.30 1.17.4 1.8.4 1.17.5 1.8.5 1.17.6 1.8.6 1.17.7 1.8.7 1.17.8 1.8.8 1.17.9 1.8.9 1.17.9-beta.1 1.9 1.18.0 2.0.0 1.19.0 2.0.1 1.19.1 2.0.10 1.2 2.0.11 1.2.1 2.0.12 1.2.2 2.0.13 1.2.3 2.0.14 1.2.4 2.0.15 1.2.5 2.0.16 1.2.6 2.0.17 1.2.7 2.0.18 1.20.0 2.0.19 1.20.0-rc.1 2.0.2 1.20.0-rc.2 2.0.20 1.20.1 2.0.3 1.20.2 2.0.4 1.20.3 2.0.5 1.21.0 2.0.6 1.21.1 2.0.7 1.22.0 2.0.8 1.22.1 2.0.9 1.22.2 1.23.0 1.23.1 1.23.2 1.24.0 1.24.1 1.24.2 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.29.0 1.29.1 1.3 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.14 1.3.15 1.3.16 1.3.17 1.3.18 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.30.0 1.30.1 1.30.2 1.30.2-rc.1 1.30.3 1.30.4 1.30.4-rc.1 1.30.5 1.31.0 1.31.1 1.32.0 1.32.0-rc.1 1.33.0 1.33.1 1.33.2 1.34.0 1.35.0 1.35.1 1.36.0 1.36.1 1.36.2 1.36.3 1.37.0 1.37.1 1.37.2
advanced-ads / classes / ad_placements.php
advanced-ads / classes Last commit date
EDD_SL_Plugin_Updater.php 6 years ago ad-ajax.php 6 years ago ad-debug.php 8 years ago ad-health-notices.php 6 years ago ad-model.php 8 years ago ad-select.php 9 years ago ad.php 6 years ago ad_ajax_callbacks.php 6 years ago ad_group.php 6 years ago ad_placements.php 6 years ago ad_type_abstract.php 8 years ago ad_type_content.php 6 years ago ad_type_dummy.php 6 years ago ad_type_group.php 8 years ago ad_type_image.php 6 years ago ad_type_plain.php 6 years ago checks.php 6 years ago compatibility.php 6 years ago display-conditions.php 6 years ago filesystem.php 8 years ago frontend_checks.php 6 years ago plugin.php 6 years ago upgrades.php 6 years ago utils.php 6 years ago visitor-conditions.php 6 years ago widget.php 6 years ago
ad_placements.php
813 lines
1 <?php
2
3 /**
4 * Advanced Ads
5 *
6 * @package Advanced_Ads_Placements
7 * @author Thomas Maier <thomas.maier@webgilde.com>
8 * @license GPL-2.0+
9 * @link http://webgilde.com
10 * @copyright 2014 Thomas Maier, webgilde GmbH
11 */
12
13 /**
14 * grouping placements functions
15 *
16 * @since 1.1.0
17 * @package Advanced_Ads_Placements
18 * @author Thomas Maier <thomas.maier@webgilde.com>
19 */
20 class Advanced_Ads_Placements {
21 private static $ads_for_placeholders = array();
22 // temporarily change content during processing
23 private static $replacements = array(
24 'gcse:search' => 'gcse__search', // Google custom search namespaced tags.
25 );
26
27 /**
28 * get placement types
29 *
30 * @since 1.2.1
31 * @return arr $types array with placement types
32 */
33 public static function get_placement_types() {
34 $types = array(
35 'default' => array(
36 'title' => __( 'Manual Placement', 'advanced-ads' ),
37 'description' => __( 'Manual placement to use as function or shortcode.', 'advanced-ads' ),
38 'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/manual.png',
39 'options' => array( 'show_position' => true, 'show_lazy_load' => true, 'amp' => true )
40 ),
41 'header' => array(
42 'title' => __( 'Header Code', 'advanced-ads' ),
43 'description' => __( 'Injected in Header (before closing &lt;/head&gt; Tag, often not visible).', 'advanced-ads' ),
44 'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/header.png'
45 ),
46 'footer' => array(
47 'title' => __( 'Footer Code', 'advanced-ads' ),
48 'description' => __( 'Injected in Footer (before closing &lt;/body&gt; Tag).', 'advanced-ads' ),
49 'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/footer.png',
50 'options' => array( 'amp' => true )
51 ),
52 'post_top' => array(
53 'title' => __( 'Before Content', 'advanced-ads' ),
54 'description' => __( 'Injected before the post content.', 'advanced-ads' ),
55 'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/content-before.png',
56 'options' => array( 'show_position' => true, 'show_lazy_load' => true, 'uses_the_content' => true, 'amp' => true )
57 ),
58 'post_bottom' => array(
59 'title' => __( 'After Content', 'advanced-ads' ),
60 'description' => __( 'Injected after the post content.', 'advanced-ads' ),
61 'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/content-after.png',
62 'options' => array( 'show_position' => true, 'show_lazy_load' => true, 'uses_the_content' => true, 'amp' => true )
63 ),
64 'post_content' => array(
65 'title' => __( 'Content', 'advanced-ads' ),
66 'description' => __( 'Injected into the content. You can choose the paragraph after which the ad content is displayed.', 'advanced-ads' ),
67 'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/content-within.png',
68 'options' => array( 'show_position' => true, 'show_lazy_load' => true, 'uses_the_content' => true, 'amp' => true )
69 ),
70 'sidebar_widget' => array(
71 'title' => __( 'Sidebar Widget', 'advanced-ads' ),
72 'description' => __( 'Create a sidebar widget with an ad. Can be placed and used like any other widget.', 'advanced-ads' ),
73 'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/widget.png',
74 'options' => array( 'show_lazy_load' => true, 'amp' => true )
75 ),
76 );
77 return apply_filters( 'advanced-ads-placement-types', $types );
78 }
79
80 /**
81 * update placements if sent
82 *
83 * @since 1.5.2
84 */
85 static function update_placements(){
86
87 // check user permissions
88 if( ! current_user_can( Advanced_Ads_Plugin::user_cap( 'advanced_ads_manage_placements') ) ) {
89 return;
90 }
91
92 $success = null;
93
94 // add hook of last opened placement settings to URL
95 $hook = !empty( $_POST['advads-last-edited-placement'] ) ? '#single-placement-' . $_POST['advads-last-edited-placement'] : '';
96
97 if ( isset($_POST['advads']['placement']) && check_admin_referer( 'advads-placement', 'advads_placement' ) ){
98 $success = self::save_new_placement( $_POST['advads']['placement'] );
99 }
100 // save placement data
101 if ( isset($_POST['advads']['placements']) && check_admin_referer( 'advads-placement', 'advads_placement' )){
102 $success = self::save_placements( $_POST['advads']['placements'] );
103 }
104
105 $success = apply_filters( 'advanced-ads-update-placements', $success );
106
107 if(isset($success)){
108 $message = $success ? 'updated' : 'error';
109 wp_redirect( esc_url_raw( add_query_arg(array('message' => $message) ) ) . $hook );
110 }
111 }
112
113 /**
114 * save a new placement
115 *
116 * @since 1.1.0
117 * @param array $new_placement
118 * @return mixed slug if saved; false if not
119 */
120 public static function save_new_placement($new_placement) {
121 // load placements // -TODO use model
122 $placements = Advanced_Ads::get_ad_placements_array();
123
124 // create slug
125 $new_placement['slug'] = sanitize_title( $new_placement['name'] );
126
127 if ( isset( $placements[ $new_placement['slug'] ] ) ) {
128 $i = 1;
129 // try to save placement until we found an empty slug
130 do {
131 $i ++;
132 if ( $i === 100 ) { // prevent endless loop, just in case
133 Advanced_Ads::log( 'endless loop when injecting placement' );
134 break;
135 }
136 } while ( isset( $placements[ $new_placement['slug'] . '_' . $i ] ) );
137
138 $new_placement['slug'] .= '_' . $i;
139 $new_placement['name'] .= ' ' . $i;
140 }
141
142 // check if slug already exists or is empty
143 if ( $new_placement['slug'] === '' || isset( $placements[$new_placement['slug']]) || !isset( $new_placement['type'] ) ) {
144 return false;
145 }
146
147 // make sure only allowed types are being saved
148 $placement_types = Advanced_Ads_Placements::get_placement_types();
149 $new_placement['type'] = (isset($placement_types[$new_placement['type']])) ? $new_placement['type'] : 'default';
150 // escape name
151 $new_placement['name'] = esc_attr( $new_placement['name'] );
152
153 // add new place to all placements
154 $placements[$new_placement['slug']] = array(
155 'type' => $new_placement['type'],
156 'name' => $new_placement['name'],
157 'item' => $new_placement['item']
158 );
159
160 // add index options
161 if ( isset($new_placement['options']) ){
162 $placements[$new_placement['slug']]['options'] = $new_placement['options'];
163 if ( isset($placements[$new_placement['slug']]['options']['index']) ) {
164 $placements[$new_placement['slug']]['options']['index'] = absint( $placements[$new_placement['slug']]['options']['index'] ); }
165 }
166
167 // save array
168 Advanced_Ads::get_instance()->get_model()->update_ad_placements_array( $placements );
169
170 return $new_placement['slug'];
171 }
172
173 /**
174 * save placements
175 *
176 * @since 1.1.0
177 * @param array $placement_items
178 * @return mixed true if saved; error message if not
179 */
180 public static function save_placements($placement_items) {
181
182 // load placements // -TODO use model
183 $placements = Advanced_Ads::get_ad_placements_array();
184
185 foreach ( $placement_items as $_placement_slug => $_placement ) {
186 // remove the placement
187 if ( isset($_placement['delete']) ) {
188 unset($placements[$_placement_slug]);
189 continue;
190 }
191 // save item
192 if ( isset($_placement['item']) ) {
193 $placements[$_placement_slug]['item'] = $_placement['item']; }
194 // save item options
195 if ( isset($_placement['options']) ){
196 $placements[$_placement_slug]['options'] = $_placement['options'];
197 if ( isset($placements[$_placement_slug]['options']['index']) ) {
198 $placements[$_placement_slug]['options']['index'] = absint( $placements[$_placement_slug]['options']['index'] ); }
199 } else {
200 $placements[$_placement_slug]['options'] = array();
201 }
202 }
203
204 // save array
205 Advanced_Ads::get_instance()->get_model()->update_ad_placements_array( $placements );
206
207 return true;
208 }
209
210 /**
211 * get items for item select field
212 *
213 * @since 1.1
214 * @return arr $select items for select field
215 */
216 public static function items_for_select() {
217 $select = array();
218 $model = Advanced_Ads::get_instance()->get_model();
219
220 // load all ad groups
221 $groups = $model->get_ad_groups();
222 foreach ( $groups as $_group ) {
223 $select['groups']['group_' . $_group->term_id] = $_group->name;
224 }
225
226 // load all ads
227 $ads = $model->get_ads( array('orderby' => 'title', 'order' => 'ASC') );
228 foreach ( $ads as $_ad ) {
229 $select['ads']['ad_' . $_ad->ID] = $_ad->post_title;
230 }
231
232 return $select;
233 }
234
235 /**
236 * get html tags for content injection
237 *
238 * @since 1.3.5
239 * @return arr $tags array with tags that can be used for content injection
240 */
241 public static function tags_for_content_injection(){
242 $tags = apply_filters( 'advanced-ads-tags-for-injection', array(
243 'p' => sprintf( __( 'paragraph (%s)', 'advanced-ads' ), '&lt;p&gt;' ),
244 'pwithoutimg' => sprintf( __( 'paragraph without image (%s)', 'advanced-ads' ), '&lt;p&gt;' ),
245 'h2' => sprintf( __( 'headline 2 (%s)', 'advanced-ads' ), '&lt;h2&gt;' ),
246 'h3' => sprintf( __( 'headline 3 (%s)', 'advanced-ads' ), '&lt;h3&gt;' ),
247 'h4' => sprintf( __( 'headline 4 (%s)', 'advanced-ads' ), '&lt;h4&gt;' ),
248 ));
249
250 return $tags;
251 }
252
253 /**
254 * return content of a placement
255 *
256 * @since 1.1.0
257 * @param string $id slug of the display
258 * @param array $args optional arguments (passed to child)
259 */
260 public static function output( $id = '', $args = array() ) {
261 // get placement data for the slug
262 if ( $id == '' ) {
263 return;
264 }
265
266 $placements = Advanced_Ads::get_ad_placements_array();
267 $placement = ( isset( $placements[ $id ] ) && is_array( $placements[ $id ] ) ) ? $placements[ $id ] : array();
268
269 if ( isset( $args['change-placement'] ) ) {
270 // some options was provided by the user
271 $placement = Advanced_Ads_Utils::merge_deep_array( array( $placement, $args['change-placement'] ) ) ;
272 }
273
274 if ( isset( $placement['item'] ) && $placement['item'] !== '' ) {
275 $_item = explode( '_', $placement['item'] );
276
277 if ( ! isset( $_item[1] ) || empty( $_item[1] ) ) {
278 return ;
279 }
280
281 // inject options
282 if ( isset( $placement['options'] ) && is_array( $placement['options'] ) ) {
283 foreach ( $placement['options'] as $_k => $_v ) {
284 if ( ! isset( $args[ $_k ] ) ) {
285 $args[ $_k ] = $_v;
286 }
287 }
288 }
289
290 // inject placement type
291 if ( isset( $placement['type'] ) ) {
292 $args[ 'placement_type' ] = $placement['type'];
293 }
294
295 // options
296 $prefix = Advanced_Ads_Plugin::get_instance()->get_frontend_prefix();
297
298 // return either ad or group content
299 switch ( $_item[0] ) {
300 case 'ad':
301 case Advanced_Ads_Select::AD :
302 // create class from placement id (not if header injection)
303 if ( ! isset( $placement['type'] ) || $placement['type'] !== 'header' ) {
304 if ( ! isset( $args['output'] ) ) {
305 $args['output'] = array();
306 }
307 if ( ! isset( $args['output']['class'] ) ) {
308 $args['output']['class'] = array();
309 }
310 $class = $prefix . $id;
311 if ( ! in_array( $class, $args['output']['class'] ) ) {
312 $args['output']['class'][] = $class;
313 }
314 }
315
316 // fix method id
317 $_item[0] = Advanced_Ads_Select::AD;
318 break;
319
320 // avoid loops (programmatical error)
321 case Advanced_Ads_Select::PLACEMENT :
322 return;
323
324 case Advanced_Ads_Select::GROUP :
325 $class = $prefix . $id;
326 if ( ( isset( $placement['type'] ) && $placement['type'] !== 'header' )
327 && ( !isset( $args['output']['class'] )
328 || !is_array( $args['output']['class'] )
329 || !in_array( $class, $args['output']['class'] ) ) ) {
330 $args['output']['class'][] = $class;
331 }
332 default:
333 }
334
335 // create placement id for various features
336 $args['output']['placement_id'] = $id;
337
338 // add the placement to the global output array
339 $advads = Advanced_Ads::get_instance();
340 $name = isset( $placement['name'] ) ? $placement['name'] : $id;
341
342 if ( ! isset( $args['global_output'] ) || $args['global_output'] ) {
343 $advads->current_ads[] = array( 'type' => 'placement', 'id' => $id, 'title' => $name );
344 }
345
346 $result = Advanced_Ads_Select::get_instance()->get_ad_by_method( (int) $_item[1], $_item[0], $args );
347
348 if ( $result && ! empty( $args['placement_clearfix'] ) ) {
349 $result .= '<br style="clear: both; display: block; float: none; color:blue;"/>';
350 }
351
352 return $result;
353 }
354 }
355
356 /**
357 * inject ads directly into the content
358 *
359 * @since 1.2.1
360 * @param string $placement_id id of the placement
361 * @param arr $placement_opts placement options
362 * @param string $content
363 * @return type
364 * @link inspired by http://www.wpbeginner.com/wp-tutorials/how-to-insert-ads-within-your-post-content-in-wordpress/
365 */
366 public static function &inject_in_content($placement_id, $placement_opts, &$content) {
367 if ( ! extension_loaded( 'dom' ) ) {
368 return $content;
369 }
370
371 // get plugin options
372 $plugin_options = Advanced_Ads::get_instance()->options();
373
374 $wpCharset = get_bloginfo('charset');
375 // parse document as DOM (fragment - having only a part of an actual post given)
376
377 $content_to_load = self::get_content_to_load( $content, $wpCharset );
378 if ( ! $content_to_load ) {
379 return $content;
380 }
381
382 $dom = new DOMDocument('1.0', $wpCharset);
383 // may loose some fragments or add autop-like code
384 libxml_use_internal_errors(true); // avoid notices and warnings - html is most likely malformed
385
386 $success = $dom->loadHtml('<!DOCTYPE html><html><meta http-equiv="Content-Type" content="text/html; charset=' . $wpCharset . '" /><body>' . $content_to_load);
387 libxml_use_internal_errors(false);
388 if ($success !== true) {
389 // -TODO handle cases were dom-parsing failed (at least inform user)
390 return $content;
391 }
392
393 // parse arguments
394 $tag = isset($placement_opts['tag']) ? $placement_opts['tag'] : 'p';
395 $tag = preg_replace('/[^a-z0-9]/i', '', $tag); // simplify tag
396
397 // allow more complex xPath expression
398 $tag = apply_filters( 'advanced-ads-placement-content-injection-xpath', $tag, $placement_opts );
399
400 if ( $tag === 'pwithoutimg' ) {
401 $tag = 'p[not(descendant::img)]';
402 }
403
404 // select positions
405 $xpath = new DOMXPath($dom);
406 $items = $xpath->query('/html/body/' . $tag);
407
408 $options = array(
409 'allowEmpty' => false, // whether the tag can be empty to be counted
410 'paragraph_select_from_bottom' => isset($placement_opts['start_from_bottom']) && $placement_opts['start_from_bottom'],
411 // only has before and after
412 'before' => isset($placement_opts['position']) && $placement_opts['position'] === 'before'
413 );
414
415 $options['paragraph_id'] = isset($placement_opts['index']) ? $placement_opts['index'] : 1;
416 $options['paragraph_id'] = max( 1, (int) $options['paragraph_id'] );
417
418 // if there are too few items at this level test nesting
419 $options['itemLimit'] = $tag === 'p' ? 2 : 1;
420
421 // trigger such a high item limit that all elements will be considered
422 if( ! empty($plugin_options['content-injection-level-disabled'] ) ){
423 $options['itemLimit'] = 1000;
424 }
425
426 // allow hooks to change some options
427 $options = apply_filters(
428 'advanced-ads-placement-content-injection-options',
429 $options,
430 $tag );
431
432 if ($items->length < $options['itemLimit'] ) {
433 $items = $xpath->query('/html/body/*/' . $tag);
434 }
435 // try third level
436 if ( $items->length < $options['itemLimit'] ) {
437 $items = $xpath->query('/html/body/*/*/' . $tag);
438 }
439 // try all levels as last resort
440 if ( $items->length < $options['itemLimit'] ) {
441 $items = $xpath->query( '//' . $tag );
442 }
443
444 // allow to select other elements
445 $items = apply_filters( 'advanced-ads-placement-content-injection-items', $items, $xpath, $tag );
446
447 // filter empty tags from items
448 $whitespaces = json_decode('"\t\n\r \u00A0"');
449 $paragraphs = array();
450 foreach ($items as $item) {
451 if ( $options['allowEmpty'] || ( isset($item->textContent) && trim($item->textContent, $whitespaces) !== '' ) ) {
452 $paragraphs[] = $item;
453 }
454 }
455
456 $options['paragraph_count'] = count($paragraphs);
457
458 if ($options['paragraph_count'] >= $options['paragraph_id']) {
459 $offset = $options['paragraph_select_from_bottom'] ? $options['paragraph_count'] - $options['paragraph_id'] : $options['paragraph_id'] - 1;
460 $offsets = apply_filters( 'advanced-ads-placement-content-offsets', array( $offset ), $options, $placement_opts );
461 $did_inject = false;
462
463 foreach ( $offsets as $offset ) {
464 // inject
465 $node = apply_filters( 'advanced-ads-placement-content-injection-node', $paragraphs[$offset], $tag, $options['before'] );
466
467 $adContent = Advanced_Ads_Select::get_instance()->get_ad_by_method( $placement_id, 'placement', $placement_opts );
468
469 if ( trim( $adContent, $whitespaces ) === '' ) {
470 continue;
471 }
472
473 $adContent = self::filter_ad_content( $adContent, $node->tagName, $options );
474
475 // convert HTML to XML!
476 $adDom = new DOMDocument('1.0', $wpCharset);
477 libxml_use_internal_errors(true);
478 $adDom->loadHtml('<!DOCTYPE html><html><meta http-equiv="Content-Type" content="text/html; charset=' . $wpCharset . '" /><body>' . $adContent);
479 // log errors
480 if ( defined ( 'WP_DEBUG' ) && WP_DEBUG && current_user_can( 'advanced_ads_manage_options' ) ) {
481 foreach( libxml_get_errors() as $_error ) {
482 // continue, if there is '&' symbol, but not HTML entity
483 if ( false === stripos( $_error->message, 'htmlParseEntityRef:' ) ) {
484 Advanced_Ads::log( 'possible content injection error for placement "' . $placement_id . '": ' . print_r( $_error, true ) );
485 }
486 }
487 }
488
489
490 if ( $options['before'] ) {
491 $refNode = $node;
492
493 foreach ( $adDom->getElementsByTagName( 'body' )->item( 0 )->childNodes as $importedNode ) {
494 $importedNode = $dom->importNode( $importedNode, true );
495 $refNode->parentNode->insertBefore( $importedNode, $refNode );
496 }
497 } else {
498 // append before next node or as last child to body
499 $refNode = $node->nextSibling;
500 if (isset($refNode)) {
501
502 foreach ( $adDom->getElementsByTagName( 'body' )->item( 0 )->childNodes as $importedNode ) {
503 $importedNode = $dom->importNode( $importedNode, true );
504 $refNode->parentNode->insertBefore( $importedNode, $refNode );
505 }
506
507 } else {
508 // append to body; -TODO using here that we only select direct children of the body tag
509 foreach ( $adDom->getElementsByTagName( 'body' )->item( 0 )->childNodes as $importedNode ) {
510 $importedNode = $dom->importNode( $importedNode, true );
511 $node->parentNode->appendChild( $importedNode );
512 }
513 }
514 }
515
516 libxml_use_internal_errors(false);
517 $did_inject = true;
518 }
519
520 if ( ! $did_inject ) {
521 return $content;
522 }
523
524 $content_orig = $content;
525 // convert to text-representation
526 $content = $dom->saveHTML();
527 $content = self::prepare_output( $content, $content_orig );
528
529 /**
530 * show a warning to ad admins in the Ad Health bar in the frontend, when
531 *
532 * * the level limitation was not disabled
533 * * could not inject one ad (as by use of `elseif` here)
534 * * but there are enough elements on the site, but just in sub-containers
535 *
536 */
537 } elseif( current_user_can( Advanced_Ads_Plugin::user_cap( 'advanced_ads_manage_options') )
538 && empty($plugin_options['content-injection-level-disabled'] ) ) {
539
540 // check if there are more elements without limitation
541 $all_items = $xpath->query( '//' . $tag );
542 if( $options['paragraph_id'] <= $all_items->length ){
543 // add a warning to ad health
544 add_filter( 'advanced-ads-ad-health-nodes', array( 'Advanced_Ads_Placements', 'add_ad_health_node' ) );
545 }
546 }
547
548 return $content;
549 }
550
551 /**
552 * Get content to load.
553 *
554 * @param string $content Original content.
555 * @param $wpCharset Blog charset.
556 * @return string $content Content to load.
557 */
558 private static function get_content_to_load( $content, $wpCharset ) {
559 $plugin_options = Advanced_Ads::get_instance()->options();
560
561 // Prevent removing closing tags in scripts.
562 $content_to_load= preg_replace( '/<script.*?<\/script>/', '<!--\0-->', $content);
563
564 // check which priority the wpautop filter has; might have been disabled on purpose
565 $wpautop_priority = has_filter( 'the_content', 'wpautop');
566 if ( $wpautop_priority && Advanced_Ads_Plugin::get_instance()->get_content_injection_priority() < $wpautop_priority ) {
567 $content_to_load = wpautop( $content_to_load );
568 }
569
570 return $content_to_load;
571 }
572
573 /**
574 * Filter ad content.
575 *
576 * @param string $adContent Ad content.
577 * @param string $tag_name tar before/after the content.
578 * @param array $options Injection options.
579 * @return string $adContent Ad content.
580 */
581 private static function filter_ad_content( $ad_content, $tag_name, $options ) {
582 $plugin_options = Advanced_Ads::get_instance()->options();
583
584 //Inject placeholder.
585 $id = count( self::$ads_for_placeholders );
586 self::$ads_for_placeholders[] = array(
587 'id' => $id,
588 'tag' => $tag_name,
589 'type' => $options['before'] ? 'before' : 'after',
590 'ad' => $ad_content
591 );
592 $ad_content = '%advads_placeholder_' . $id . '%';
593
594 return $ad_content;
595 }
596
597 /**
598 * Prepare output.
599 *
600 * @param string $content Modified content.
601 * @param string $content_orig Original content.
602 * @return string $content Content to output.
603 */
604 private static function prepare_output( $content, $content_orig ) {
605 $plugin_options = Advanced_Ads::get_instance()->options();
606
607 $content = self::inject_ads( $content, $content_orig, self::$ads_for_placeholders );
608 self::$ads_for_placeholders = array();
609
610 return $content;
611 }
612
613 /**
614 * Search for ad placeholders in the `$content` to determine positions at which to inject ads.
615 * Given the positions, inject ads into `$content_orig.
616 *
617 * @param string $content Post content with injected ad placeholders.
618 * @param string $content_orig Unmodified post content.
619 * @param arrray $options Injection options.
620 * @param arrray $ads_for_placeholders Array of ads.
621 * Each ad contains placeholder id, before or after which tag to inject the ad, the ad content.
622 * @return string $content
623 */
624 private static function inject_ads( $content, $content_orig, $ads_for_placeholders ) {
625 $self_closing_tags = array( 'area', 'base', 'basefont', 'bgsound', 'br', 'col', 'embed', 'frame', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr' );
626
627 // It is not possible to append/prepend in self closing tags.
628 foreach ( $ads_for_placeholders as &$ad_content ) {
629 if ( ( 'prepend' === $ad_content['type'] || 'append' === $ad_content['type'] )
630 && in_array( $ad_content['tag'], $self_closing_tags, true ) ) {
631 $ad_content['type'] = 'after';
632 }
633 }
634 unset( $ad_content );
635 usort( $ads_for_placeholders, array( 'Advanced_Ads_Placements', 'sort_ads_for_placehoders' ) );
636
637
638 // Add tags before/after which ad placehoders were injected.
639 foreach ( $ads_for_placeholders as $ad_content ) {
640 $tag = $ad_content['tag'];
641
642 switch( $ad_content['type'] ) {
643 case 'before':
644 case 'prepend':
645 $alts[] = "<${tag}[^>]*>";
646 break;
647 case 'after':
648 if ( in_array( $tag, $self_closing_tags, true ) ) {
649 $alts[] = "<${tag}[^>]*>";
650 } else {
651 $alts[] = "</${tag}>";
652 }
653 break;
654 case 'append':
655 $alts[] = "</${tag}>";
656 break;
657 }
658
659 }
660 $alts = array_unique( $alts );
661 $tag_regexp = implode( '|', $alts );
662 // Add ad placeholder.
663 $alts[] = '%advads_placeholder_(?:\d+)%';
664 $tag_and_placeholder_regexp = implode( '|', $alts );
665
666 preg_match_all( "#{$tag_and_placeholder_regexp}#i", $content, $tag_matches );
667 $count = 0;
668
669 // For each tag located before/after an ad placeholder, find its offset among the same tags.
670 foreach ( $tag_matches[0] as $r ) {
671 if ( preg_match( '/%advads_placeholder_(\d+)%/', $r, $result ) ) {
672 $id = $result[1];
673 $found_ad = false;
674 foreach ( $ads_for_placeholders as $n => $ad ) {
675 if ( (int) $ad['id'] === (int) $id ) {
676 $found_ad = $ad;
677 break;
678 }
679 }
680 if ( ! $found_ad ) {
681 continue;
682 }
683
684 switch( $found_ad['type'] ) {
685 case 'before':
686 case 'append':
687 $ads_for_placeholders[ $n ]['offset'] = $count;
688 break;
689 case 'after':
690 case 'prepend':
691 $ads_for_placeholders[ $n ]['offset'] = $count -1;
692 break;
693 }
694
695 } else {
696 $count++;
697 }
698 }
699
700 // Find tags before/after which we need to inject ads.
701 preg_match_all( "#{$tag_regexp}#i", $content_orig, $orig_tag_matches, PREG_OFFSET_CAPTURE );
702 $new_content = '';
703 $pos = 0;
704
705 foreach ( $orig_tag_matches[0] as $n => $r ) {
706 $to_inject = array();
707 // Check if we need to inject an ad at this offset.
708 foreach ( $ads_for_placeholders as $ad ) {
709 if ( isset( $ad['offset'] ) && $ad['offset'] === $n ) {
710 $to_inject[] = $ad;
711 }
712 }
713
714 foreach ( $to_inject as $item ) {
715 switch( $item['type'] ) {
716 case 'before':
717 case 'append':
718 $found_pos = $r[1];
719 break;
720 case 'after':
721 case 'prepend':
722 $found_pos = $r[1] + strlen( $r[0] );
723 break;
724 }
725
726 $new_content .= substr( $content_orig, $pos, $found_pos - $pos );
727 $pos = $found_pos;
728 $new_content .= $item['ad'];
729 }
730 }
731 $new_content .= substr( $content_orig, $pos );
732 return $new_content;
733 }
734
735
736 /**
737 * Callback function for usort() to sort ads for placeholders.
738 *
739 * @param array $first The first array to compare.
740 * @param array $second The second array to compare.
741 * @return int 0 if both objects equal. -1 if second array should come first, 1 otherwise.
742 */
743 public static function sort_ads_for_placehoders( $first, $second ) {
744 if ( $first['type'] === $second['type'] ) {
745 return 0;
746 }
747
748 $num = array( 'before' => 1, 'prepend' => 2, 'append' => 3, 'after' => 4 );
749
750 return $num[ $first['type'] ] > $num[ $second['type'] ] ? 1 : -1;
751 }
752
753 /**
754 * Add a warning to 'Ad health'.
755 *
756 * @param array $nodes.
757 * @return array $nodes.
758 */
759 public static function add_ad_health_node( $nodes ) {
760 $nodes[] = array( 'type' => 1, 'data' => array(
761 'parent' => 'advanced_ads_ad_health',
762 'id' => 'advanced_ads_ad_health_the_content_not_enough_elements',
763 'title' => sprintf(
764 /* translators: %s stands for the name of the "Disable level limitation" option and automatically translated as well */
765 __( 'Set <em>%s</em> to show more ads', 'advanced-ads' ),
766 __('Disable level limitation', 'advanced-ads' ) ),
767 'href' => admin_url( '/admin.php?page=advanced-ads-settings#top#general' ),
768 'meta' => array(
769 'class' => 'advanced_ads_ad_health_warning',
770 'target' => '_blank'
771 )
772 ) );
773 return $nodes;
774 }
775
776 /**
777 * check if the placement can be displayed
778 *
779 * @since 1.6.9
780 * @param int $id placement id
781 * @return bool true if placement can be displayed
782 */
783 static function can_display( $id = 0 ){
784 if ( ! isset($id) || $id === 0 ) {
785 return true;
786 }
787
788 return apply_filters( 'advanced-ads-can-display-placement', true, $id );
789 }
790
791 /**
792 * Get the placements that includes the ad or group.
793 *
794 * @param string $type 'ad' or 'group'.
795 * @param int $id Id.
796 * @return array
797 */
798 public static function get_placements_by( $type, $id ) {
799 $result = array();
800
801 $placements = Advanced_Ads::get_ad_placements_array();
802 foreach ( $placements as $_id => $_placement ) {
803 if ( isset( $_placement['item'] ) && $_placement['item'] === $type . '_' . $id ) {
804 $result[ $_id ] = $_placement;
805 }
806 }
807
808 return $result;
809 }
810
811 }
812
813