| @@ -1,761 +1,796 @@ | ||
| 1 | 1 | <?php |
| 2 | -//avoid direct calls to this file | |
| 3 | -if (!function_exists('add_action')) { | |
| 4 | - header('Status: 403 Forbidden'); | |
| 5 | - header('HTTP/1.1 403 Forbidden'); | |
| 6 | - exit(); | |
| 7 | -} | |
| 8 | -if (!class_exists('ISC_Public')) { | |
| 9 | 2 | |
| 10 | - /** | |
| 11 | - * handles all admin functionalities | |
| 12 | - * | |
| 13 | - * @since 1.7 | |
| 14 | - * @todo move frontend-only functions from general class here | |
| 15 | - */ | |
| 16 | - class ISC_Public extends ISC_Class { | |
| 3 | +use ISC\Standard_Source; | |
| 4 | +use ISC\Helpers; | |
| 17 | 5 | |
| 18 | - public function __construct() { | |
| 19 | - parent::__construct(); | |
| 6 | +/** | |
| 7 | + * Handles all frontend facing functionalities | |
| 8 | + * | |
| 9 | + * @todo move frontend-only functions from general class here | |
| 10 | + */ | |
| 11 | +class ISC_Public extends \ISC\Image_Sources\Image_Sources { | |
| 20 | 12 | |
| 21 | - add_action('wp_enqueue_scripts', array($this, 'front_scripts')); | |
| 22 | - add_action('wp_head', array($this, 'front_head')); | |
| 13 | + /** | |
| 14 | + * Instance of ISC_Public | |
| 15 | + * | |
| 16 | + * @var $instance | |
| 17 | + */ | |
| 18 | + protected static $instance = null; | |
| 23 | 19 | |
| 24 | - /** | |
| 25 | - * filters need to be above 10 in order to interpret also gallery shortcode | |
| 26 | - */ | |
| 27 | - add_filter('the_content', array($this, 'content_filter'), 20); | |
| 28 | - add_filter('the_excerpt', array($this, 'excerpt_filter'), 20); | |
| 29 | - add_shortcode('isc_list', array($this, 'list_post_attachments_with_sources_shortcode')); | |
| 30 | - add_shortcode('isc_list_all', array($this, 'list_all_post_attachments_sources_shortcode')); | |
| 31 | - } | |
| 20 | + /** | |
| 21 | + * ISC_Public constructor. | |
| 22 | + */ | |
| 23 | + public function __construct() { | |
| 24 | + parent::__construct(); | |
| 32 | 25 | |
| 33 | - /** | |
| 34 | - * Enqueue scripts for the front-end. | |
| 35 | - */ | |
| 36 | - public function front_scripts() { | |
| 37 | - // inject in footer as we only do stuff after dom-ready | |
| 38 | - wp_enqueue_script('isc_front_js', plugins_url('/assets/js/front-js.js', __FILE__), array('jquery'), ISCVERSION, true); | |
| 39 | - } | |
| 26 | + add_action( 'wp', [ $this, 'register_hooks' ] ); | |
| 27 | + } | |
| 40 | 28 | |
| 41 | - /** | |
| 42 | - * Front-end scripts in <head /> section. | |
| 43 | - */ | |
| 44 | - public function front_head() | |
| 45 | - { | |
| 46 | - $options = $this->get_isc_options(); | |
| 47 | - ?> | |
| 48 | - <script type="text/javascript"> | |
| 49 | - /* <![CDATA[ */ | |
| 50 | - var isc_front_data = | |
| 51 | - { | |
| 52 | - caption_position : '<?php echo $options['caption_position']; ?>', | |
| 53 | - } | |
| 54 | - /* ]]> */ | |
| 55 | - </script> | |
| 56 | - <style> | |
| 57 | - .isc-source { position: relative; } | |
| 58 | - </style> | |
| 59 | - <?php | |
| 60 | - } | |
| 29 | + /** | |
| 30 | + * Register hooks after the page is set up so that we have access to the post ID. | |
| 31 | + */ | |
| 32 | + public function register_hooks() { | |
| 33 | + // register the shortcode for the global list. Since this shortcode has to be placed manually and is normally only used once, checking if ISC is disabled on cetain pages doesn’t make sense | |
| 34 | + add_shortcode( 'isc_list_all', [ '\ISC\Image_Sources\Renderer\Global_List', 'execute_shortcode' ] ); | |
| 61 | 35 | |
| 62 | - /** | |
| 63 | - * add captions to post content and include source into caption, if this setting is enabled | |
| 64 | - * | |
| 65 | - * @param string $content post content | |
| 66 | - * @return string $content | |
| 67 | - * | |
| 68 | - * @update 1.4.3 | |
| 69 | - */ | |
| 70 | - public function content_filter($content) | |
| 71 | - { | |
| 36 | + if ( ! self::can_load_image_sources() ) { | |
| 37 | + return; | |
| 38 | + } | |
| 72 | 39 | |
| 73 | - // display inline sources | |
| 74 | - $options = $this->get_isc_options(); | |
| 75 | - if( isset($options['display_type']) && is_array($options['display_type']) && in_array('overlay', $options['display_type'] ) ) { | |
| 76 | - /** | |
| 77 | - * split content where `isc_stop_overlay` is found to not display overlays starting there | |
| 78 | - */ | |
| 79 | - if( strpos( $content, 'isc_stop_overlay' ) ){ | |
| 80 | - list( $content, $content_after ) = explode( 'isc_stop_overlay', $content, 2 ); | |
| 81 | - } else { | |
| 82 | - $content_after = ''; | |
| 83 | - } | |
| 40 | + // prepare the log | |
| 41 | + $this->prepare_log(); | |
| 84 | 42 | |
| 85 | - /** | |
| 86 | - * removed [caption], because this check runs after the hook that interprets shortcodes | |
| 87 | - * img tag is checked individually since there is a different order of attributes when images are used in gallery or individually | |
| 88 | - * | |
| 89 | - * 0 – full match | |
| 90 | - * 1 - <figure> if set | |
| 91 | - * 2 – alignment | |
| 92 | - * 3 – inner code starting with <a> | |
| 93 | - * 4 – opening link attribute | |
| 94 | - * 5 – "rel" attribute from link tag | |
| 95 | - * 6 – image id from link wp-att- value in "rel" attribute | |
| 96 | - * 7 – full img tag | |
| 97 | - * 8 – image URL | |
| 98 | - * 9 – (unused) | |
| 99 | - * 10 - </figure> | |
| 100 | - * | |
| 101 | - * tested with: | |
| 102 | - * * with and without [caption] | |
| 103 | - * * with and without link attibute | |
| 104 | - * | |
| 105 | - * potential issues: | |
| 106 | - * * line breaks in the code | |
| 107 | - */ | |
| 108 | - $pattern = '#(<[^>]*class="[^"]*(alignleft|alignright|alignnone|aligncenter).*)?((<a [^>]*(rel="[^"]*[^"]*wp-att-(\d+)"[^>]*)>)? *(<img [^>]*[^>]*src="(.+)".*\/?>).*(</a>)??[^<]*).*(<\/figure.*>)?#isU'; | |
| 109 | - $count = preg_match_all($pattern, $content, $matches); | |
| 43 | + if ( ISC\Image_Sources\Renderer\Caption::has_caption_style() ) { | |
| 44 | + add_action( 'wp_enqueue_scripts', [ $this, 'front_scripts' ] ); | |
| 45 | + add_action( 'wp_head', [ $this, 'front_head' ] ); | |
| 46 | + } | |
| 110 | 47 | |
| 111 | - // error_log(print_r($content, true)); error_log(print_r($matches, true)); | |
| 112 | - if (false !== $count) { | |
| 113 | - for ($i=0; $i < $count; $i++) { | |
| 48 | + // Content filters need to be above 10 in order to interpret also gallery shortcode | |
| 49 | + self::register_the_content_filters(); | |
| 50 | + add_filter( 'the_excerpt', [ $this, 'excerpt_filter' ], 20 ); | |
| 51 | + add_filter( 'render_block', [ $this, 'add_featured_image_source_to_excerpt_block' ], 10, 2 ); | |
| 114 | 52 | |
| 115 | - /** | |
| 116 | - * interpret the image tag | |
| 117 | - * | |
| 118 | - * we only need the ID if we don’t have it yet | |
| 119 | - * it can be retrieved from "wp-image-" class (single) or "aria-describedby="gallery-1-34" in gallery | |
| 120 | - */ | |
| 121 | - $id = $matches[6][$i]; | |
| 122 | - $img_tag = $matches[7][$i]; | |
| 53 | + add_shortcode( 'isc_list', [ $this, 'list_post_attachments_with_sources_shortcode' ] ); | |
| 54 | + } | |
| 123 | 55 | |
| 124 | - if( ! $id ){ | |
| 125 | - $success = preg_match('#wp-image-(\d+)|aria-describedby="gallery-1-(\d+)#is', $img_tag, $matches_id); | |
| 126 | - if( $success ){ | |
| 127 | - $id = $matches_id[1] ? intval( $matches_id[1] ) : intval( $matches_id[2] ); | |
| 128 | - } | |
| 129 | - } | |
| 56 | + /** | |
| 57 | + * Check if the current frontend page can run ISC properly | |
| 58 | + * | |
| 59 | + * @return bool | |
| 60 | + */ | |
| 61 | + public static function can_load_image_sources() { | |
| 130 | 62 | |
| 131 | - // if ID is still missing get image by URL | |
| 132 | - if( ! $id ){ | |
| 133 | - $src = $matches[8][$i]; | |
| 134 | - $id = $this->get_image_by_url($src); | |
| 135 | - } | |
| 63 | + $post_id = get_the_ID(); | |
| 136 | 64 | |
| 137 | - // don’t show caption for own image if admin choose not to do so | |
| 138 | - if($options['exclude_own_images']){ | |
| 139 | - if(get_post_meta($id, 'isc_image_source_own', true)) { | |
| 140 | - continue; | |
| 141 | - } | |
| 142 | - } | |
| 65 | + if ( $post_id | |
| 66 | + && is_singular() | |
| 67 | + /** | |
| 68 | + * Filter posts that should not output ISC information by their ID | |
| 69 | + * | |
| 70 | + * @param int[] void WP_Post IDs. | |
| 71 | + */ | |
| 72 | + && in_array( $post_id, apply_filters( 'isc_public_excluded_post_ids', [] ), true ) ) { | |
| 73 | + return false; | |
| 74 | + } | |
| 143 | 75 | |
| 144 | - // don’t display empty sources | |
| 145 | - if( !$source_string = $this->render_image_source_string( $id ) ) { | |
| 146 | - continue; | |
| 147 | - } | |
| 76 | + return apply_filters( 'isc_can_load', ISC\Plugin::is_module_enabled( 'image_sources' ) ); | |
| 77 | + } | |
| 148 | 78 | |
| 149 | - // get any alignment from the original code | |
| 150 | - preg_match('#alignleft|alignright|alignnone|aligncenter#is', $matches[0][$i], $matches_align); | |
| 151 | - $alignment = isset( $matches_align[0] ) ? $matches_align[0] : ''; | |
| 79 | + /** | |
| 80 | + * Prepare the log file | |
| 81 | + */ | |
| 82 | + public function prepare_log() { | |
| 83 | + if ( ISC_Log::clear_log() ) { | |
| 84 | + ISC_Log::delete_log_file(); | |
| 85 | + } | |
| 152 | 86 | |
| 153 | - $source = '<span class="isc-source-text">' . $options['source_pretext'] . ' ' . $source_string . '</span>'; | |
| 154 | - $old_content = $matches[3][$i]; | |
| 155 | - $new_content = str_replace('wp-image-' . $id, 'wp-image-' . $id . ' with-source', $old_content); | |
| 87 | + if ( ISC_Log::enabled() ) { | |
| 88 | + ISC_Log::log( '---' ); | |
| 89 | + ISC_Log::maybe_log_settings(); | |
| 90 | + } | |
| 91 | + } | |
| 156 | 92 | |
| 157 | - $content = str_replace($old_content, '<div id="isc_attachment_' . $id . '" class="isc-source ' . $alignment . '"> ' . $new_content . $source . '</div>', $content); | |
| 93 | + /** | |
| 94 | + * Register our the_content filters | |
| 95 | + */ | |
| 96 | + public static function register_the_content_filters() { | |
| 158 | 97 | |
| 159 | - } | |
| 160 | - } | |
| 161 | - /** | |
| 162 | - * attach follow content back | |
| 163 | - */ | |
| 164 | - $content = $content . $content_after; | |
| 165 | - } | |
| 98 | + // Content filters need to be above 10 in order to interpret also gallery shortcode | |
| 99 | + // needs to be added to remove_the_content_filters() as well to prevent infinite loops | |
| 100 | + add_filter( 'the_content', [ self::get_instance(), 'add_sources_to_content' ], 20 ); | |
| 101 | + } | |
| 166 | 102 | |
| 167 | - // attach image source list to content, if option is enabled | |
| 168 | - if ((isset($options['list_on_archives']) && $options['list_on_archives']) || | |
| 169 | - (is_singular() && isset($options['display_type']) && is_array($options['display_type']) && in_array('list', $options['display_type']))) { | |
| 170 | - $content = $content . $this->list_post_attachments_with_sources(); | |
| 171 | - } | |
| 103 | + /** | |
| 104 | + * Unregister our the_content filters | |
| 105 | + * used in places where we want to prevent infinite loops | |
| 106 | + */ | |
| 107 | + public static function remove_the_content_filters() { | |
| 108 | + remove_filter( 'the_content', [ self::get_instance(), 'add_sources_to_content' ], 20 ); | |
| 109 | + } | |
| 172 | 110 | |
| 173 | - return $content; | |
| 174 | - } | |
| 111 | + /** | |
| 112 | + * Get an instance of ISC_Public | |
| 113 | + * | |
| 114 | + * @return ISC_Public|null | |
| 115 | + */ | |
| 116 | + public static function get_instance() { | |
| 117 | + if ( null === self::$instance ) { | |
| 118 | + self::$instance = new self(); | |
| 119 | + } | |
| 120 | + return self::$instance; | |
| 121 | + } | |
| 175 | 122 | |
| 176 | - /** | |
| 177 | - * add image source of featured image to post excerpts | |
| 178 | - * | |
| 179 | - * @param string $excerpt post excerpt | |
| 180 | - * @return string $excerpt | |
| 181 | - * | |
| 182 | - * @update 1.4.3 | |
| 183 | - */ | |
| 184 | - public function excerpt_filter($excerpt) | |
| 185 | - { | |
| 123 | + /** | |
| 124 | + * Enqueue scripts for the front-end. | |
| 125 | + */ | |
| 126 | + public function front_scripts() { | |
| 127 | + // don’t add the script on AMP pages | |
| 128 | + if ( self::is_amp() ) { | |
| 129 | + return; | |
| 130 | + } | |
| 131 | + // inject in footer as we can only reliably position captions when the DOM is fully loaded | |
| 132 | + Helpers::enqueue_script( 'isc_caption', 'public/assets/js/captions.js' ); | |
| 186 | 133 | |
| 187 | - // display inline sources | |
| 188 | - $options = $this->get_isc_options(); | |
| 189 | - $post = get_post(); | |
| 134 | + $options = $this->get_options(); | |
| 190 | 135 | |
| 191 | - if (empty($options['list_on_excerpts'])) return $excerpt; | |
| 136 | + $caption_style = [ | |
| 137 | + 'position' => 'absolute', | |
| 138 | + 'font-size' => '0.9em', | |
| 139 | + 'background-color' => '#333', | |
| 140 | + 'color' => '#fff', | |
| 141 | + 'opacity' => '0.70', | |
| 142 | + 'padding' => '0 0.15em', | |
| 143 | + 'text-shadow' => 'none', | |
| 144 | + 'display' => 'block', | |
| 145 | + ]; | |
| 192 | 146 | |
| 193 | - $source_string = $this->get_thumbnail_source_string($post->ID); | |
| 147 | + $front_data = [ | |
| 148 | + 'caption_position' => isset( $options['caption_position'] ) ? esc_html( $options['caption_position'] ) : '', | |
| 149 | + /** | |
| 150 | + * Filter: isc_public_caption_default_style | |
| 151 | + * Allows to change the default caption style. | |
| 152 | + * | |
| 153 | + * @param array $caption_style The default caption style. | |
| 154 | + * @param array $options The options array. | |
| 155 | + */ | |
| 156 | + 'caption_style' => apply_filters( 'isc_public_caption_default_style', $caption_style, $options ), | |
| 157 | + ]; | |
| 194 | 158 | |
| 195 | - $excerpt = $excerpt . $source_string; | |
| 159 | + /** | |
| 160 | + * Filter: isc_public_caption_script_options | |
| 161 | + * | |
| 162 | + * @param array $front_data The data to be localized. | |
| 163 | + * @param array $options The options array. | |
| 164 | + */ | |
| 165 | + $filtered_front_data = apply_filters( 'isc_public_caption_script_options', $front_data, $options ); | |
| 196 | 166 | |
| 197 | - return $excerpt; | |
| 198 | - } | |
| 167 | + wp_localize_script( | |
| 168 | + 'isc_caption', | |
| 169 | + 'isc_front_data', | |
| 170 | + $filtered_front_data | |
| 171 | + ); | |
| 172 | + } | |
| 199 | 173 | |
| 200 | - /** | |
| 201 | - * create image sources list for all images of this post | |
| 202 | - * | |
| 203 | - * @since 1.0 | |
| 204 | - * @updated 1.1, 1.3.5 | |
| 205 | - * @updated 1.5 use new render function to create basic image source string | |
| 206 | - * | |
| 207 | - * @param int $post_id id of the current post/page | |
| 208 | - * @return echo output | |
| 209 | - */ | |
| 210 | - public function list_post_attachments_with_sources($post_id = 0) | |
| 211 | - { | |
| 212 | - global $post; | |
| 213 | - if (empty($post_id)) { | |
| 214 | - if (!empty($post->ID)) { | |
| 215 | - $post_id = $post->ID; | |
| 216 | - } else { | |
| 217 | - return; | |
| 218 | - } | |
| 219 | - } | |
| 174 | + /** | |
| 175 | + * Front-end scripts in <head /> section. | |
| 176 | + */ | |
| 177 | + public function front_head() { | |
| 178 | + // don’t add the script on AMP pages | |
| 179 | + if ( self::is_amp() ) { | |
| 180 | + return; | |
| 181 | + } | |
| 220 | 182 | |
| 221 | - $attachments = get_post_meta($post_id, 'isc_post_images', true); | |
| 183 | + $options = $this->get_options(); | |
| 184 | + ?> | |
| 185 | + <style> | |
| 186 | + .isc-source { position: relative; display: inline-block; line-height: initial; } | |
| 187 | + .isc-ai-label-icon { display: inline-block; width: auto; height: 1.25em; vertical-align: -0.25em; } | |
| 188 | + /* Hides the caption initially until it is positioned via JavaScript */ | |
| 189 | + .isc-source > .isc-source-text { display: none; } | |
| 190 | + .wp-block-cover .isc-source { position: static; } | |
| 191 | + <?php | |
| 192 | + // The 2022 theme adds display:block to the featured image block, which creates additional line breaks. `display: inline` fixes that. | |
| 193 | + ?> | |
| 194 | + span.isc-source-text a { display: inline; color: #fff; } | |
| 195 | + <?php | |
| 196 | + // force the overlay caption into the bottom left corner for lightboxes introduced in WP 6.4; only applied to the positions that are currently broken | |
| 197 | + if ( in_array( $options['caption_position'], [ 'top-center', 'top-right', 'center' ], true ) ) { | |
| 198 | + ?> | |
| 199 | + .wp-lightbox-overlay.active .isc-source-text { top: initial !important; left: initial !important; bottom: 0! important; } | |
| 200 | + <?php | |
| 201 | + } | |
| 202 | + ?> | |
| 203 | + </style> | |
| 204 | + <?php | |
| 205 | + } | |
| 222 | 206 | |
| 223 | - // if attachments is an empty string, search for images in it | |
| 224 | - if ($attachments == '') { | |
| 225 | - // unregister our content filter in order to prevent infinite loops when calling the_content in the next steps | |
| 226 | - remove_filter( 'the_content', array( $this, 'content_filter' ), 20 ); | |
| 207 | + /** | |
| 208 | + * Find images in the content, create the index, and maybe add sources to it | |
| 209 | + * | |
| 210 | + * @param string $content post content. | |
| 211 | + * @return string $content | |
| 212 | + */ | |
| 213 | + public function add_sources_to_content( $content ) { | |
| 214 | + // return if content is empty or null (the latter being an actual issue on a user’s site likely caused by a third-party plugin using the the_content filter wrongly) | |
| 215 | + if ( empty( $content ) ) { | |
| 216 | + ISC_Log::log( 'skipped adding sources because the content was empty' ); | |
| 217 | + return $content; | |
| 218 | + } | |
| 227 | 219 | |
| 228 | - $this->save_image_information_on_load(); | |
| 229 | - $this->update_image_posts_meta($post_id, $post->post_content); | |
| 220 | + if ( ISC\Indexer::is_global_list_page( $content ) ) { | |
| 221 | + ISC_Log::log( 'skipped adding sources because the content contains the Global List' ); | |
| 222 | + return $content; | |
| 223 | + } | |
| 230 | 224 | |
| 231 | - $attachments = get_post_meta($post_id, 'isc_post_images', true); | |
| 232 | - } | |
| 225 | + // return if this is not the main query or within the loop | |
| 226 | + if ( ! self::is_main_loop() ) { | |
| 227 | + ISC_Log::log( 'skipped adding sources because the content was loaded outside the main loop' ); | |
| 228 | + return $content; | |
| 229 | + } | |
| 233 | 230 | |
| 234 | - $return = ''; | |
| 235 | - if (!empty($attachments)) { | |
| 236 | - $atts = array(); | |
| 237 | - foreach ($attachments as $attachment_id => $attachment_array) { | |
| 231 | + if ( ISC_Log::is_type( 'backtrace' ) ) { | |
| 232 | + ISC_Log::log_stack_trace(); | |
| 233 | + } | |
| 238 | 234 | |
| 239 | - $own = get_post_meta($attachment_id, 'isc_image_source_own', true); | |
| 240 | - $source = get_post_meta($attachment_id, 'isc_image_source', true); | |
| 235 | + // maybe add source captions | |
| 236 | + if ( self::captions_enabled() && apply_filters( 'isc_public_add_source_captions_to_content', true ) ) { | |
| 237 | + $content = self::add_source_captions_to_content( $content ); | |
| 238 | + } else { | |
| 239 | + ISC_Log::log( 'not creating image overlays because the option is disabled for post content' ); | |
| 240 | + } | |
| 241 | 241 | |
| 242 | - // check if source of own images can be displayed | |
| 243 | - if ( ($own == '' && $source == '' ) || ($own != '' && $this->_options['exclude_own_images'])) { | |
| 244 | - unset($atts[$attachment_id]); | |
| 245 | - continue; | |
| 246 | - } else { | |
| 247 | - $atts[$attachment_id]['title'] = get_the_title($attachment_id); | |
| 248 | - $atts[$attachment_id]['source'] = $this->render_image_source_string($attachment_id); | |
| 249 | - } | |
| 242 | + /** | |
| 243 | + * Indexing the content for images here after we added overlays, so that we could also count | |
| 244 | + * non-images with overlays | |
| 245 | + */ | |
| 246 | + if ( apply_filters( 'isc_update_indexes_in_the_content', true ) ) { | |
| 247 | + \ISC\Indexer::update_indexes( $content ); | |
| 248 | + } | |
| 250 | 249 | |
| 251 | - } | |
| 250 | + /** | |
| 251 | + * The sources list is rendered after indexing the content for images | |
| 252 | + * since it is build on top of it | |
| 253 | + */ | |
| 254 | + // maybe add source list | |
| 255 | + return self::add_source_list_to_content( $content ); | |
| 256 | + } | |
| 252 | 257 | |
| 253 | - $return = $this->render_attachments($atts); | |
| 254 | - } | |
| 258 | + /** | |
| 259 | + * Check main loop | |
| 260 | + * | |
| 261 | + * @return bool true if we are currently on something that could be called the "main loop" | |
| 262 | + */ | |
| 263 | + public static function is_main_loop() { | |
| 255 | 264 | |
| 256 | - return $return; | |
| 257 | - } | |
| 265 | + // Exception: Oxygen builder, where `is_the_loop()` is false | |
| 266 | + if ( defined( 'CT_VERSION' ) && defined( 'CT_FW_PATH' ) ) { | |
| 267 | + return true; | |
| 268 | + } elseif ( self::is_amp_reader_mode() && is_single() ) { | |
| 269 | + // Exception: AMP reader mode and the AMPforWP plugin need this extra check | |
| 270 | + return true; | |
| 271 | + } elseif ( in_the_loop() && is_main_query() ) { | |
| 272 | + return true; | |
| 273 | + } | |
| 258 | 274 | |
| 259 | - /** | |
| 260 | - * render attachment list | |
| 261 | - * | |
| 262 | - * @param array $attachments | |
| 263 | - * @updated 1.3.5 | |
| 264 | - * @updated 1.5 removed rendering the license to an earlier function | |
| 265 | - */ | |
| 266 | - protected function render_attachments($attachments) | |
| 267 | - { | |
| 268 | - // don't display anything, if no image sources displayed | |
| 269 | - if ($attachments == array()) { | |
| 270 | - return ; | |
| 271 | - } | |
| 275 | + return false; | |
| 276 | + } | |
| 272 | 277 | |
| 278 | + /** | |
| 279 | + * Add captions to post content and include source into caption, if this setting is enabled | |
| 280 | + * | |
| 281 | + * @param string $content post content. | |
| 282 | + * @return string $content | |
| 283 | + */ | |
| 284 | + public function add_source_captions_to_content( $content ) { | |
| 285 | + ISC_Log::log( 'start creating source overlays' ); | |
| 273 | 286 | |
| 274 | - $options = $this->get_isc_options(); | |
| 287 | + /** | |
| 288 | + * Split content where `isc_stop_overlay` is found to not display overlays starting there | |
| 289 | + */ | |
| 290 | + if ( strpos( $content, 'isc_stop_overlay' ) !== false ) { | |
| 291 | + list( $content, $content_after ) = explode( 'isc_stop_overlay', $content, 2 ); | |
| 292 | + } else { | |
| 293 | + $content_after = ''; | |
| 294 | + } | |
| 275 | 295 | |
| 276 | - ob_start(); | |
| 277 | - $headline = $this->_options['image_list_headline']; | |
| 278 | - ?><div class="isc_image_list_box"><?php | |
| 279 | - printf('<p class="isc_image_list_title">%1$s</p>', $headline); ?> | |
| 280 | - <ul class="isc_image_list"><?php | |
| 296 | + $content = apply_filters( 'isc_public_caption_regex_content', $content ); | |
| 297 | + $matches = $this->html_analyzer->extract_images_from_html( $content ); | |
| 281 | 298 | |
| 282 | - foreach ($attachments as $atts_id => $atts_array) { | |
| 283 | - if (empty($atts_array['source'])) { | |
| 284 | - continue; | |
| 285 | - } | |
| 286 | - printf('<li>%1$s: %2$s</li>', $atts_array['title'], $atts_array['source']); | |
| 287 | - } | |
| 288 | - ?></ul></div><?php | |
| 289 | - return ob_get_clean(); | |
| 290 | - } | |
| 299 | + ISC_Log::log( 'embedded images found: ' . count( $matches ) ); | |
| 291 | 300 | |
| 292 | - /** | |
| 293 | - * shortcode function to list all image sources | |
| 294 | - * @param arr $atts | |
| 295 | - */ | |
| 296 | - public function list_post_attachments_with_sources_shortcode($atts = array()) | |
| 297 | - { | |
| 298 | - global $post; | |
| 299 | - extract(shortcode_atts(array('id' => 0), $atts)); | |
| 301 | + if ( ! count( $matches ) ) { | |
| 302 | + return $content . $content_after; | |
| 303 | + } | |
| 300 | 304 | |
| 301 | - // if $id not set, use the current ID from the post | |
| 302 | - if (empty($id)) { | |
| 303 | - $id = $post->ID; | |
| 304 | - } | |
| 305 | + // gather elements already replaced to prevent duplicate sources, see GitHub #105 | |
| 306 | + $replaced = []; | |
| 305 | 307 | |
| 306 | - if (empty($id)) { | |
| 307 | - return; | |
| 308 | - } | |
| 309 | - return $this->list_post_attachments_with_sources($id); | |
| 310 | - } | |
| 308 | + foreach ( $matches as $_match ) { | |
| 309 | + if ( ! $_match['img_src'] ) { | |
| 310 | + ISC_Log::log( 'skipped an image because src is empty' ); | |
| 311 | + continue; | |
| 312 | + } | |
| 313 | + $hash = md5( $_match['inner_code'] ); | |
| 311 | 314 | |
| 312 | - /** | |
| 313 | - * create shortcode to list all image sources in the frontend | |
| 314 | - * @param array $atts | |
| 315 | - * @since 1.1.3 | |
| 316 | - */ | |
| 317 | - public function list_all_post_attachments_sources_shortcode($atts = array()) | |
| 318 | - { | |
| 319 | - extract(shortcode_atts(array( | |
| 320 | - 'per_page' => 99999, | |
| 321 | - 'before_links' => '', | |
| 322 | - 'after_links' => '', | |
| 323 | - 'prev_text' => '« Previous', | |
| 324 | - 'next_text' => 'Next »', | |
| 325 | - 'included' => 'displayed' | |
| 326 | - ), | |
| 327 | - $atts)); | |
| 315 | + if ( in_array( $hash, $replaced, true ) ) { | |
| 316 | + ISC_Log::log( 'skipped an image because it appears multiple times' ); | |
| 317 | + continue; | |
| 318 | + } else { | |
| 319 | + $replaced[] = $hash; | |
| 320 | + } | |
| 328 | 321 | |
| 329 | - if ('« Previous' == $prev_text) | |
| 330 | - $prev_text = __('« Previous', 'image-source-control-isc'); | |
| 331 | - if ('Next »' == $next_text) | |
| 332 | - $next_text = __('Next »', 'image-source-control-isc'); | |
| 322 | + $id = $this->html_analyzer->extract_image_id( $_match['inner_code'] ); | |
| 333 | 323 | |
| 334 | - // retrieve all attachments | |
| 335 | - $args = array( | |
| 336 | - 'post_type' => 'attachment', | |
| 337 | - 'numberposts' => -1, | |
| 338 | - 'post_status' => null, | |
| 339 | - 'post_parent' => null | |
| 340 | - ); | |
| 324 | + // if ID is still missing get the image by URL | |
| 325 | + if ( ! $id ) { | |
| 326 | + $id = ISC_Model::get_image_by_url( $_match['img_src'] ); | |
| 327 | + ISC_Log::log( sprintf( 'ID for source "%s": "%s"', $_match['img_src'], $id ) ); | |
| 328 | + } | |
| 341 | 329 | |
| 342 | - // check mode | |
| 343 | - if( $included === 'all' ){ | |
| 344 | - // load all images | |
| 330 | + $source = ISC\Image_Sources\Renderer\Caption::get( $id ); | |
| 345 | 331 | |
| 346 | - } else { // load only images attached to posts | |
| 347 | - $args['meta_query'] = array( | |
| 348 | - array( | |
| 349 | - 'key' => 'isc_image_posts', | |
| 350 | - 'value' => 'a:0:{}', | |
| 351 | - 'compare' => '!=' | |
| 352 | - ) | |
| 353 | - ); | |
| 354 | - } | |
| 332 | + if ( ! $source ) { | |
| 333 | + ISC_Log::log( sprintf( 'skipped empty sources string for ID "%s"', $id ) ); | |
| 334 | + continue; | |
| 335 | + } | |
| 355 | 336 | |
| 356 | - $attachments = get_posts($args); | |
| 357 | - if (empty($attachments)) { | |
| 358 | - return; | |
| 359 | - } | |
| 337 | + // get any alignment from the original code | |
| 338 | + preg_match( '#alignleft|alignright|alignnone|aligncenter#is', $_match['full'], $matches_align ); | |
| 339 | + $alignment = $matches_align[0] ?? ''; | |
| 340 | + $old_content = $_match['inner_code']; | |
| 341 | + $markup_before = ''; | |
| 342 | + $markup_after = ''; | |
| 360 | 343 | |
| 361 | - $options = $this->get_isc_options(); | |
| 344 | + // default style | |
| 345 | + if ( ISC\Image_Sources\Renderer\Caption::has_caption_style() ) { | |
| 346 | + $markup_before = '<span id="isc_attachment_' . $id . '" class="isc-source ' . $alignment . '">'; | |
| 347 | + $markup_after = '</span>'; | |
| 348 | + } | |
| 362 | 349 | |
| 363 | - $connected_atts = array(); | |
| 350 | + $content = str_replace( | |
| 351 | + $old_content, | |
| 352 | + sprintf( | |
| 353 | + '%s%s%s%s', | |
| 354 | + apply_filters( 'isc_overlay_html_markup_before', $markup_before, $id ), | |
| 355 | + str_replace( 'wp-image-' . $id, 'wp-image-' . $id . ' with-source', $old_content ), // new content | |
| 356 | + $source, | |
| 357 | + apply_filters( 'isc_overlay_html_markup_after', $markup_after, $id ) | |
| 358 | + ), | |
| 359 | + $content | |
| 360 | + ); | |
| 361 | + } | |
| 362 | + ISC_Log::log( 'number of unique images found: ' . count( $replaced ) ); | |
| 364 | 363 | |
| 365 | - foreach ($attachments as $_attachment) { | |
| 366 | - $connected_atts[$_attachment->ID]['source'] = get_post_meta($_attachment->ID, 'isc_image_source', true); | |
| 367 | - $connected_atts[$_attachment->ID]['own'] = get_post_meta($_attachment->ID, 'isc_image_source_own', true); | |
| 368 | - // jump to next element if author images are not to be included in the list | |
| 369 | - if($options['exclude_own_images'] && '' != $connected_atts[$_attachment->ID]['own']) { | |
| 370 | - unset($connected_atts[$_attachment->ID]); | |
| 371 | - continue; | |
| 372 | - } | |
| 364 | + /** | |
| 365 | + * Attach follow content back | |
| 366 | + */ | |
| 367 | + return $content . $content_after; | |
| 368 | + } | |
| 373 | 369 | |
| 374 | - $connected_atts[$_attachment->ID]['title'] = $_attachment->post_title; | |
| 375 | - $connected_atts[$_attachment->ID]['author_name'] = ''; | |
| 376 | - if ('' != $connected_atts[$_attachment->ID]['own']) { | |
| 377 | - $connected_atts[$_attachment->ID]['author_name'] = get_the_author_meta('display_name', $_attachment->post_author); | |
| 378 | - } | |
| 370 | + /** | |
| 371 | + * Return true if captions are enabled | |
| 372 | + * | |
| 373 | + * @return bool | |
| 374 | + */ | |
| 375 | + public static function captions_enabled(): bool { | |
| 376 | + $options = self::get_instance()->get_options(); | |
| 377 | + return ! empty( $options['display_type'] ) && is_array( $options['display_type'] ) && in_array( 'overlay', $options['display_type'], true ); | |
| 378 | + } | |
| 379 | 379 | |
| 380 | - $metadata = get_post_meta($_attachment->ID, 'isc_image_posts', true); | |
| 381 | - $usage_data = ''; | |
| 380 | + /** | |
| 381 | + * Add the Per-page list if the option is enabled | |
| 382 | + * | |
| 383 | + * @param string $content post content. | |
| 384 | + * @return string $content | |
| 385 | + */ | |
| 386 | + public function add_source_list_to_content( $content ) { | |
| 382 | 387 | |
| 383 | - if (is_array($metadata) && array() != $metadata) { | |
| 384 | - $usage_data .= "<ul style='margin: 0;'>"; | |
| 385 | - $usage_data_array = array(); | |
| 386 | - foreach($metadata as $data) { | |
| 387 | - // only list published posts | |
| 388 | - if(get_post_status($data) == 'publish') { | |
| 389 | - $usage_data_array[] = sprintf(__('<li><a href="%1$s" title="View %2$s">%3$s</a></li>', 'image-source-control-isc'), | |
| 390 | - esc_url(get_permalink($data)), | |
| 391 | - esc_attr(get_the_title($data)), | |
| 392 | - esc_html(get_the_title($data)) | |
| 393 | - ); | |
| 394 | - } | |
| 395 | - } | |
| 396 | - if ( 'all' !== $included && $usage_data_array === array() ) { | |
| 397 | - unset($connected_atts[$_attachment->ID]); | |
| 398 | - continue; | |
| 399 | - } | |
| 400 | - $usage_data .= implode( '', $usage_data_array ); | |
| 401 | - $usage_data .= "</ul>"; | |
| 402 | - } | |
| 388 | + /** | |
| 389 | + * Display the Per-page list | |
| 390 | + * on single pages if the following option is enabled: How to display source in Frontend > list below content | |
| 391 | + * on archive pages and home pages with posts if the following option is enabled: Archive Pages > Display sources list below full posts | |
| 392 | + */ | |
| 393 | + if ( $this->can_add_list_to_content() ) { | |
| 394 | + ISC_Log::log( 'start creating source list below content' ); | |
| 395 | + $content = $content . $this->list_post_attachments_with_sources(); | |
| 396 | + } | |
| 403 | 397 | |
| 404 | - $connected_atts[$_attachment->ID]['posts'] = $usage_data; | |
| 405 | - } | |
| 398 | + return $content; | |
| 399 | + } | |
| 406 | 400 | |
| 407 | - $total = count($connected_atts); | |
| 401 | + /** | |
| 402 | + * Add image source of featured image to post excerpts | |
| 403 | + * | |
| 404 | + * @param string $excerpt post excerpt. | |
| 405 | + * @return string $excerpt | |
| 406 | + * | |
| 407 | + * @update 1.4.3 | |
| 408 | + */ | |
| 409 | + public function excerpt_filter( $excerpt ) { | |
| 408 | 410 | |
| 409 | - if (0 == $total) | |
| 410 | - return; | |
| 411 | + // display inline sources | |
| 412 | + $options = $this->get_options(); | |
| 413 | + $post = get_post(); | |
| 411 | 414 | |
| 412 | - $page = isset($_GET['isc-page']) ? intval($_GET['isc-page']) : 1; | |
| 413 | - $down_limit = 1; // First page | |
| 415 | + if ( empty( $options['list_on_excerpts'] ) ) { | |
| 416 | + return $excerpt; | |
| 417 | + } | |
| 414 | 418 | |
| 415 | - $up_limit = 1; | |
| 419 | + return $excerpt . $this->get_thumbnail_source_string( $post->ID ); | |
| 420 | + } | |
| 416 | 421 | |
| 417 | - if ($per_page < $total) { | |
| 418 | - $rem = $total % $per_page; // The Remainder of $total/$per_page | |
| 419 | - $up_limit = ($total - $rem) / $per_page; | |
| 420 | - if (0 < $rem) { | |
| 421 | - $up_limit++; //If rem is positive, add the last page that contains less than $per_page attachment; | |
| 422 | - } | |
| 423 | - } | |
| 422 | + /** | |
| 423 | + * Add image source of featured image to the post excerpt block in the frontend | |
| 424 | + * | |
| 425 | + * @param string $block_content rendered content of the block. | |
| 426 | + * @param array $block full block details. | |
| 427 | + * @return string $excerpt | |
| 428 | + */ | |
| 429 | + public function add_featured_image_source_to_excerpt_block( $block_content, $block ) { | |
| 424 | 430 | |
| 425 | - ob_start(); | |
| 426 | - if ( 2 > $up_limit ) { | |
| 427 | - $this->display_all_attachment_list($connected_atts); | |
| 428 | - } else { | |
| 429 | - $starting_atts = $per_page * ($page - 1); // for page 2 and 3 $per_page start display on $connected_atts[3*(2-1) = 3] | |
| 430 | - $paged_atts = array_slice($connected_atts, $starting_atts, $per_page, true); | |
| 431 | - $this->display_all_attachment_list($paged_atts); | |
| 432 | - $this->pagination_links($up_limit, $before_links, $after_links, $prev_text, $next_text); | |
| 433 | - } | |
| 434 | - if (isset($options['webgilde']) && true == $options['webgilde']) { | |
| 435 | - ?> | |
| 436 | - <p class="isc-backlink"><?php printf(__('Image list created by <a href="%s" title="Image Source Control">Image Source Control Plugin</a>', 'image-source-control-isc'), WEBGILDE); ?></p> | |
| 437 | - <?php | |
| 438 | - } | |
| 431 | + if ( isset( $block['blockName'] ) && $block['blockName'] !== 'core/post-excerpt' ) { | |
| 432 | + return $block_content; | |
| 433 | + } | |
| 439 | 434 | |
| 440 | - $output = ob_get_clean(); | |
| 441 | - return $output; | |
| 442 | - } | |
| 435 | + $options = $this->get_options(); | |
| 436 | + if ( empty( $options['list_on_excerpts'] ) ) { | |
| 437 | + return $block_content; | |
| 438 | + } | |
| 443 | 439 | |
| 440 | + $post = get_post(); | |
| 441 | + if ( ! $post || empty( $post->ID ) ) { | |
| 442 | + return $block_content; | |
| 443 | + } | |
| 444 | 444 | |
| 445 | - /** | |
| 446 | - * performs rendering of all attachments list | |
| 447 | - * @since 1.1.3 | |
| 448 | - * @update 1.5 added new method to get source | |
| 449 | - */ | |
| 450 | - public function display_all_attachment_list($atts) | |
| 451 | - { | |
| 452 | - if (!is_array($atts) || $atts == array()) | |
| 453 | - return; | |
| 454 | - $options = $this->get_isc_options(); | |
| 445 | + return str_replace( | |
| 446 | + '</p></div>', | |
| 447 | + $this->get_thumbnail_source_string( $post->ID ) . '</p></div>', | |
| 448 | + $block_content | |
| 449 | + ); | |
| 450 | + } | |
| 455 | 451 | |
| 456 | - /** | |
| 457 | - * added comment `isc_stop_overlay` as a class to the table to suppress overlays within it starting at that point | |
| 458 | - * todo: allow overlays to start again after the table | |
| 459 | - * | |
| 460 | - */ | |
| 461 | - ?><div class="isc_all_image_list_box isc_stop_overlay"> | |
| 462 | - <table> | |
| 463 | - <thead> | |
| 464 | - <?php if ($options['thumbnail_in_list']) : ?> | |
| 465 | - <th><?php _e('Thumbnail', 'image-source-control-isc'); ?></th> | |
| 466 | - <?php endif; ?> | |
| 467 | - <th><?php _e("Attachment's ID", 'image-source-control-isc'); ?></th> | |
| 468 | - <th><?php _e('Title', 'image-source-control-isc'); ?></th> | |
| 469 | - <th><?php _e('Attached to', 'image-source-control-isc'); ?></th> | |
| 470 | - <th><?php _e('Source', 'image-source-control-isc'); ?></th> | |
| 471 | - </thead> | |
| 472 | - <tbody> | |
| 473 | - <?php foreach ($atts as $id => $data) : ?> | |
| 474 | - <?php | |
| 475 | - $source = $this->render_image_source_string($id); | |
| 476 | - ?> | |
| 477 | - <tr> | |
| 478 | - <?php | |
| 479 | - $v_align = ''; | |
| 480 | - if ($options['thumbnail_in_list']) : | |
| 481 | - $v_align = 'style="vertical-align: top;"'; | |
| 482 | - ?> | |
| 483 | - <?php if ('custom' != $options['thumbnail_size']) : ?> | |
| 484 | - <td><?php echo wp_get_attachment_image($id, $options['thumbnail_size']); ?></td> | |
| 485 | - <?php else : ?> | |
| 486 | - <td><?php echo wp_get_attachment_image($id, array($options['thumbnail_width'], $options['thumbnail_height'])); ?></td> | |
| 487 | - <?php endif; ?> | |
| 488 | - <?php endif; ?> | |
| 489 | - <td <?php echo $v_align;?>><?php echo $id; ?></td> | |
| 490 | - <td <?php echo $v_align;?>><?php echo $data['title']; ?></td> | |
| 491 | - <td <?php echo $v_align;?>><?php echo $data['posts']; ?></td> | |
| 492 | - <td <?php echo $v_align;?>><?php echo $source; ?></td> | |
| 493 | - </tr> | |
| 494 | - <?php endforeach; ?> | |
| 495 | - </tbody> | |
| 496 | - </table></div> | |
| 497 | - <?php | |
| 498 | - } | |
| 452 | + /** | |
| 453 | + * Create image sources list for all images of this post | |
| 454 | + * | |
| 455 | + * @since 1.0 | |
| 456 | + * @updated 1.1, 1.3.5 | |
| 457 | + * @updated 1.5 use new render function to create basic image source string | |
| 458 | + * | |
| 459 | + * @param integer $post_id id of the current post/page. | |
| 460 | + * @return string output | |
| 461 | + */ | |
| 462 | + public function list_post_attachments_with_sources( $post_id = 0 ) { | |
| 463 | + global $post; | |
| 499 | 464 | |
| 500 | - /** | |
| 501 | - * Render pagination links, use $before_links and after_links to wrap pagination links inside an additional block | |
| 502 | - * @param int $max_page total page count | |
| 503 | - * @param string $before_links optional html to display before pagination links | |
| 504 | - * @param string $after_links optional html to display after pagination links | |
| 505 | - * @param string $prev_text text for the previous page link | |
| 506 | - * @param string $next_text text for the next page link | |
| 507 | - * @since 1.1.3 | |
| 508 | - * | |
| 509 | - */ | |
| 510 | - public function pagination_links($max_page, $before_links, $after_links, $prev_text, $next_text) | |
| 511 | - { | |
| 512 | - if ((!isset($max_page)) || (!isset($before_links)) || (!isset($after_links)) || (!isset($prev_text)) || (!isset($next_text))) | |
| 513 | - return; | |
| 514 | - if (!empty($before_links)) | |
| 515 | - echo $before_links; | |
| 516 | - ?> | |
| 517 | - <div class="isc-paginated-links"> | |
| 518 | - <?php | |
| 519 | - $page = isset($_GET['isc-page']) ? intval($_GET['isc-page']) : 1; | |
| 520 | - if ($max_page < $page) { | |
| 521 | - $page = $max_page; | |
| 522 | - } | |
| 523 | - if ($page < 1) { | |
| 524 | - $page = 1; | |
| 525 | - } | |
| 526 | - $min_page = 1; | |
| 527 | - $backward_distance = $page - $min_page; | |
| 528 | - $forward_distance = $max_page - $page; | |
| 465 | + if ( empty( $post_id ) && ! empty( $post->ID ) ) { | |
| 466 | + $post_id = $post->ID; | |
| 467 | + } | |
| 529 | 468 | |
| 530 | - $page_link = get_page_link(); | |
| 469 | + if ( isset( $_SERVER['REQUEST_URI'] ) ) { | |
| 470 | + ISC_Log::log( 'enter list_post_attachments_with_sources() for ' . $_SERVER['REQUEST_URI'] . ' and post_id ' . $post_id ); | |
| 471 | + } | |
| 531 | 472 | |
| 532 | - /** | |
| 533 | - * Remove the query_string of the page_link (?page_id=xyz for the standard permalink structure), | |
| 534 | - * which is already captured in $_SERVER['QUERY_STRING']. | |
| 535 | - * @todo replace regex with other value (does WP store the url path without attributes somewhere? | |
| 536 | - * >get_page_link() returns the permalink but for the default WP permalink structure, the permalink looks like "http://domain.tld/?p=52", while $_GET | |
| 537 | - * still has a field named 'page_id' with the same value of 52. | |
| 538 | - */ | |
| 473 | + // don’t do anything on REST requests since that causes issues with the block editor rendering a "post" for each image | |
| 474 | + // just in case, we also prevent output for "content" from attachments | |
| 475 | + if ( defined( 'REST_REQUEST' ) || ! isset( $post->post_type ) || 'attachment' === $post->post_type ) { | |
| 476 | + ISC_Log::log( 'exit because of invalid request' ); | |
| 477 | + return ''; | |
| 478 | + } | |
| 539 | 479 | |
| 540 | - $pos = strpos($page_link, '?'); | |
| 541 | - if (false !== $pos) { | |
| 542 | - $page_link = substr($page_link, 0, $pos); | |
| 543 | - } | |
| 480 | + // do not render an empty source list on non-post pages unless explicitly stated. | |
| 481 | + if ( empty( $post_id ) ) { | |
| 482 | + /** | |
| 483 | + * Filter: isc_source_list_empty_output | |
| 484 | + * allow to return some output even if there is no post ID (e.g., on archive pages). | |
| 485 | + */ | |
| 486 | + return apply_filters( 'isc_source_list_empty_output', '' ); | |
| 487 | + } | |
| 544 | 488 | |
| 545 | - /** | |
| 546 | - * Unset the actual "$_GET['isc-page']" variable (if is set). Pagination variable will be appended to the new query string with a different value for each | |
| 547 | - * pagination link. | |
| 548 | - */ | |
| 489 | + // allow developers to override the output of the sources list | |
| 490 | + $override = apply_filters( 'isc_sources_list_override_output', false, $post_id ); | |
| 491 | + if ( $override ) { | |
| 492 | + ISC_Log::log( 'exit because override was set' ); | |
| 493 | + return $override; | |
| 494 | + } | |
| 549 | 495 | |
| 550 | - if (isset($_GET['isc-page'])) { | |
| 551 | - unset($_GET['isc-page']); | |
| 552 | - } | |
| 496 | + $attachments = get_post_meta( $post_id, 'isc_post_images', true ); | |
| 497 | + $exclude_standard = Standard_Source::standard_source_is( 'exclude' ); | |
| 553 | 498 | |
| 554 | - $query_string = http_build_query($_GET); | |
| 499 | + if ( ! empty( $attachments ) && is_array( $attachments ) ) { | |
| 500 | + ISC_Log::log( sprintf( 'going through %d attachments', count( $attachments ) ) ); | |
| 501 | + $atts = []; | |
| 502 | + foreach ( $attachments as $attachment_id => $attachment_array ) { | |
| 503 | + $image_uses_standard_source = Standard_Source::use_standard_source( $attachment_id ); | |
| 504 | + $source = ISC\Image_Sources\Image_Sources::sanitize_source_html( self::get_image_source_text_raw( $attachment_id ) ); | |
| 555 | 505 | |
| 556 | - $isc_query_tag = ''; | |
| 557 | - if (empty($query_string)) { | |
| 558 | - $isc_query_tag = '?isc-page='; | |
| 559 | - } else { | |
| 560 | - $query_string = '?' . $query_string; | |
| 561 | - $isc_query_tag = '&isc-page='; | |
| 562 | - } | |
| 506 | + // check if source of own images can be displayed | |
| 507 | + if ( ( ! $image_uses_standard_source && $source === '' ) || ( $image_uses_standard_source && $exclude_standard ) ) { | |
| 508 | + if ( $image_uses_standard_source && $exclude_standard ) { | |
| 509 | + ISC_Log::log( sprintf( 'image %d: "own" sources are excluded', $attachment_id ) ); | |
| 510 | + } else { | |
| 511 | + ISC_Log::log( sprintf( 'image %d: skipped because of empty source', $attachment_id ) ); | |
| 512 | + } | |
| 513 | + unset( $atts[ $attachment_id ] ); | |
| 514 | + } else { | |
| 515 | + $atts[ $attachment_id ]['title'] = get_the_title( $attachment_id ); | |
| 516 | + ISC_Log::log( sprintf( 'image %d: getting title "%s"', $attachment_id, $atts[ $attachment_id ]['title'] ) ); | |
| 517 | + $atts[ $attachment_id ]['source'] = ISC\Image_Sources\Renderer\Image_Source_String::get( $attachment_id ); | |
| 518 | + if ( ! $atts[ $attachment_id ]['source'] ) { | |
| 519 | + ISC_Log::log( sprintf( 'image %d: skipped because of empty source', $attachment_id ) ); | |
| 520 | + unset( $atts[ $attachment_id ] ); | |
| 521 | + } | |
| 522 | + } | |
| 523 | + } | |
| 563 | 524 | |
| 564 | - if ($min_page != $page) { | |
| 565 | - ?> | |
| 566 | - <a href="<?php echo $page_link . $query_string . $isc_query_tag . ($page-1); ?>" class="prev page-numbers"><?php echo $prev_text; ?></a> | |
| 567 | - <?php | |
| 568 | - } | |
| 525 | + return $this->render_attachments( $atts ); | |
| 526 | + } else { | |
| 527 | + // see description above. | |
| 528 | + ISC_Log::log( 'exit without any images found ' ); | |
| 529 | + // allow to return result if the source list is empty. | |
| 530 | + return apply_filters( 'isc_source_list_empty_output', '' ); | |
| 531 | + } | |
| 532 | + } | |
| 569 | 533 | |
| 570 | - if (5 < $max_page) { | |
| 534 | + /** | |
| 535 | + * Render attachment list | |
| 536 | + * | |
| 537 | + * @updated 1.3.5 | |
| 538 | + * @updated 1.5 removed rendering the license to an earlier function | |
| 539 | + * @param array $attachments array of attachments. | |
| 540 | + * @return string | |
| 541 | + */ | |
| 542 | + public function render_attachments( $attachments ) { | |
| 543 | + ISC_Log::log( 'start to render attachments list' ); | |
| 571 | 544 | |
| 572 | - if (3 < $backward_distance) { | |
| 573 | - ?> | |
| 574 | - <a href="<?php echo $page_link . $query_string . $isc_query_tag; ?>1" class="page-numbers">1</a> | |
| 575 | - <span class="page-numbers dots">...</span> | |
| 576 | - <a href="<?php echo $page_link . $query_string . $isc_query_tag . ($page-2);?>" class="page-numbers"><?php echo $page-2; ?></a> | |
| 577 | - <a href="<?php echo $page_link . $query_string . $isc_query_tag . ($page-1);?>" class="page-numbers"><?php echo $page-1; ?></a> | |
| 578 | - <span class="page-numbers current"><?php echo $page; ?></span> | |
| 579 | - <?php | |
| 580 | - } else { | |
| 581 | - for ($i = 1; $i <= $page; $i++) { | |
| 582 | - if ($i == $page) { | |
| 583 | - ?> | |
| 584 | - <span class="page-numbers current"><?php echo $i; ?></span> | |
| 585 | - <?php | |
| 586 | - } else { | |
| 587 | - ?> | |
| 588 | - <a href="<?php echo $page_link . $query_string . $isc_query_tag . $i;?>" class="page-numbers"><?php echo $i; ?></a> | |
| 589 | - <?php | |
| 590 | - } | |
| 591 | - } | |
| 592 | - } | |
| 545 | + // don't display anything, if no image sources displayed | |
| 546 | + if ( $attachments === [] ) { | |
| 547 | + ISC_Log::log( 'exit due to missing attachments' ); | |
| 548 | + return ''; | |
| 549 | + } | |
| 593 | 550 | |
| 594 | - if (3 < $forward_distance) { | |
| 595 | - ?> | |
| 596 | - <a href="<?php echo $page_link . $query_string . $isc_query_tag . ($page+1);?>" class="page-numbers"><?php echo $page+1; ?></a> | |
| 597 | - <a href="<?php echo $page_link . $query_string . $isc_query_tag . ($page+2);?>" class="page-numbers"><?php echo $page+2; ?></a> | |
| 598 | - <span class="page-numbers dots">...</span> | |
| 599 | - <a href="<?php echo $page_link . $query_string . $isc_query_tag . $max_page;?>" class="page-numbers"><?php echo $max_page; ?></a> | |
| 600 | - <?php | |
| 601 | - } else { | |
| 602 | - for ($i = $page+1; $i <= $max_page; $i++) { | |
| 603 | - ?> | |
| 604 | - <a href="<?php echo $page_link . $query_string . $isc_query_tag . $i;?>" class="page-numbers"><?php echo $i; ?></a> | |
| 605 | - <?php | |
| 606 | - } | |
| 607 | - } | |
| 608 | - } else { | |
| 609 | - for ($i = 1; $i <= $max_page; $i++) { | |
| 610 | - if ($i == $page) { | |
| 611 | - ?> | |
| 612 | - <span class="page-numbers current"><?php echo $i; ?></span> | |
| 613 | - <?php | |
| 614 | - } else { | |
| 615 | - ?> | |
| 616 | - <a href="<?php echo $page_link . $query_string . $isc_query_tag . $i;?>" class="page-numbers"><?php echo $i; ?></a> | |
| 617 | - <?php | |
| 618 | - } | |
| 619 | - } | |
| 620 | - } | |
| 621 | - if ($page != $max_page) { | |
| 622 | - ?> | |
| 623 | - <a href="<?php echo $page_link . $query_string . $isc_query_tag . ($page+1);?>" class="next page-numbers"><?php echo $next_text; ?></a> | |
| 624 | - <?php | |
| 625 | - } | |
| 626 | - ?> | |
| 627 | - </div> | |
| 628 | - <?php | |
| 629 | - echo $after_links; | |
| 630 | - } | |
| 551 | + ob_start(); | |
| 631 | 552 | |
| 632 | - /** | |
| 633 | - * get source string of a feature image | |
| 634 | - * | |
| 635 | - * @since 1.8 | |
| 636 | - * @param $post_id | |
| 637 | - * @return source string | |
| 638 | - */ | |
| 639 | - function get_thumbnail_source_string($post_id = 0){ | |
| 553 | + ?> | |
| 554 | + <ul class="isc_image_list"> | |
| 555 | + <?php | |
| 640 | 556 | |
| 641 | - if(empty($post_id)) return ''; | |
| 557 | + ISC_Log::log( sprintf( 'start listing %d attachments', count( $attachments ) ) ); | |
| 642 | 558 | |
| 643 | - $options = $this->get_isc_options(); | |
| 559 | + foreach ( $attachments as $atts_id => $atts_array ) { | |
| 560 | + if ( empty( $atts_array['source'] ) ) { | |
| 561 | + ISC_Log::log( sprintf( 'skip image %d because of empty source', $atts_id ) ); | |
| 562 | + continue; | |
| 563 | + } | |
| 564 | + echo apply_filters( | |
| 565 | + 'isc_source_list_line', | |
| 566 | + sprintf( '<li>%1$s: %2$s</li>', $atts_array['title'], $atts_array['source'] ), | |
| 567 | + $atts_id, | |
| 568 | + $atts_array, | |
| 569 | + $attachments | |
| 570 | + ); | |
| 571 | + } | |
| 572 | + ?> | |
| 573 | + </ul> | |
| 574 | + <?php | |
| 575 | + $output = apply_filters( 'isc_source_list', ob_get_clean() ); | |
| 644 | 576 | |
| 645 | - if(has_post_thumbnail($post_id)){ | |
| 646 | - $id = get_post_thumbnail_id($post_id); | |
| 647 | - $thumb = get_post($post_id); | |
| 577 | + return $this->render_image_source_box( $output ); | |
| 578 | + } | |
| 648 | 579 | |
| 649 | - // don’t show caption for own image if admin choose not to do so | |
| 650 | - if($options['exclude_own_images']){ | |
| 651 | - if(get_post_meta($id, 'isc_image_source_own', true)) return ''; | |
| 652 | - } | |
| 653 | - // don’t display empty sources | |
| 654 | - $src = $thumb->guid; | |
| 655 | - if(!$source_string = $this->render_image_source_string($id)) return ''; | |
| 580 | + /** | |
| 581 | + * Shortcode function to list all image sources of a given page (per-page list) | |
| 582 | + * | |
| 583 | + * @param array $atts attributes. | |
| 584 | + * @return string | |
| 585 | + */ | |
| 586 | + public function list_post_attachments_with_sources_shortcode( $atts = [] ) { | |
| 587 | + global $post; | |
| 656 | 588 | |
| 657 | - return '<p class="isc-source-text">' . $options['source_pretext'] . ' ' . $source_string . '</p>'; | |
| 658 | - } | |
| 589 | + ISC_Log::log( 'enter for [isc_list] shortcode' ); | |
| 659 | 590 | |
| 660 | - return ''; | |
| 661 | - } | |
| 591 | + // hotfix for https://github.com/webgilde/image-source-control/issues/48 to prevent loops | |
| 592 | + if ( defined( 'REST_REQUEST' ) ) { | |
| 593 | + ISC_Log::log( 'exit due to calling through REST_REQUEST' ); | |
| 594 | + return ''; | |
| 595 | + } | |
| 662 | 596 | |
| 663 | - /** | |
| 664 | - * load an image source string by url | |
| 665 | - * | |
| 666 | - * @updated 1.5 | |
| 667 | - * @deprecated since 1.9 | |
| 668 | - * @param string $url url of the image | |
| 669 | - * @return type | |
| 670 | - */ | |
| 671 | - public function get_source_by_url($url) | |
| 672 | - { | |
| 673 | - // get the id by the image source | |
| 674 | - $id = $this->get_image_by_url($url); | |
| 597 | + $a = shortcode_atts( [ 'id' => 0 ], $atts ); | |
| 675 | 598 | |
| 676 | - return $this->render_image_source_string($id); | |
| 599 | + // if $id not set, use the current ID from the post | |
| 600 | + if ( ! $a['id'] && isset( $post->ID ) ) { | |
| 601 | + $a['id'] = $post->ID; | |
| 602 | + } | |
| 677 | 603 | |
| 678 | - } | |
| 604 | + return $this->list_post_attachments_with_sources( $a['id'] ); | |
| 605 | + } | |
| 679 | 606 | |
| 680 | - /** | |
| 681 | - * render source string of single image by its id | |
| 682 | - * this only returns the string with source and license (and urls), | |
| 683 | - * but no wrapping, because the string is used in a lot of functions | |
| 684 | - * (e.g. image source list where title is prepended) | |
| 685 | - * | |
| 686 | - * @updated 1.5 wrapped source into source url | |
| 687 | - * | |
| 688 | - * @param int $id id of the image | |
| 689 | - * @return bool|string false if no source was given, else string with source | |
| 690 | - */ | |
| 691 | - public function render_image_source_string($id){ | |
| 692 | - $id = absint($id); | |
| 607 | + /** | |
| 608 | + * Get source string of a feature image | |
| 609 | + * | |
| 610 | + * @param integer $post_id post object ID. | |
| 611 | + * | |
| 612 | + * @return string source | |
| 613 | + */ | |
| 614 | + public function get_thumbnail_source_string( int $post_id = 0 ): string { | |
| 615 | + if ( empty( $post_id ) || ! has_post_thumbnail( $post_id ) ) { | |
| 616 | + return ''; | |
| 617 | + } | |
| 693 | 618 | |
| 694 | - $options = $this->get_isc_options(); | |
| 619 | + $id = get_post_thumbnail_id( $post_id ); | |
| 620 | + $source_string = ISC\Image_Sources\Renderer\Caption::get( | |
| 621 | + $id, | |
| 622 | + [], | |
| 623 | + [ | |
| 624 | + 'prefix' => false, | |
| 625 | + 'styled' => false, | |
| 626 | + ] | |
| 627 | + ); | |
| 628 | + if ( ! $source_string ) { | |
| 629 | + return ''; | |
| 630 | + } | |
| 695 | 631 | |
| 696 | - $metadata['source'] = get_post_meta($id, 'isc_image_source', true); | |
| 697 | - $metadata['source_url'] = get_post_meta($id, 'isc_image_source_url', true); | |
| 698 | - $metadata['own'] = get_post_meta($id, 'isc_image_source_own', true); | |
| 699 | - $metadata['licence'] = get_post_meta($id, 'isc_image_licence', true); | |
| 632 | + return '<p class="isc-source-text">' . apply_filters( 'isc_featured_image_source_pre_text', _x( 'Featured image: ', 'label of the featured image source, when displayed below post excerpts', 'image-source-control-isc' ) ) . ' ' . $source_string . '</p>'; | |
| 633 | + } | |
| 700 | 634 | |
| 701 | - $source = ''; | |
| 635 | + /** | |
| 636 | + * Load an image source string by url | |
| 637 | + * | |
| 638 | + * @updated 1.5 | |
| 639 | + * @deprecated since 1.9 | |
| 640 | + * @param string $url url of the image. | |
| 641 | + * @return string | |
| 642 | + */ | |
| 643 | + public function get_source_by_url( $url ) { | |
| 644 | + // get the id by the image source | |
| 645 | + $id = ISC_Model::get_image_by_url( $url ); | |
| 702 | 646 | |
| 703 | - $att_post = get_post($id); | |
| 647 | + _deprecated_function( __METHOD__, '1.9.0' ); | |
| 704 | 648 | |
| 705 | - if ('' != $metadata['own']) { | |
| 706 | - if ($this->_options['use_authorname']) { | |
| 707 | - if (!empty($att_post)) { | |
| 708 | - $source = get_the_author_meta('display_name', $att_post->post_author); | |
| 709 | - } | |
| 710 | - } else { | |
| 711 | - $source = $this->_options['by_author_text']; | |
| 712 | - } | |
| 713 | - } else { | |
| 714 | - if ('' != $metadata['source']) { | |
| 715 | - $source = $metadata['source']; | |
| 716 | - } | |
| 717 | - } | |
| 649 | + return ISC\Image_Sources\Renderer\Image_Source_String::get( $id ); | |
| 650 | + } | |
| 718 | 651 | |
| 719 | - if($source == '') return false; | |
| 652 | + /** | |
| 653 | + * Render the image source box | |
| 654 | + * dedicated to displaying an empty box as well so don’t add more visible elements | |
| 655 | + * | |
| 656 | + * @param string $content content of the source box, i.e., list of sources. | |
| 657 | + * @param bool $create_placeholder if true, create a placeholder box without content. | |
| 658 | + */ | |
| 659 | + public function render_image_source_box( string $content = '', bool $create_placeholder = false ): string { | |
| 660 | + $options = $this->get_options(); | |
| 661 | + $headline = $options['image_list_headline']; | |
| 720 | 662 | |
| 721 | - // wrap link around source, if given | |
| 722 | - if('' != $metadata['source_url']){ | |
| 723 | - $source = sprintf('<a href="%2$s" target="_blank" rel="nofollow">%1$s</a>', $source, $metadata['source_url']); | |
| 724 | - } | |
| 663 | + ob_start(); | |
| 664 | + require ISCPATH . 'public/views/image-source-box.php'; | |
| 725 | 665 | |
| 726 | - // add license if enabled | |
| 727 | - if($options['enable_licences'] && isset($metadata['licence']) && $metadata['licence']) { | |
| 728 | - $licences = $this->licences_text_to_array($options['licences']); | |
| 729 | - if(isset($licences[$metadata['licence']]['url'])) $licence_url = $licences[$metadata['licence']]['url']; | |
| 666 | + ISC_Log::log( 'finished creating image source box' ); | |
| 730 | 667 | |
| 731 | - if(isset($licence_url) && $licence_url != '') { | |
| 732 | - $source = sprintf('%1$s | <a href="%3$s" target="_blank" rel="nofollow">%2$s</a>', $source, $metadata['licence'], $licence_url); | |
| 733 | - } else { | |
| 734 | - $source = sprintf('%1$s | %2$s', $source, $metadata['licence']); | |
| 735 | - } | |
| 736 | - } | |
| 668 | + /** | |
| 669 | + * Filter: isc_render_image_source_box | |
| 670 | + * allow to modify the output of the image source box | |
| 671 | + * | |
| 672 | + * @param string $content content of the source box. | |
| 673 | + * @param string $headline headline of the source box. | |
| 674 | + * @param bool $create_placeholder if true, create a placeholder box without content. | |
| 675 | + */ | |
| 676 | + return apply_filters( 'isc_render_image_source_box', ob_get_clean(), $content, $headline, $create_placeholder ); | |
| 677 | + } | |
| 737 | 678 | |
| 738 | - return $source; | |
| 739 | - } | |
| 679 | + /** | |
| 680 | + * Render source string of single image by its id | |
| 681 | + * this only returns the string with source and license (and urls), | |
| 682 | + * but no wrapping, because the string is used in a lot of functions | |
| 683 | + * (e.g. image source list where title is prepended) | |
| 684 | + * | |
| 685 | + * @updated 1.5 wrapped source into source url | |
| 686 | + * @updated 2.4 accept metadata as an argument | |
| 687 | + * @deprecated since 3.0 | |
| 688 | + * | |
| 689 | + * @param int|string $id id of the image. | |
| 690 | + * @param string[] $data metadata. | |
| 691 | + * @param array $args arguments | |
| 692 | + * use "disable-links" = (any value), to disable any working links. | |
| 693 | + * | |
| 694 | + * @return bool|string false if no source was given, else string with source | |
| 695 | + */ | |
| 696 | + public function render_image_source_string( $id, array $data = [], array $args = [] ) { | |
| 697 | + _deprecated_function( __METHOD__, '3.0.0', 'ISC\Image_Sources\Renderer\Image_Source_String::get' ); | |
| 740 | 698 | |
| 741 | - /** | |
| 742 | - * save image information for a post when it is viewed – only called when using isc_list function | |
| 743 | - * (to help indexing old posts) | |
| 744 | - * | |
| 745 | - * @since 1.1 | |
| 746 | - */ | |
| 747 | - public function save_image_information_on_load() | |
| 748 | - { | |
| 749 | - global $post; | |
| 750 | - if (empty($post->ID)) { | |
| 751 | - return; | |
| 752 | - } | |
| 699 | + return ISC\Image_Sources\Renderer\Image_Source_String::get( $id, $data, $args ); | |
| 700 | + } | |
| 753 | 701 | |
| 754 | - $post_id = $post->ID; | |
| 755 | - $_content = $post->post_content; | |
| 702 | + /** | |
| 703 | + * Check if the source list can be added to the content automatically | |
| 704 | + * | |
| 705 | + * @return bool true if the source list can be added to the content | |
| 706 | + */ | |
| 707 | + public function can_add_list_to_content() { | |
| 708 | + $options = $this->get_options(); | |
| 756 | 709 | |
| 757 | - $this->save_image_information($post_id, $_content); | |
| 758 | - } | |
| 710 | + /** | |
| 711 | + * Tests: | |
| 712 | + * - is list allowed on archive pages? | |
| 713 | + * - automatic injection of the list is enabled | |
| 714 | + */ | |
| 715 | + if ( | |
| 716 | + ( | |
| 717 | + ( is_archive() || is_home() ) | |
| 718 | + && isset( $options['list_on_archives'] ) && $options['list_on_archives'] ) | |
| 719 | + || ( is_singular() && isset( $options['display_type'] ) && is_array( $options['display_type'] ) && in_array( 'list', $options['display_type'], true ) ) ) { | |
| 720 | + return true; | |
| 721 | + } | |
| 759 | 722 | |
| 760 | - } | |
| 723 | + return false; | |
| 724 | + } | |
| 725 | + | |
| 726 | + /** | |
| 727 | + * Get image source string for public output | |
| 728 | + * | |
| 729 | + * @depreacted since 3.0 | |
| 730 | + * | |
| 731 | + * @param int $attachment_id attachment ID. | |
| 732 | + * @return string | |
| 733 | + */ | |
| 734 | + public static function get_image_source_text( $attachment_id ) { | |
| 735 | + _deprecated_function( __METHOD__, '3.0.0', 'ISC\Image_Sources\Image_Source::get_image_source_text' ); | |
| 736 | + | |
| 737 | + return ISC\Image_Sources\Image_Sources::get_image_source_text( $attachment_id ); | |
| 738 | + } | |
| 739 | + | |
| 740 | + /** | |
| 741 | + * Are we currently on an AMP URL? | |
| 742 | + * | |
| 743 | + * @return bool true if AMP url, false otherwise | |
| 744 | + */ | |
| 745 | + public static function is_amp() { | |
| 746 | + global $pagenow; | |
| 747 | + if ( is_admin() | |
| 748 | + || is_embed() | |
| 749 | + || is_feed() | |
| 750 | + || ( isset( $pagenow ) && in_array( $pagenow, [ 'wp-login.php', 'wp-signup.php', 'wp-activate.php' ], true ) ) | |
| 751 | + || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) | |
| 752 | + || ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) | |
| 753 | + ) { | |
| 754 | + return false; | |
| 755 | + } | |
| 756 | + | |
| 757 | + if ( ! did_action( 'wp' ) ) { | |
| 758 | + return false; | |
| 759 | + } | |
| 760 | + | |
| 761 | + return ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() ) | |
| 762 | + || ( function_exists( 'is_wp_amp' ) && is_wp_amp() ) | |
| 763 | + || ( function_exists( 'ampforwp_is_amp_endpoint' ) && ampforwp_is_amp_endpoint() ) | |
| 764 | + || ( function_exists( 'is_penci_amp' ) && is_penci_amp() ) | |
| 765 | + || isset( $_GET ['wpamp'] ); | |
| 766 | + } | |
| 767 | + | |
| 768 | + /** | |
| 769 | + * Are we currently on a URL using AMP reader mode? | |
| 770 | + * This mode is optional in the official AMP plugin and the only choice in the AMPforWP plugin | |
| 771 | + * in reader mode, the classic WordPress filters are not working | |
| 772 | + * | |
| 773 | + * @return bool true if AMP url, false otherwise | |
| 774 | + */ | |
| 775 | + public static function is_amp_reader_mode() { | |
| 776 | + // stop if we are not even on an AMP page | |
| 777 | + if ( ! self::is_amp() ) { | |
| 778 | + return false; | |
| 779 | + } | |
| 780 | + | |
| 781 | + // official AMP plugin in reader mode | |
| 782 | + if ( | |
| 783 | + class_exists( 'AMP_Options_Manager', false ) | |
| 784 | + && AMP_Options_Manager::get_option( 'theme_support' ) === 'reader' | |
| 785 | + ) { | |
| 786 | + return true; | |
| 787 | + } | |
| 788 | + | |
| 789 | + // the AMPforWP plugin only uses reader mode since they base on an old version of the AMP plugin where only that one was available | |
| 790 | + if ( function_exists( 'ampforwp_is_amp_endpoint' ) && ampforwp_is_amp_endpoint() ) { | |
| 791 | + return true; | |
| 792 | + } | |
| 793 | + | |
| 794 | + return false; | |
| 795 | + } | |
| 761 | 796 | } |