2

Is there any neat way of disabling the figures/images in a certain post category until a user logs in. I want the entire post content to be visible except for the images.

I'm not a programmer and I have not found any useful plugin.

2

You can use PHP strip_tags and hook to the_content filter by checking if user is logged in.

add this to your functions.php file

add_filter( 'the_content', 'wpse_remove_img' );
function wpse_remove_img( $content ){

  if( ! is_user_logged_in() ){

    // You need to specify which tag you want to keep here it's p, a, h2,span, div.
    // Adjust accordingly (you might want to add other or remove all)
    $content = strip_tags($content, '<p><a><h2><span><div>'); 


  }

  return $content;

}

EDIT

If you want to replace the image instead, I would use PHP DOMDocument to do the search and replace.

Note that some issues with text encoding can be experienced or warnings for some HTML5 tags. Hakre explains in depth why this can happen and Gordon explains it well, both over at StackExchange.

In my solution below, I took care of that by defining the encoding before the $content is loaded. And temporary disabling libxml errors so we don't get bugged with warnings by using libxml_use_internal_errors

again, this goes into your functions.php

add_filter( 'the_content', 'wpse_replace_img' );
function wpse_replace_img( $content ){

  if( ! is_user_logged_in() ){

    $dom = new DOMDocument();

    libxml_use_internal_errors(true); // deactivating errors
    // Load without added HTML and doctype, see https://stackoverflow.com/a/22490902/4643867
    $dom->loadHTML( '<?xml encoding="UTF-8">' . $content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD ); // adding encoding
    libxml_use_internal_errors(false); // reactivating errors

    $tags = $dom->getElementsByTagName( 'img' );

    foreach ($tags as $img) {
      $new_src_url = 'http://url/to/your/filler-image.jpg';
            $img->setAttribute( 'src', $new_src_url );
            $img->setAttribute( 'srcset', '' );

    }

    $content = $dom->saveHTML();

  }

  return $content;

}

EDIT

Ok, so here's an edit that takes into consideration wrapping <a> tags around images. This first solution will replace the link to the image with a #

add_filter( 'the_content', 'wpse_replace_img' );
function wpse_replace_img( $content ){

  if( ! is_user_logged_in() ){

    $dom = new DOMDocument();

    libxml_use_internal_errors(true);
    // Load without added HTML and doctype, see https://stackoverflow.com/a/22490902/4643867
    $dom->loadHTML( '<?xml encoding="UTF-8">' . $content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD ); // adding encoding
    libxml_use_internal_errors(false);

    $tags  = $dom->getElementsByTagName( 'img' );

    foreach ($tags as $img) {
      $new_src_url = 'http://url/to/your/filler-image.jpg';
      $img->setAttribute( 'src', $new_src_url );
      $img->setAttribute( 'srcset', '' );

      if( $img->parentNode->tagName == 'a' ){ // if the current image is wrapped by an <a> tag, replace href link with an #

        $img->parentNode->setAttribute( 'href', '#' );

      }

    }

    $content = $dom->saveHTML();

  }

  return $content;

}

Then you could also remove the <a> tag altogether. With some help from matb33 over at stackoverflow I managed to come up with this solution.

add_filter( 'the_content', 'wpse_replace_img' );
function wpse_replace_img( $content ){

  if( ! is_user_logged_in() ){

    $dom = new DOMDocument();

    libxml_use_internal_errors(true);
    // Load without added HTML and doctype, see https://stackoverflow.com/a/22490902/4643867
    $dom->loadHTML( '<?xml encoding="UTF-8">' . $content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD ); // adding encoding
    libxml_use_internal_errors(false);

    $tags  = $dom->getElementsByTagName( 'img' );
    $fragment = $dom->createDocumentFragment();  // Create our new document fragment to manipulate


    foreach ($tags as $img) {
      $new_src_url = 'http://url/to/your/filler-image.jpg';
      $img->setAttribute( 'src', $new_src_url );
      $img->setAttribute( 'srcset', '' );

      if( $img->parentNode->tagName == 'a' ){ // if the current image has an  <a> tag as a parent apply replace our <a> tag with our <img> tag

        $a = $img->parentNode;
        $fragment->appendChild( $img->parentNode->childNodes->item( 0 ) ) ; // store our image node into the fragment
        $a->parentNode->replaceChild( $fragment, $a ); // replace our <a> tag with our <img> tag

      }

    }

    $content = $dom->saveHTML();

  }

  return $content;

}

EDIT

And here's how to apply only to specific categories

