/** * * @return Advanced_Ads_Select */ public static function get_instance() { if (!isset(self::$instance)) { self::$instance = new self(); } return self::$instance; }
/** * Simple wp ajax interface for ad selection. * * Provides a single ad given ID and selection method. */ public function advads_ajax_ad_select() { // set proper header header('Content-Type: application/json; charset: utf-8'); // allow modules / add ons to test (this is rather late but should happen before anything important is called) do_action('advanced-ads-ajax-ad-select-init'); // init handlers $selector = Advanced_Ads_Select::get_instance(); $methods = $selector->get_methods(); $method = isset($_REQUEST['ad_method']) ? (string) $_REQUEST['ad_method'] : null; $id = isset($_REQUEST['ad_id']) ? (string) $_REQUEST['ad_id'] : null; $arguments = isset($_REQUEST['ad_args']) ? $_REQUEST['ad_args'] : array(); if (is_string($arguments)) { $arguments = stripslashes($arguments); $arguments = json_decode($arguments, true); } $adIds = isset($_REQUEST['ad_ids']) ? $_REQUEST['ad_ids'] : null; if (is_string($adIds)) { $adIds = json_decode($adIds, true); } $response = array(); if (isset($methods[$method]) && isset($id)) { $advads = Advanced_Ads::get_instance(); if (is_array($adIds)) { // ads loaded previously and passed by query $advads->current_ads += $adIds; } $l = count($advads->current_ads); // build content $content = $selector->get_ad_by_method($id, $method, $arguments); $adIds = array_slice($advads->current_ads, $l); // ads loaded by this request $response = array('status' => 'success', 'item' => $content, 'id' => $id, 'method' => $method, 'ads' => $adIds); } else { // report error $response = array('status' => 'error', 'message' => 'No valid ID or METHOD found.'); } echo json_encode($response); die; }
/** * inject ads directly into the content * * @since 1.2.1 * @param string $placement_id id of the placement * @param arr $options placement options * @param string $content * @return type * @link inspired by http://www.wpbeginner.com/wp-tutorials/how-to-insert-ads-within-your-post-content-in-wordpress/ */ public static function &inject_in_content($placement_id, $options, &$content) { /* * hot-fixed to support some tags under idealised conditions. * this does ignore: * - autop() messups * - nesting of any kind * - non-valid XHTML (i.e. wild HTML) * * 'after' is experimental as the concept requires DOM and is not fully deterministic across implementations. * it falls back to 'before' n+1-th element or 'after' closing tag of the n-th. */ $tag = isset($options['tag']) ? $options['tag'] : 'p'; $position = isset($options['position']) ? $options['position'] : 'after'; $paragraph_id = isset($options['index']) ? $options['index'] : 1; $offset = 0; $insertAt = null; // sanitise $tag = preg_quote(strtolower($tag), '`'); $before = $position !== 'after'; $paragraph_id = max(1, (int) $paragraph_id); // detect n-th tag $matches = array(); while (1 === preg_match("`<{$tag}(?=[ >/])(?:[^>]+/>|.+?(?:</{$tag}\\s*>|(?=<{$tag}(?=[ >/])|\$)))`Ssi", $content, $matches, PREG_OFFSET_CAPTURE, $offset)) { $offset = $matches[0][1]; // skip empty // -TODO should only ignore whitespaces, not all special chars if ('' === preg_replace('/&[a-z0-9#]+;|\\s+/i', '', strip_tags(htmlspecialchars($matches[0][0])))) { continue; } $paragraph_id--; if ($paragraph_id <= 0) { $insertAt = $before ? $offset : $offset + strlen($matches[0][0]); break; } // start at next offset $offset += strlen($matches[0][0]); } if (isset($insertAt)) { $ad_content = Advanced_Ads_Select::get_instance()->get_ad_by_method($placement_id, 'placement', $options); if ($insertAt === false) { $content .= $ad_content; // fallback: end-of-content } else { $content = substr($content, 0, $insertAt) . $ad_content . substr($content, $insertAt); } return $content; } return $content; }
/** * injected ad into content (before and after) * displays ALL ads * * @since 1.1.0 * @param str $content post content */ public function inject_content($content = '') { // run only within the loop on single pages of public post types $public_post_types = get_post_types(array('public' => true, 'publicly_queryable' => true), 'names', 'or'); // check if admin allows injection in all places $options = $this->plugin->options(); if (!isset($options['content-injection-everywhere'])) { // check if this is a singular page within the loop if (!is_singular($public_post_types) || !in_the_loop()) { return $content; } } $placements = get_option('advads-ads-placements', array()); if (!apply_filters('advanced-ads-can-inject-into-content', true, $content, $placements)) { return $content; } foreach ($placements as $_placement_id => $_placement) { if (empty($_placement['item']) || !isset($_placement['type'])) { continue; } $_options = isset($_placement['options']) ? $_placement['options'] : array(); switch ($_placement['type']) { case 'post_top': // TODO broken: does not serve placement but serves ad directly $content = Advanced_Ads_Select::get_instance()->get_ad_by_method($_placement_id, Advanced_Ads_Select::PLACEMENT, $_options) . $content; break; case 'post_bottom': $content .= Advanced_Ads_Select::get_instance()->get_ad_by_method($_placement_id, Advanced_Ads_Select::PLACEMENT, $_options); break; case 'post_content': $content = Advanced_Ads_Placements::inject_in_content($_placement_id, $_options, $content); break; } } return $content; }
/** * inject ads directly into the content * * @since 1.2.1 * @param string $placement_id id of the placement * @param arr $options placement options * @param string $content * @return type * @link inspired by http://www.wpbeginner.com/wp-tutorials/how-to-insert-ads-within-your-post-content-in-wordpress/ */ public static function &inject_in_content($placement_id, $options, &$content) { // test ad is emtpy $whitespaces = json_decode('"\\t\\n\\r \\u00A0"'); $adContent = Advanced_Ads_Select::get_instance()->get_ad_by_method($placement_id, 'placement', $options); if (trim($adContent, $whitespaces) === '') { return $content; } // parse document as DOM (fragment - having only a part of an actual post given) // -TODO may want to verify the wpcharset is supported by server (mb_list_encodings) // prevent messages from dom parser $wpCharset = get_bloginfo('charset'); // check if mbstring exists if (!function_exists('mb_convert_encoding')) { if ($wpCharset === "UTF-8") { $content = htmlspecialchars_decode(htmlentities($content, ENT_COMPAT, $wpCharset, false)); } else { return $content; } } else { $content = mb_convert_encoding($content, 'HTML-ENTITIES', $wpCharset); } // check which priority the wpautop filter has; might have been disabled on purpose $wpautop_priority = has_filter('the_content', 'wpautop'); if ($wpautop_priority && Advanced_Ads_Plugin::get_instance()->get_content_injection_priority() < $wpautop_priority) { $content = wpautop($content); } $dom = new DOMDocument('1.0', $wpCharset); // may loose some fragments or add autop-like code libxml_use_internal_errors(true); // avoid notices and warnings - html is most likely malformed $success = $dom->loadHtml('<!DOCTYPE html><html><meta http-equiv="Content-Type" content="text/html; charset=' . $wpCharset . '" /><body>' . $content); libxml_use_internal_errors(false); if ($success !== true) { // -TODO handle cases were dom-parsing failed (at least inform user) return $content; } // parse arguments $tag = isset($options['tag']) ? $options['tag'] : 'p'; $tag = preg_replace('/[^a-z0-9]/i', '', $tag); // simplify tag // only has before and after $before = isset($options['position']) && $options['position'] === 'before'; $paragraph_id = isset($options['index']) ? $options['index'] : 1; $paragraph_id = max(1, (int) $paragraph_id); $paragraph_select_from_bottom = isset($options['start_from_bottom']) && $options['start_from_bottom']; // select positions $xpath = new DOMXPath($dom); $items = $xpath->query('/html/body/' . $tag); $offset = null; $options = array('allowEmpty' => false); // if there are too few items at this level test nesting $options['itemLimit'] = $tag === 'p' ? 2 : 1; // allow hooks to change some options $options = apply_filters('advanced-ads-placement-content-injection-options', $options, $tag); if ($items->length < $options['itemLimit']) { $items = $xpath->query('/html/body/*/' . $tag); } // try third level as last resort if ($items->length < $options['itemLimit']) { $items = $xpath->query('/html/body/*/*/' . $tag); } // allow to select other elements $items = apply_filters('advanced-ads-placement-content-injection-items', $items, $xpath, $tag); // filter empty tags from items $paragraphs = array(); foreach ($items as $item) { if ($options['allowEmpty'] || isset($item->textContent) && trim($item->textContent, $whitespaces) !== '') { $paragraphs[] = $item; } } $paragraph_count = count($paragraphs); if ($paragraph_count >= $paragraph_id) { $offset = $paragraph_select_from_bottom ? $paragraph_count - $paragraph_id : $paragraph_id - 1; // convert HTML to XML! $adDom = new DOMDocument('1.0', $wpCharset); libxml_use_internal_errors(true); $adDom->loadHtml('<!DOCTYPE html><html><meta http-equiv="Content-Type" content="text/html; charset=' . $wpCharset . '" /><body>' . $adContent); $adNode = $adDom->lastChild->lastChild; // >html>body libxml_use_internal_errors(false); $adContent = $adDom->saveXML($adNode); $adContent = substr($adContent, 6, -7); $adNode = $dom->createDocumentFragment(); $adNode->appendXML($adContent); // inject $node = apply_filters('advanced-ads-placement-content-injection-node', $paragraphs[$offset], $tag, $before); if ($before) { $refNode = $node; $items = $xpath->query('/html/body/' . $tag); $refNode->parentNode->insertBefore($adNode, $refNode); } else { // append before next node or as last child to body $refNode = $node->nextSibling; if (isset($refNode)) { $refNode->parentNode->insertBefore($adNode, $refNode); } else { // append to body; -TODO using here that we only select direct children of the body tag $paragraphs[$offset]->parentNode->appendChild($adNode); } } } // convert to text-representation $content = $dom->saveHTML(); // remove head and tail (required for dom parser but unwanted for content) $content = substr($content, stripos($content, '<body>') + 6); $content = str_replace(array('</body>', '</html>'), '', $content); // no fall-back desired: if there are too few paragraphs do nothing // fix shortcode quotes (malformed by backend editor) $matches = array(); if (0 < preg_match_all('/\\[[^]]+\\]/Siu', $content, $matches, PREG_OFFSET_CAPTURE) && isset($matches[0])) { foreach ($matches[0] as $match) { $offset = $match[1]; $content = substr($content, 0, $offset) . str_replace(array('“', '″', '“', '"e;', '″'), '"', $match[0]) . substr($content, $offset + strlen($match[0])); } } return $content; }
/** * control the output of the group by type and amount of ads * * @since 1.4.8 * @return str $output output of ad(s) by ad */ public function output() { if (!$this->id) { return; } // load ads $ads = $this->load_all_ads(); if ($ads === array()) { return; } // get ad weights serving as an order here $weights = $this->get_ad_weights(); asort($weights); // if ads and weights don’t have the same keys, update weights array if (count($weights) == 0 && count($ads) > 0 || count($weights) != count($ads) || array_diff_key($weights, $ads) != array() || array_diff_key($ads, $weights) != array()) { $this->update_ad_weights(); $weights = $this->ad_weights; } // remove ads with 0 ad weight foreach ($weights as $_ad_id => $_ad_weight) { if ($_ad_weight === 0) { unset($weights[$_ad_id]); } } // order ads based on group type switch ($this->type) { case 'ordered': // order to highest weight first arsort($weights); $ordered_ad_ids = array_keys($weights); break; default: // default $ordered_ad_ids = $this->shuffle_ads($ads, $weights); } $ordered_ad_ids = apply_filters('advanced-ads-group-output-ad-ids', $ordered_ad_ids, $this->type, $ads, $weights); // load the ad output $output = array(); $ads_displayed = 0; $ad_count = apply_filters('advanced-ads-group-ad-count', $this->ad_count, $this); $ad_select = Advanced_Ads_Select::get_instance(); foreach ($ordered_ad_ids as $_ad_id) { // load the ad object $ad = $ad_select->get_ad_by_method($_ad_id, Advanced_Ads_Select::AD, $this->ad_args); if ($ad !== null) { $output[] = $ad; $ads_displayed++; // break the loop when maximum ads are reached if ($ads_displayed === $ad_count) { break; } } } // add the group to the global output array $advads = Advanced_Ads::get_instance(); $advads->current_ads[] = array('type' => 'group', 'id' => $this->id, 'title' => $this->name); // filter grouped ads output $output_string = implode('', apply_filters('advanced-ads-group-output-array', $output, $this)); // filter final group output return apply_filters('advanced-ads-group-output', $output_string, $this); }
/** * return content of an ad placement * * @since 1.1.0 * @param string $id slug of the ad placement * */ function get_ad_placement($id = '') { return Advanced_Ads_Select::get_instance()->get_ad_by_method($id, 'placement'); }
/** * control the output of the group by type and amount of ads * * @since 1.4.8 * @param array/null ordered_ad_ids ordered ids of the ads that belong to the group * @return str $output output of ad(s) by ad */ public function output($ordered_ad_ids = false) { // if $ordered_ad_ids was not passed to the function, load it $ordered_ad_ids = $ordered_ad_ids === false ? $this->get_ordered_ad_ids() : $ordered_ad_ids; if ($ordered_ad_ids === null) { return; } // load the ad output $output = array(); $ads_displayed = 0; $ad_count = apply_filters('advanced-ads-group-ad-count', $this->ad_count, $this); $ad_select = Advanced_Ads_Select::get_instance(); foreach ($ordered_ad_ids as $_ad_id) { // load the ad object $ad = $ad_select->get_ad_by_method($_ad_id, Advanced_Ads_Select::AD, $this->ad_args); if ($ad !== null) { $output[] = $ad; $ads_displayed++; // break the loop when maximum ads are reached if ($ads_displayed === $ad_count) { break; } } } // add the group to the global output array $advads = Advanced_Ads::get_instance(); $advads->current_ads[] = array('type' => 'group', 'id' => $this->id, 'title' => $this->name); // filter grouped ads output $output_string = implode('', apply_filters('advanced-ads-group-output-array', $output, $this)); // filter final group output return apply_filters('advanced-ads-group-output', $output_string, $this); }