PluginProbe ʕ •ᴥ•ʔ
Advanced Ads – Ad Manager & AdSense / 1.10.7
Advanced Ads – Ad Manager & AdSense v1.10.7
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 7 years ago ad-ajax.php 8 years ago ad-debug.php 8 years ago ad-model.php 8 years ago ad-select.php 9 years ago ad.php 7 years ago ad_ajax_callbacks.php 7 years ago ad_group.php 7 years ago ad_placements.php 7 years ago ad_type_abstract.php 8 years ago ad_type_content.php 8 years ago ad_type_dummy.php 8 years ago ad_type_group.php 8 years ago ad_type_image.php 7 years ago ad_type_plain.php 8 years ago checks.php 7 years ago compatibility.php 7 years ago display-conditions.php 7 years ago filesystem.php 8 years ago frontend_checks.php 7 years ago plugin.php 7 years ago upgrades.php 9 years ago utils.php 7 years ago visitor-conditions.php 7 years ago widget.php 7 years ago
ad_placements.php
635 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
22 /**
23 * get placement types
24 *
25 * @since 1.2.1
26 * @return arr $types array with placement types
27 */
28 public static function get_placement_types() {
29 $types = array(
30 'default' => array(
31 'title' => __( 'Manual Placement', 'advanced-ads' ),
32 'description' => __( 'Manual placement to use as function or shortcode.', 'advanced-ads' ),
33 'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/manual.png',
34 'options' => array( 'show_position' => true, 'show_lazy_load' => true )
35 ),
36 'header' => array(
37 'title' => __( 'Header Code', 'advanced-ads' ),
38 'description' => __( 'Injected in Header (before closing &lt;/head&gt; Tag, often not visible).', 'advanced-ads' ),
39 'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/header.png'
40 ),
41 'footer' => array(
42 'title' => __( 'Footer Code', 'advanced-ads' ),
43 'description' => __( 'Injected in Footer (before closing &lt;/body&gt; Tag).', 'advanced-ads' ),
44 'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/footer.png'
45 ),
46 'post_top' => array(
47 'title' => __( 'Before Content', 'advanced-ads' ),
48 'description' => __( 'Injected before the post content.', 'advanced-ads' ),
49 'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/content-before.png',
50 'options' => array( 'show_position' => true, 'show_lazy_load' => true, 'uses_the_content' => true )
51 ),
52 'post_bottom' => array(
53 'title' => __( 'After Content', 'advanced-ads' ),
54 'description' => __( 'Injected after the post content.', 'advanced-ads' ),
55 'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/content-after.png',
56 'options' => array( 'show_position' => true, 'show_lazy_load' => true, 'uses_the_content' => true )
57 ),
58 'post_content' => array(
59 'title' => __( 'Content', 'advanced-ads' ),
60 'description' => __( 'Injected into the content. You can choose the paragraph after which the ad content is displayed.', 'advanced-ads' ),
61 'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/content-within.png',
62 'options' => array( 'show_position' => true, 'show_lazy_load' => true, 'uses_the_content' => true )
63 ),
64 'sidebar_widget' => array(
65 'title' => __( 'Sidebar Widget', 'advanced-ads' ),
66 'description' => __( 'Create a sidebar widget with an ad. Can be placed and used like any other widget.', 'advanced-ads' ),
67 'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/widget.png',
68 'options' => array( 'show_lazy_load' => true )
69 ),
70 );
71 return apply_filters( 'advanced-ads-placement-types', $types );
72 }
73
74 /**
75 * update placements if sent
76 *
77 * @since 1.5.2
78 */
79 static function update_placements(){
80
81 // check user permissions
82 if( ! current_user_can( Advanced_Ads_Plugin::user_cap( 'advanced_ads_manage_placements') ) ) {
83 return;
84 }
85
86 $success = null;
87
88 // add hook of last opened placement settings to URL
89 $hook = !empty( $_POST['advads-last-edited-placement'] ) ? '#single-placement-' . $_POST['advads-last-edited-placement'] : '';
90
91 if ( isset($_POST['advads']['placement']) && check_admin_referer( 'advads-placement', 'advads_placement' ) ){
92 $success = self::save_new_placement( $_POST['advads']['placement'] );
93 }
94 // save placement data
95 if ( isset($_POST['advads']['placements']) && check_admin_referer( 'advads-placement', 'advads_placement' )){
96 $success = self::save_placements( $_POST['advads']['placements'] );
97 }
98
99 $success = apply_filters( 'advanced-ads-update-placements', $success );
100
101 if(isset($success)){
102 $message = $success ? 'updated' : 'error';
103 wp_redirect( esc_url_raw( add_query_arg(array('message' => $message) ) ) . $hook );
104 }
105 }
106
107 /**
108 * save a new placement
109 *
110 * @since 1.1.0
111 * @param array $new_placement
112 * @return mixed slug if saved; false if not
113 */
114 public static function save_new_placement($new_placement) {
115 // load placements // -TODO use model
116 $placements = Advanced_Ads::get_ad_placements_array();
117
118 // create slug
119 $new_placement['slug'] = sanitize_title( $new_placement['name'] );
120
121 if ( isset( $placements[ $new_placement['slug'] ] ) ) {
122 $i = 1;
123 // try to save placement until we found an empty slug
124 do {
125 $i ++;
126 if ( $i === 100 ) { // prevent endless loop, just in case
127 Advanced_Ads::log( 'endless loop when injecting placement' );
128 break;
129 }
130 } while ( isset( $placements[ $new_placement['slug'] . '_' . $i ] ) );
131
132 $new_placement['slug'] .= '_' . $i;
133 $new_placement['name'] .= ' ' . $i;
134 }
135
136 // check if slug already exists or is empty
137 if ( $new_placement['slug'] === '' || isset( $placements[$new_placement['slug']]) || !isset( $new_placement['type'] ) ) {
138 return false;
139 }
140
141 // make sure only allowed types are being saved
142 $placement_types = Advanced_Ads_Placements::get_placement_types();
143 $new_placement['type'] = (isset($placement_types[$new_placement['type']])) ? $new_placement['type'] : 'default';
144 // escape name
145 $new_placement['name'] = esc_attr( $new_placement['name'] );
146
147 // add new place to all placements
148 $placements[$new_placement['slug']] = array(
149 'type' => $new_placement['type'],
150 'name' => $new_placement['name'],
151 'item' => $new_placement['item']
152 );
153
154 // add index options
155 if ( isset($new_placement['options']) ){
156 $placements[$new_placement['slug']]['options'] = $new_placement['options'];
157 if ( isset($placements[$new_placement['slug']]['options']['index']) ) {
158 $placements[$new_placement['slug']]['options']['index'] = absint( $placements[$new_placement['slug']]['options']['index'] ); }
159 }
160
161 // save array
162 Advanced_Ads::get_instance()->get_model()->update_ad_placements_array( $placements );
163
164 return $new_placement['slug'];
165 }
166
167 /**
168 * save placements
169 *
170 * @since 1.1.0
171 * @param array $placement_items
172 * @return mixed true if saved; error message if not
173 */
174 public static function save_placements($placement_items) {
175
176 // load placements // -TODO use model
177 $placements = Advanced_Ads::get_ad_placements_array();
178
179 foreach ( $placement_items as $_placement_slug => $_placement ) {
180 // remove the placement
181 if ( isset($_placement['delete']) ) {
182 unset($placements[$_placement_slug]);
183 continue;
184 }
185 // save item
186 if ( isset($_placement['item']) ) {
187 $placements[$_placement_slug]['item'] = $_placement['item']; }
188 // save item options
189 if ( isset($_placement['options']) ){
190 $placements[$_placement_slug]['options'] = $_placement['options'];
191 if ( isset($placements[$_placement_slug]['options']['index']) ) {
192 $placements[$_placement_slug]['options']['index'] = absint( $placements[$_placement_slug]['options']['index'] ); }
193 } else {
194 $placements[$_placement_slug]['options'] = array();
195 }
196 }
197
198 // save array
199 Advanced_Ads::get_instance()->get_model()->update_ad_placements_array( $placements );
200
201 return true;
202 }
203
204 /**
205 * get items for item select field
206 *
207 * @since 1.1
208 * @return arr $select items for select field
209 */
210 public static function items_for_select() {
211 $select = array();
212 $model = Advanced_Ads::get_instance()->get_model();
213
214 // load all ad groups
215 $groups = $model->get_ad_groups();
216 foreach ( $groups as $_group ) {
217 $select['groups']['group_' . $_group->term_id] = $_group->name;
218 }
219
220 // load all ads
221 $ads = $model->get_ads( array('orderby' => 'title', 'order' => 'ASC') );
222 foreach ( $ads as $_ad ) {
223 $select['ads']['ad_' . $_ad->ID] = $_ad->post_title;
224 }
225
226 return $select;
227 }
228
229 /**
230 * get html tags for content injection
231 *
232 * @since 1.3.5
233 * @return arr $tags array with tags that can be used for content injection
234 */
235 public static function tags_for_content_injection(){
236 $tags = apply_filters( 'advanced-ads-tags-for-injection', array(
237 'p' => sprintf( __( 'paragraph (%s)', 'advanced-ads' ), '&lt;p&gt;' ),
238 'pwithoutimg' => sprintf( __( 'paragraph without image (%s)', 'advanced-ads' ), '&lt;p&gt;' ),
239 'h2' => sprintf( __( 'headline 2 (%s)', 'advanced-ads' ), '&lt;h2&gt;' ),
240 'h3' => sprintf( __( 'headline 3 (%s)', 'advanced-ads' ), '&lt;h3&gt;' ),
241 'h4' => sprintf( __( 'headline 4 (%s)', 'advanced-ads' ), '&lt;h4&gt;' ),
242 ));
243
244 return $tags;
245 }
246
247 /**
248 * return content of a placement
249 *
250 * @since 1.1.0
251 * @param string $id slug of the display
252 * @param array $args optional arguments (passed to child)
253 */
254 public static function output( $id = '', $args = array() ) {
255 // get placement data for the slug
256 if ( $id == '' ) {
257 return;
258 }
259
260 $placements = Advanced_Ads::get_ad_placements_array();
261 $placement = ( isset( $placements[ $id ] ) && is_array( $placements[ $id ] ) ) ? $placements[ $id ] : array();
262
263 if ( isset( $args['change-placement'] ) ) {
264 // some options was provided by the user
265 $placement = Advanced_Ads_Utils::merge_deep_array( array( $placement, $args['change-placement'] ) ) ;
266 }
267
268 if ( isset( $placement['item'] ) && $placement['item'] !== '' ) {
269 $_item = explode( '_', $placement['item'] );
270
271 if ( ! isset( $_item[1] ) || empty( $_item[1] ) ) {
272 return ;
273 }
274
275 // inject options
276 if ( isset( $placement['options'] ) && is_array( $placement['options'] ) ) {
277 foreach ( $placement['options'] as $_k => $_v ) {
278 if ( ! isset( $args[ $_k ] ) ) {
279 $args[ $_k ] = $_v;
280 }
281 }
282 }
283
284 // inject placement type
285 if ( isset( $placement['type'] ) ) {
286 $args[ 'placement_type' ] = $placement['type'];
287 }
288
289 // options
290 $prefix = Advanced_Ads_Plugin::get_instance()->get_frontend_prefix();
291
292 // return either ad or group content
293 switch ( $_item[0] ) {
294 case 'ad':
295 case Advanced_Ads_Select::AD :
296 // create class from placement id (not if header injection)
297 if ( ! isset( $placement['type'] ) || $placement['type'] !== 'header' ) {
298 if ( ! isset( $args['output'] ) ) {
299 $args['output'] = array();
300 }
301 if ( ! isset( $args['output']['class'] ) ) {
302 $args['output']['class'] = array();
303 }
304 $class = $prefix . $id;
305 if ( ! in_array( $class, $args['output']['class'] ) ) {
306 $args['output']['class'][] = $class;
307 }
308 }
309
310 // fix method id
311 $_item[0] = Advanced_Ads_Select::AD;
312 break;
313
314 // avoid loops (programmatical error)
315 case Advanced_Ads_Select::PLACEMENT :
316 return;
317
318 case Advanced_Ads_Select::GROUP :
319 $class = $prefix . $id;
320 if ( ( isset( $placement['type'] ) && $placement['type'] !== 'header' )
321 && ( !isset( $args['output']['class'] )
322 || !is_array( $args['output']['class'] )
323 || !in_array( $class, $args['output']['class'] ) ) ) {
324 $args['output']['class'][] = $class;
325 }
326 default:
327 }
328
329 // create placement id for various features
330 $args['output']['placement_id'] = $id;
331
332 // add the placement to the global output array
333 $advads = Advanced_Ads::get_instance();
334 $name = isset( $placement['name'] ) ? $placement['name'] : $id;
335
336 if ( ! isset( $args['global_output'] ) || $args['global_output'] ) {
337 $advads->current_ads[] = array( 'type' => 'placement', 'id' => $id, 'title' => $name );
338 }
339
340 $result = Advanced_Ads_Select::get_instance()->get_ad_by_method( (int) $_item[1], $_item[0], $args );
341
342 if ( $result && ! empty( $args['placement_clearfix'] ) ) {
343 $result .= '<br style="clear: both; display: block; float: none; color:blue;"/>';
344 }
345
346 return $result;
347 }
348 }
349
350 /**
351 * inject ads directly into the content
352 *
353 * @since 1.2.1
354 * @param string $placement_id id of the placement
355 * @param arr $placement_opts placement options
356 * @param string $content
357 * @return type
358 * @link inspired by http://www.wpbeginner.com/wp-tutorials/how-to-insert-ads-within-your-post-content-in-wordpress/
359 */
360 public static function &inject_in_content($placement_id, $placement_opts, &$content) {
361 if ( ! extension_loaded( 'dom' ) ) {
362 return $content;
363 }
364
365 // get plugin options
366 $plugin_options = Advanced_Ads::get_instance()->options();
367
368 // parse document as DOM (fragment - having only a part of an actual post given)
369 // -TODO may want to verify the wpcharset is supported by server (mb_list_encodings)
370 // prevent messages from dom parser
371 $wpCharset = get_bloginfo('charset');
372 // check if mbstring exists
373 if ( ! function_exists( 'mb_convert_encoding' ) ) {
374 if ( $wpCharset === "UTF-8" ) {
375 $content_to_load = htmlspecialchars_decode( htmlentities( $content, ENT_COMPAT, $wpCharset, false ) );
376 } else {
377 return $content;
378 }
379 } else {
380 $content_to_load = mb_convert_encoding( $content, 'HTML-ENTITIES', $wpCharset );
381 }
382
383 // check which priority the wpautop filter has; might have been disabled on purpose
384 $wpautop_priority = has_filter( 'the_content', 'wpautop');
385 if ( $wpautop_priority && Advanced_Ads_Plugin::get_instance()->get_content_injection_priority() < $wpautop_priority ) {
386 $content_to_load = wpautop( $content_to_load );
387 }
388
389 // temporarily change content during processing
390 $replacements = array(
391 'gcse:search' => 'gcse__search', // Google custom search namespaced tags.
392 );
393 $content_to_load = str_replace( array_keys( $replacements ), array_values( $replacements ), $content_to_load );
394
395 $dom = new DOMDocument('1.0', $wpCharset);
396 // may loose some fragments or add autop-like code
397 libxml_use_internal_errors(true); // avoid notices and warnings - html is most likely malformed
398
399 $success = $dom->loadHtml('<!DOCTYPE html><html><meta http-equiv="Content-Type" content="text/html; charset=' . $wpCharset . '" /><body>' . $content_to_load);
400 libxml_use_internal_errors(false);
401 if ($success !== true) {
402 // -TODO handle cases were dom-parsing failed (at least inform user)
403 return $content;
404 }
405
406 // parse arguments
407 $tag = isset($placement_opts['tag']) ? $placement_opts['tag'] : 'p';
408 $tag = preg_replace('/[^a-z0-9]/i', '', $tag); // simplify tag
409
410 // allow more complex xPath expression
411 $tag = apply_filters( 'advanced-ads-placement-content-injection-xpath', $tag, $placement_opts );
412
413 if ( $tag === 'pwithoutimg' ) {
414 $tag = 'p[not(descendant::img)]';
415 }
416
417 // select positions
418 $xpath = new DOMXPath($dom);
419 $items = $xpath->query('/html/body/' . $tag);
420
421 $options = array(
422 'allowEmpty' => false, // whether the tag can be empty to be counted
423 'paragraph_select_from_bottom' => isset($placement_opts['start_from_bottom']) && $placement_opts['start_from_bottom'],
424 // only has before and after
425 'before' => isset($placement_opts['position']) && $placement_opts['position'] === 'before'
426 );
427
428 $options['paragraph_id'] = isset($placement_opts['index']) ? $placement_opts['index'] : 1;
429 $options['paragraph_id'] = max( 1, (int) $options['paragraph_id'] );
430
431 // if there are too few items at this level test nesting
432 $options['itemLimit'] = $tag === 'p' ? 2 : 1;
433
434 // trigger such a high item limit that all elements will be considered
435 if( ! empty($plugin_options['content-injection-level-disabled'] ) ){
436 $options['itemLimit'] = 1000;
437 }
438
439 // allow hooks to change some options
440 $options = apply_filters(
441 'advanced-ads-placement-content-injection-options',
442 $options,
443 $tag );
444
445 if ($items->length < $options['itemLimit'] ) {
446 $items = $xpath->query('/html/body/*/' . $tag);
447 }
448 // try third level
449 if ( $items->length < $options['itemLimit'] ) {
450 $items = $xpath->query('/html/body/*/*/' . $tag);
451 }
452 // try all levels as last resort
453 if ( $items->length < $options['itemLimit'] ) {
454 $items = $xpath->query( '//' . $tag );
455 }
456
457 // allow to select other elements
458 $items = apply_filters( 'advanced-ads-placement-content-injection-items', $items, $xpath, $tag );
459
460 // filter empty tags from items
461 $whitespaces = json_decode('"\t\n\r \u00A0"');
462 $paragraphs = array();
463 foreach ($items as $item) {
464 if ( $options['allowEmpty'] || ( isset($item->textContent) && trim($item->textContent, $whitespaces) !== '' ) ) {
465 $paragraphs[] = $item;
466 }
467 }
468
469 $options['paragraph_count'] = count($paragraphs);
470
471 if ($options['paragraph_count'] >= $options['paragraph_id']) {
472 $offset = $options['paragraph_select_from_bottom'] ? $options['paragraph_count'] - $options['paragraph_id'] : $options['paragraph_id'] - 1;
473 $offsets = apply_filters( 'advanced-ads-placement-content-offsets', array( $offset ), $options, $placement_opts );
474 $did_inject = false;
475
476 foreach ( $offsets as $offset ) {
477 // test ad is emtpy
478 $adContent = Advanced_Ads_Select::get_instance()->get_ad_by_method( $placement_id, 'placement', $options );
479 if ( trim( $adContent, $whitespaces ) === '' ) {
480 continue;
481 }
482
483 // convert HTML to XML!
484 $adDom = new DOMDocument('1.0', $wpCharset);
485 libxml_use_internal_errors(true);
486 // replace `</` with `<\/` in ad content when placed within `document.write()` to prevent code from breaking
487 // source for this regex: http://stackoverflow.com/questions/17852537/preg-replace-only-specific-part-of-string
488 $adContent = preg_replace('#(document.write.+)</(.*)#', '$1<\/$2', $adContent); // escapes all closing html tags
489 // $adContent = preg_replace('#(document.write.+)</sc(.*)#', '$1<\/sc$2', $adContent); // only escapes closing </script> tags
490 // $adContent = preg_replace('#(document.write[^<^)]+)</sc(.*)#', '$1<\/sc$2', $adContent); // too restrict, doesn’t work when beginning <script> tag is in the same line
491 $adDom->loadHtml('<!DOCTYPE html><html><meta http-equiv="Content-Type" content="text/html; charset=' . $wpCharset . '" /><body>' . $adContent);
492 // log errors
493 if ( defined ( 'WP_DEBUG' ) && WP_DEBUG && current_user_can( 'advanced_ads_manage_options' ) ) {
494 foreach( libxml_get_errors() as $_error ) {
495 // continue, if there is '&' symbol, but not HTML entity
496 if ( false === stripos( $_error->message, 'htmlParseEntityRef:' ) ) {
497 Advanced_Ads::log( 'possible content injection error for placement "' . $placement_id . '": ' . print_r( $_error, true ) );
498 }
499 }
500 }
501
502 // inject
503 $node = apply_filters( 'advanced-ads-placement-content-injection-node', $paragraphs[$offset], $tag, $options['before'] );
504 if ( $options['before'] ) {
505 $refNode = $node;
506
507 foreach ( $adDom->getElementsByTagName( 'body' )->item( 0 )->childNodes as $importedNode ) {
508 $importedNode = $dom->importNode( $importedNode, true );
509 $refNode->parentNode->insertBefore( $importedNode, $refNode );
510 }
511 } else {
512 // append before next node or as last child to body
513 $refNode = $node->nextSibling;
514 if (isset($refNode)) {
515
516 foreach ( $adDom->getElementsByTagName( 'body' )->item( 0 )->childNodes as $importedNode ) {
517 $importedNode = $dom->importNode( $importedNode, true );
518 $refNode->parentNode->insertBefore( $importedNode, $refNode );
519 }
520
521 } else {
522 // append to body; -TODO using here that we only select direct children of the body tag
523 foreach ( $adDom->getElementsByTagName( 'body' )->item( 0 )->childNodes as $importedNode ) {
524 $importedNode = $dom->importNode( $importedNode, true );
525 $node->parentNode->appendChild( $importedNode );
526 }
527 }
528 }
529
530 libxml_use_internal_errors(false);
531 $did_inject = true;
532 }
533
534 if ( ! $did_inject ) {
535 return $content;
536 }
537
538 // convert to text-representation
539 $content = $dom->saveHTML();
540 // remove head and tail (required for dom parser but unwanted for content)
541 $content = substr($content, stripos($content, '<body>') + 6);
542 $content = str_replace(array('</body>', '</html>'), '', $content);
543 $content = str_replace( array_values( $replacements ), array_keys( $replacements ), $content );
544
545 // no fall-back desired: if there are too few paragraphs do nothing
546
547 // fix shortcode quotes (malformed by backend editor)
548 $matches = array();
549 if (0 < preg_match_all('/\[[^]]+\]/Siu', $content, $matches, PREG_OFFSET_CAPTURE) && isset($matches[0])) {
550 foreach ($matches[0] as $match) {
551 $offset = $match[1];
552 $content = substr($content, 0, $offset) . str_replace(array('', '', '&#8220;', '&quote;', '&#8243;'), '"', $match[0]) . substr($content, $offset + strlen($match[0]));
553 }
554 }
555 /**
556 * show a warning to ad admins in the Ad Health bar in the frontend, when
557 *
558 * * the level limitation was not disabled
559 * * could not inject one ad (as by use of `elseif` here)
560 * * but there are enough elements on the site, but just in sub-containers
561 *
562 */
563 } elseif( current_user_can( Advanced_Ads_Plugin::user_cap( 'advanced_ads_manage_options') )
564 && empty($plugin_options['content-injection-level-disabled'] ) ) {
565
566 // check if there are more elements without limitation
567 $all_items = $xpath->query( '//' . $tag );
568 if( $options['paragraph_id'] <= $all_items->length ){
569 // add a warning to ad health
570 add_filter( 'advanced-ads-ad-health-nodes', array( 'Advanced_Ads_Placements', 'add_ad_health_node' ) );
571 }
572 }
573
574 return $content;
575 }
576
577 /**
578 * Add a warning to 'Ad health'.
579 *
580 * @param array $nodes.
581 * @return array $nodes.
582 */
583 public static function add_ad_health_node( $nodes ) {
584 $nodes[] = array( 'type' => 1, 'data' => array(
585 'parent' => 'advanced_ads_ad_health',
586 'id' => 'advanced_ads_ad_health_the_content_not_enough_elements',
587 'title' => sprintf(
588 /* translators: %s stands for the name of the "Disable level limitation" option and automatically translated as well */
589 __( 'Set <em>%s</em> to show more ads', 'advanced-ads' ),
590 __('Disable level limitation', 'advanced-ads' ) ),
591 'href' => admin_url( '/admin.php?page=advanced-ads-settings#top#general' ),
592 'meta' => array(
593 'class' => 'advanced_ads_ad_health_warning',
594 'target' => '_blank'
595 )
596 ) );
597 return $nodes;
598 }
599
600 /**
601 * check if the placement can be displayed
602 *
603 * @since 1.6.9
604 * @param int $id placement id
605 * @return bool true if placement can be displayed
606 */
607 static function can_display( $id = 0 ){
608 if ( ! isset($id) || $id === 0 ) {
609 return true;
610 }
611
612 return apply_filters( 'advanced-ads-can-display-placement', true, $id );
613 }
614
615 /**
616 * Get the placements that includes the ad or group.
617 *
618 * @param string $type 'ad' or 'group'.
619 * @param int $id Id.
620 * @return array
621 */
622 public static function get_placements_by( $type, $id ) {
623 $result = array();
624
625 $placements = Advanced_Ads::get_ad_placements_array();
626 foreach ( $placements as $_id => $_placement ) {
627 if ( isset( $_placement['item'] ) && $_placement['item'] === $type . '_' . $id ) {
628 $result[ $_id ] = $_placement;
629 }
630 }
631
632 return $result;
633 }
634 }
635