add_filter( 'the_content', 'wpse_replace_img' );
function wpse_replace_img( $content ){

  if( ! is_user_logged_in() ){

    $apply_restriction = false;
    $categories = get_the_category();
    $restricted_cat = array(
        'restricted_cat_slug' // Our restricted category slug, since it's an array you could provide more than one
    );


    // Performing our check, switch the flag if we find a match
    foreach( $categories as $cat ){
        foreach( $restricted_cat as $r_cat ){
            if( $cat->slug == $r_cat ){
                $apply_restriction = true;
            }
        }
    }

    // Bail out without applying any restriction if no category match is found
    if( ! $apply_restriction ){
        return $content;
    }

    $dom = new DOMDocument();

    libxml_use_internal_errors(true);
    // Load without added HTML and doctype, see https://stackoverflow.com/a/22490902/4643867
    $dom->loadHTML( '<?xml encoding="UTF-8">' . $content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD ); // adding encoding
    libxml_use_internal_errors(false);

    $tags  = $dom->getElementsByTagName( 'img' );
    $fragment = $dom->createDocumentFragment();  // Create our new document fragment to manipulate


    foreach ($tags as $img) {
      $new_src_url = 'http://url/to/your/filler-image.jpg';
      $img->setAttribute( 'src', $new_src_url );
      $img->setAttribute( 'srcset', '' );

      if( $img->parentNode->tagName == 'a' ){ // if the current image has an  <a> tag as a parent apply replace our <a> tag with our <img> tag

        $a = $img->parentNode;
        $fragment->appendChild( $img->parentNode->childNodes->item( 0 ) ) ; // store our image node into the fragment
        $a->parentNode->replaceChild( $fragment, $a ); // replace our <a> tag with our <img> tag

      }

    }

    $content = $dom->saveHTML();

  }

  return $content;

}
12
  • Thats absolutely great! And thousand thanks for the prompt reply. Dare I ask what the corresponding tweak would be to replace all original images (in every post in a category) with one standard image (which will be a text saying: "log in to see this image") for all non logged in users? I would be immensely grateful. – Adam Robinsson Aug 28 '16 at 18:45
  • @AdamRobinsson Edited my answer – bynicolas Aug 28 '16 at 20:54
  • Worked like a charm until i pressed the stand-in image, then the correct image (which was supposed to be blocked) appeared :-) See for yourself: mitralis.se/uncategorized/test – Adam Robinsson Aug 28 '16 at 21:08
  • So the image with the bicycle is the stand-in image and the image with the chair is the blocked image? – bynicolas Aug 28 '16 at 21:13
  • 1
    @birgire you were right, I edited my answer to remove those extra tags from the source. Good call :) – bynicolas Aug 28 '16 at 23:08
2

Here's a suggestion how to remove images, with caption, for visitors (not logged in):

add_filter( 'img_caption_shortcode', function( $output, $attr, $content )
{
    $width = isset( $attr['width'] ) ? $attr['width'] : '300';
    $align = isset( $attr['align'] ) ? $attr['align'] : '';
    $class = isset( $attr['class'] ) ? $attr['class'] : 'no-image';

    $class = sprintf( 'wp-caption %s %s', $align, $class );

    return is_user_logged_in() 
        ? $output 
        : sprintf( 
            '<div class="%s" width="%d">%s</div>',
            esc_attr( $class ),
            (int) $width,
            esc_html__( 'Please log in to view the image', 'mydomain' )
        );
}, 10, 3 );

i.e. only images that are wrapped within the [caption] shortcode are replaced with a text.

The caption's text must be non-empty and the width non-zero.

Hope you can adjust it to your needs.

Here's an example:

no-image

6
  • Million thanks. Thats something I add to functions.php, right? – Adam Robinsson Aug 28 '16 at 19:39
  • yes, that should work in functions.php, though it might be better to create a plugin, if you later decide to get a new theme and you still want this feature. @AdamRobinsson – birgire Aug 28 '16 at 19:43
  • this code turned out to have the same issue as above. It correctly displays the "stand-in" image (a bicycle) until you press the image, then the correct image, which should be blocked, turns out in a new window. mitralis.se/uncategorized/test – Adam Robinsson Aug 28 '16 at 21:16
  • I guess it's how you add the stand-in image - What's your current code snippet ? Did the stand-in text version work for you? I tested in on the default Twentysixteen theme. @AdamRobinsson – birgire Aug 28 '16 at 21:22
  • This also works beautifully. It did however, not replace the original image with a stand-in image. nevertheless it did the job. i'm grateful for your time. – Adam Robinsson Aug 28 '16 at 21:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.