settings = Share_My_Post_Settings::get(); // is_preview() is deliberately absent from the guard: an author // previewing a draft should see what the published post will look like. $post_id = (int) get_queried_object_id(); $state = array( 'is_admin' => is_admin(), 'is_builder' => self::is_builder(), 'is_feed' => is_feed(), 'is_embed' => is_embed(), // Blunt: this also switches off legitimate headless rendering. // Overridable through the filter below, which is why it is safe to // be blunt — the default protects the block editor's own preview // (which fetches content.rendered) from growing a share bar. 'is_rest' => defined('REST_REQUEST') && REST_REQUEST, 'is_singular' => is_singular(), 'password_required' => $post_id > 0 && post_password_required($post_id), 'post_type' => get_post_type($post_id), 'post_types' => $this->settings['general']['post_types'] ?? array(), 'enabled' => !empty($this->settings['general']['enabled']), ); $should = self::should_render_for($state); /** * Filters whether Share My Post renders on this request. * * The REST guard above is intentionally broad; use this filter to allow * share bars into a headless consumer's content.rendered. * * @param bool $should Whether to render. * @param array $state The facts the decision was made from. * @param int $post_id Post being viewed. */ if (!apply_filters('divitorque_share_my_post_should_render', $should, $state, $post_id)) { return; } $this->post_id = $post_id; $this->context = Share_My_Post_Url::context_from_post( $post_id, $this->settings['advanced']['mastodon_instance'] ?? '' ); $inline = !empty($this->settings['inline']['enabled']) && !empty(Share_My_Post_Networks::filter($this->settings['inline']['networks'] ?? array())); $floating = !empty($this->settings['floating']['enabled']) && !empty(Share_My_Post_Networks::filter($this->settings['floating']['networks'] ?? array())); if (!$inline && !$floating) { return; } add_action('wp_enqueue_scripts', array($this, 'enqueue'), 20); // Divi fires these two around the_content() inside its Post Content // module specifically so filters can detect that context — its own // docblock says as much. Paired, not a one-way latch: leaving the flag // set would hand the same exemption to every later the_content() call // on the page, and a related-posts grid below the article would grow a // share bar per item. add_action('et_theme_builder_before_render_post_content', array($this, 'enter_post_content')); add_action('et_theme_builder_after_render_post_content', array($this, 'exit_post_content')); if ($inline) { /** * Filters the_content priority for the inline bar. * * 20 puts it after wpautop (10), do_shortcode (11) and Divi's own * content wrappers, so the markup is emitted verbatim and Divi's * shortcodes have already been rendered. * * @param int $priority Filter priority. */ add_filter('the_content', array($this, 'filter_content'), apply_filters('divitorque_share_my_post_content_priority', 20)); } if ($floating) { add_action('wp_footer', array($this, 'render_floating'), 20); } } /** * Enqueue the extension's CSS and JS. * * Reached only when a bar will actually render, so a page without one ships * zero bytes of either. * * @return void */ public function enqueue() { $dist_url = DIVI_TORQUE_LITE_DIST_URL . 'divi5/'; $dist_dir = DIVI_TORQUE_LITE_DIR . 'dist/divi5/'; $ver = function ($file) use ($dist_dir) { $path = $dist_dir . $file; return file_exists($path) ? (string) filemtime($path) : DIVI_TORQUE_LITE_VERSION; }; wp_enqueue_style( 'divi-torque-lite-share-my-post', $dist_url . 'share-my-post.css', array(), $ver('share-my-post.css') ); wp_add_inline_style( 'divi-torque-lite-share-my-post', Share_My_Post_Render::inline_css($this->settings) ); wp_enqueue_script( 'divi-torque-lite-share-my-post', $dist_url . 'share-my-post.js', array(), $ver('share-my-post.js'), true ); wp_localize_script( 'divi-torque-lite-share-my-post', 'DiviTorqueShareMyPost', array( 'copied' => __('Link copied', 'addons-for-divi'), 'failed' => __('Could not copy the link', 'addons-for-divi'), ) ); } /** * Tier 2 — is *this* the_content call the post's own main content? * * Tier 1 already established that the request could show a bar. This adds * the loop-level facts, which change from call to call within one request: * a sidebar widget, a related-posts grid and an excerpt all run the_content * on the same page, and each would otherwise get its own share bar. * * @param string $content Post content. * * @return string */ public function filter_content($content) { if ($this->rendering) { return $content; } // The loop check is skipped inside Divi's Post Content module, which // renders the post without one (see $in_post_content). Everywhere else // it stands: a sidebar widget, a related-posts grid and an excerpt all // run the_content on the same page, and each would otherwise get its // own share bar. if (!$this->in_post_content && (!in_the_loop() || !is_main_query())) { return $content; } // Never relaxed. This is what keeps the bar off a *different* post // rendered through the same module, and it is what makes skipping the // loop check above safe rather than a hole. if (get_the_ID() !== $this->post_id) { return $content; } // get_the_excerpt() runs the_content on the way to building an excerpt. // Without this, every excerpt on the site would contain a share bar. if (doing_filter('get_the_excerpt')) { return $content; } $this->rendering = true; $bar = Share_My_Post_Render::bar('inline', $this->settings, $this->context); $this->rendering = false; if ('' === $bar) { return $content; } $position = $this->settings['inline']['position'] ?? 'below'; if ('above' === $position) { return $bar . $content; } if ('both' === $position) { return $bar . $content . $bar; } return $content . $bar; } /** * Divi's Post Content module is about to render. See $in_post_content. * * @return void */ public function enter_post_content() { $this->in_post_content = true; } /** * Divi's Post Content module has finished. * * @return void */ public function exit_post_content() { $this->in_post_content = false; } /** * Print the floating bar. * * wp_footer rather than wp_body_open: wp_body_open gives better DOM order, * but it is a WP 5.2 hook that older and custom themes do not all call, and * a position:fixed element gains nothing from being early in the document. * * @return void */ public function render_floating() { // Escaped per attribute inside Share_My_Post_Render. Deliberately not // routed through wp_kses_post(): its SVG allow-list is narrow and moves // between WordPress versions, and it strips stroke/width/height — which // would render every stroked utility glyph as an empty box. echo Share_My_Post_Render::bar('floating', $this->settings, $this->context); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } }