• Resolved kostek00

    (@kostek00)


    For some time I’m trying to write a plugin for my site that hides certain tags from normal users but staying visible to administrators. I also searched for answers and while finding some they don’t work already because answers are few years old.

    Any ideas how to achieve this or is it even possible?

Viewing 11 replies - 1 through 11 (of 11 total)
  • Moderator bcworkz

    (@bcworkz)

    It would depend on where you want to hide the tags and how they are being displayed in the first place. For example, to hide tags on post listings where your theme uses the_tags() to output the tags, you can manage what is output with ‘the_tags’ filter. If your theme uses some other way to list tags, what you do depends on what that other way is.

    If you need to hide tags on the backend, how it’s done depends on what capabilities users have that you want to hide tags from. Some users cannot see tags anyway. For those that can, hiding certain backend tags from such users may prove to be difficult. You’re probably best off using jQuery to hide the tags after the page loads, though clever users will still be able see the tags by viewing their page source.

    Thread Starter kostek00

    (@kostek00)

    I wanted to hide tags on pages as I don’t tag posts and tag cloud I modified to show only selected tags. By default my theme don’t show tags on pages, luckly I found in posts line that does that and copied it to pages. Theme uses the_tags() method to display tags on pages.

    I also want to mention that I only know HTML and CSS. All my tries were dependant on online guides and possibility that it maybe somehow will work.

    Moderator bcworkz

    (@bcworkz)

    Well, ‘the_tags’ filter passes the HTML that is just about to be output to any added callback function. Such a function can manipulate the HTML before returning it. One could add or remove any valid HTML to this using various string functions. The basic technique to hook a filter is well documented. The specifics of altering ‘the_tags’ not so much. The str_replace() function might be good to do this.

    If you want to try to cobble something together, I’m willing to help you fix any bugs, but I’m not going to be able to write up a fully functioning code for you. Have you any interest in learning a little PHP? šŸ™‚

    Thread Starter kostek00

    (@kostek00)

    So I was poking here and there and I found something helpful. Here I found another method to show tags on page with class creation of tag name which leads me to 2 things.

    1. If it’s possible to load css file in plugin based on user capabilities then I can create that file which will hide those tags. I don’t prefer this method because I will need to somehow hide separators from between tags.

    2. I also found this function that actually works with this method of showing tags:

    function exclude_tags($tags) {
    		foreach ($tags as $tag)
    		switch ($tag->name) {
    		case 'tag1':
    		case 'tag2':
    		break;
    		default:
    		$newtags[] = $tag;
    	}
    	return $newtags;
    }
    add_filter( 'get_the_tags', 'exclude_tags');

    The thing is if I add capabilities check to this then it hides those 2 tags to admins and hides all tags to all other users. While it should only hide those 2 tags to other user and nothing else. Admin should see all tags all the time.

    function exclude_tags($tags) {
    	if ( !is_super_admin() )
    		return;
    
    		foreach ($tags as $tag)
    		switch ($tag->name) {
    		case 'tag1':
    		case 'tag2':
    		break;
    		default:
    		$newtags[] = $tag;
    	}
    	return $newtags;
    }
    add_filter( 'get_the_tags', 'exclude_tags');

    There is one more thing. If I would use method above to show tags on pages then it requires some sort of formating to make tags showed like this code:
    <?php the_tags('<p class="taxonomy">'.__('<span class="tag-title">Tags: </span>', 'warp'), ' | ', '</p>'); ?>
    It’s look:
    Tags: Tag1 | Tag2 | Tag3 | Tag4
    (Word “Tags: ” is translatable)

    Moderator bcworkz

    (@bcworkz)

    You’re pretty close, a logic adjustment is all that’s needed I think. A few things to make note of first though. While it’s possible to selectively load CSS files based on user role, please do remember that when you hide content using CSS, that content is still accessible to restricted users by them simply looking at the page’s source HTML. I imagine this is not an issue with tags and you are more concerned with appearance than securing information.

    CSS stylesheets should be enqueued using wp_enqueue_style(). Thus you can do variations of something like this:
    if ( ! current_user_can('publish_posts')) wp_enqueue_style('hide_tags', plugins_url( 'hide-tags.css', __FILE__ ));

    It’s a little tricky, but it’s even possible to have a PHP based stylesheet that dynamically outputs certain rules based on some arbitrary criteria such as user capability.

    If you’re managing the output, you could set things up so the CSS properly handles separators as well as certain tags. But if you’re managing output, you may as well manage the tags too and not fuss with elaborate CSS schemes. Then you also avoid any possible security issues from hiding certain content. Much better to not output it instead of hiding it!

    When coding something that involves user permissions of some sort, avoid thinking in terms of roles. You really should focus on specific capabilities. Roles are really a variable container of capabilities. It’s possible a particular admin role might not be able to do something because another plugin has removed that capability, or a subscriber can actually do something that is not normally allowed. Of course on your own site you know if these variations exist or not. Still, it’s a good idea to think in terms of capabilities instead of roles. In some cases doing so might simplify the code’s logic because one capability could span multiple roles.

    In your ‘get_the_tags’ callback you are simply using is_super_admin(), which isn’t addressing what happens with regular admins. I suggest you find a related capability that users you want to see extra tags have and users who shouldn’t see them don’t have. ‘publish_posts’ perhaps? You can actually create custom capabilities and assign them to whatever role or even to individual users, giving you very fine grained control on who sees what.

    When dealing with capabilities we use current_user_can() to check if the user has a particular capability or not. So if ‘publish_posts’ is the right capability for your situation, you should replace this:

    if ( !is_super_admin() )
       return;

    with this:

    if ( current user_can('publish_posts'))
       return $tags; //unchanged

    Now for the output. Oddly, the_tags() function has nothing to do with the ‘get_the_tags’ filter. Each function has it’s own filter and there is no cross talk between them. Since you have an effective filter with ‘get_the_tags’ and filtering ‘the_tags’ requires a different, more fragile approach, we should work with get_the_tags(). This involves different logic than when the_tags() is used.

    After we use get_the_tags(), we need to do a foreach loop to construct a simple array of just tag names because what’s returned by get_the_tags() is way more data than we need. Your filter callback is similar but different. It’s rebuilding the entire array of tag objects except for those two tags. You now want to build an array of just $tag->name values. You’ll see why in a bit.

    First we need to output the translatable label. When we use various translation functions, we should try to avoid HTML and just translate the actual text. Your label code should be more like this:
    echo '<p class="taxonomy"><span class="tag-title">'. __('Tags', 'warp') . ':</span> ';

    Next we output the tags separated by pipes. We have the array of tag names. One’s inclination is to loop through the array and output each name, but there’s a better way — use implode() to glue the array elements together with ' | ', then echo out the returned string. Finally, output the closing </p>.

    Given all those tidbits and hints I think you’ll be able to piece something together.

    Thread Starter kostek00

    (@kostek00)

    I thikn you misunderstand second part (or did I misunderstand your response?).

    <?php the_tags('<p class="taxonomy">'.__('<span class="tag-title">Tags: </span>', 'warp'), ' | ', '</p>'); ?>
    Code above is my theme default code and with him function to exclude tags don’t work.

    I need to somehow format code below (from link in my previous post):

    <?php $tags = get_the_tags();
    if( $tags ) : ?>
      <p class="tags">
      <?php foreach( $tags as $tag ) { ?>
       <span class="<?php echo $tag->slug; ?>"><a href="<?php echo get_tag_link($tag->term_id); ?>"><?php echo $tag->name; ?></a></span>
      <?php } ?>
    </p>
    <?php endif; ?>

    To make tags looks like:
    Tags: Tag1 | Tag2 | Tag3 | Tag4
    As currently this code shows tags without any seprator and I can’t seem to make “Tags: ” word translatable.

    Thread Starter kostek00

    (@kostek00)

    I used capability instead of role in excluding function as you suggested and now it works exacly like I wanted it. Big thanks to you. I always thought that roles and capabilities are readed the same, I guess I was wrong.

    I also made “Tags: ” translatable with code below. The best thing is that I don’t need to change .po and .mo files as it will be in the same line as original code.

    <?php $tags = get_the_tags();
    if( $tags ) : ?>
    <p class="taxonomy">
    <?php echo '<span class="tag-title">'.__('Tags: ', 'warp').'</span>' ?>
    <?php foreach( $tags as $tag ) { ?>
    <span class="<?php echo $tag->slug; ?>"><a href="<?php echo get_tag_link($tag->term_id); ?>"><?php echo $tag->name; ?></a></span>
    <?php } ?>
    </p>
    <?php endif; ?>

    Now my problem is that I can’t make last pipe to be hidden. I tried to use implode() as you suggested but my best result looked like that:
    Tags: tag | | tag | | tag | | and so on

    Moderator bcworkz

    (@bcworkz)

    That’s some good progress, well done! I think the double pipes on the tags output probably has something to do with how you built the source array before imploding. Something like this is what I was thinking:

    echo '<p class="taxonomy"><span class="tag-title">'. __('Tags', 'warp') . ':</span> ';
    $tags = get_the_tags();
    $names = array();
    if( $tags ) {
       foreach( $tags as $tag ) {
          $names[] = $tag->name;
       }
       echo implode(' | ', $names ) . '</p>';
    } else echo '-none-';
    Thread Starter kostek00

    (@kostek00)

    I still can’t make implode() work. When I try to implement it it does show tags without separator at end but they are not as links. Whats more it shows first set of tags without separator but as links and then those not linked. Like so: tag tag tagtag | tag | tag

    For now I changed code to only one bigger block of PHP instead of many small.

    <?php $tags = get_the_tags();
    if( $tags ) :
    echo '<p class="taxonomy"><span class="tag-title">'.__('Tags: ', 'warp').'</span>';
    foreach( $tags as $tag ) {
    echo '<span class="'. $tag->slug .'"><a href="'. get_tag_link($tag->term_id) .'">'. $tag->name .'</a></span>';
    }
    echo '</p>';
    endif; ?>

    If i won’t be able to make it work I will look into CSS :last-child with adding pipe after </span>.

    • This reply was modified 7 years, 7 months ago by kostek00.
    Moderator bcworkz

    (@bcworkz)

    Sorry for the slow response, the forums did a migration and I missed your last post since it no longer pops up to the top.

    Yeah, I didn’t address links for the tags, I was just showing how implode works. If you modify the $names[] = $tag->name; line in my example so that $names[] is set to the entire HTML link instead of just $tag->name, it’ll work. Something like this I think:
    $names[] = '<a href="'. site_url("/tag/{$tag->name}/") .'" >'. $tag->name . '</a>';

    Untested but should be close.

    Thread Starter kostek00

    (@kostek00)

    Oh, I didn’t even considered that way. Thanks a lot.

    • Tags without last pipe – done.
    • Hiding tags for users – done.

    If anyone else would like to use it here it is.

    Code for hiding tags:

    function exclude_tags($tags) {
    		if ( current_user_can('activate_plugins'))
    		return $tags;
    		
    		foreach ($tags as $tag)
    		switch ($tag->name) {
    		case 'tag1':
    		case 'tag2':
    		case 'tag3':
    		break;
    		default:
    		$newtags[] = $tag;
    	}
    	return $newtags;
    }
    add_filter( 'get_the_tags', 'exclude_tags');

    Code for showing tags on pages that works with function above:

    <?php $tags = get_the_tags();
    if( $tags ) :
    	echo '<p class="taxonomy"><span class="tag-title">'.__('Tags: ', 'warp').'</span>';
    	foreach( $tags as $tag ) {
    		$names[] = '<span class="'. $tag->slug .'"><a href="'. get_tag_link($tag->term_id) .'">'. $tag->name .'</a></span>';
    	}
    echo implode(' | ', $names ) . '</p>';
    endif; ?>

    Also this code creates for each tag classes with name of tag slug.

    • This reply was modified 7 years, 7 months ago by kostek00.
Viewing 11 replies - 1 through 11 (of 11 total)
  • The topic ‘Hide certain tags from users’ is closed to new replies.