Enable multisite admins to add custom CSS

This blog runs on a multisite, so I can easily translate all my blost posts using MultilingualPress. Another website on this multisite installation is the website for WP Meetup Berlin. On this site, another user has the admin role. With this role you can usually do most of the things an admin on a single site can do. With some exceptions:

  • Updating core, plugins, themes, etc.
  • Adding sites
  • Adding new users
  • Installing plugins
  • Installing themes
  • Using the plugin/theme editor (which you should never do anyways)
  • Use unfiltered HTML
  • Use the custom CSS option in the customizer

This last exceptions really surprised me. I always thought that this would be the only possible option for site admins to change the design of the site (as they can’t install themes or child themes or change their files). But only superadmins have this option.

Enabling the option for site admin

Fortunately, there is a capabilty called edit_css you can simply assign to the admins. You can do this using a role management plugin such as Members or you simply install the plugin Multisite Custom CSS which is doing exactly this one thing.

Conclusion

For security reasons, it makes sense to not allow the admins of a multisite website to change the theme files. But not allowing them to edit the Custom CSS in the Customizer doesn’t make sense in my opinion. But using one of the plugins, it’s pretty easy to enable this option.

Remove the WordPress.org link in the meta widget

WordPress comes with a lot of widgets bundled in core. One of this widgets is the meta widget. When you install a fresh copy of WordPress, this widget is usually put into the main widget area. Most users remove this widget, as they don’t see any need of it. For specific websites, this widget can come in handy. It has links to register (if allowed), the login (if you are not currently logged in), a link to the RSS feed of the website as well as a link to the RSS feed of the comments of the current page or blog post:

In a Facebook Group, someone asked if it’s possible to remove the link to WordPress.org in the meta widget. Fortunately, there is a filter around this specific link. You can easily change the content of the link or you can simple remove it completely by removing an empty string. With the one of the “magic callback functions” in WordPress, this task can be done with a single line of code:

add_filter( 'widget_meta_poweredby', '__return_empty_string' );

That’s it. Now the meta widget will not show the link to WordPress.org anymore. I personally would never remove the link, but if you want to, just add this single line of code or use the small plugin in this Gist.

Remove the “Private” and “Protected” prefix on blog posts

Some weeks ago on a client website, we were asked to remove the “Private: ” and “Protected: ” prefixes on a custom post type. If you don’t know what I am talking about, let me quickly explain the feature.

The visibility of a blog post

When you write a blog post, you’ll find the option “Visibility” in the “Publish” meta box. This is usually set to “Public”. The other two options are “Passwort proteced” and “Private”. When you select the first one, a blog post is only visible for logged in users with any role. The second option lets you set a single password for this blog post (this password is store plain text in the post edit screen).

Changes on the blog post titles

When you pick one of the other visibility setting, WordPress will automatically prefix the blog post title with “Proteced: ” or “Private: “, when you use the default functions such as the_title(). So when you don’t want to have the prefix, how could we remove it?

Removing the prefixes with filters

Remove the prefixes is rather easy. There the two filters private_title_format and protected_title_format to change the title of such blog posts. We use it as the following:

function remove_pp_prefix_title_format( $content ) {
    return '%s';
}
add_filter( 'private_title_format', 'remove_pp_prefix_title_format' );
add_filter( 'protected_title_format', 'remove_pp_prefix_title_format' );

Conclusion

So with these few lines, we can easily change the title for proteced and private posts. When I was asked the same questions in a Facebook group, I put this into a small plugin an published it as a Gist just to find out, that someone even had published a plugin on the official plugin directory. I hope you’ll find this useful for your own blog.

WordCamp Cologne 2017 – A BarCamp style WordCamp

 

 

Last weekend, I’ve visited WordCamp Cologne. It was my fourth WordCamp in Cologne and the second year in a row, they organized it as a BarCamp. Last year they had only one day with talks, this year it was two days.

Thursday: Pre Warm-Up Party

I took the train from Berlin to Cologne on Thursday as the Contributor Day was early on Friday. The organizer team organized a small party in a typical “Brauhaus”. Around 25 attendees showed up:

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Friday: Contributor Day

The Contributor Day of WordCamp Cologne was taking place at the Microsoft Cologne offices. Microsoft not only donated the space, but they also took care of the drinks. There were around 80 signups and a good number of them showed up:

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

I’ve helped out in the Design and Accessibility team to work on some tickets and helping people setting up their environments and getting started.

Warm-Up Party

As many of the WordCamp attendees arrived on Friday, we had an official party in yet another Brauhaus. I would guess that around 80 people showed up:

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Saturday: The first conference day

As the WordCamp Cologne was a BarCamp, it also followed the rules of a BarCamp. One of this rule is, that every attendee introduces herself/himself with name, profession and three hashtags. With more than 200 attendees present at the start, this took a while but was very interesting, as you got a good feeling on who is attending.

https://twitter.com/LucasPrigge/status/931816384908136448

After the indroduction round, every attendee had the chance to “pitch” a talk. If enough hands were raised the talk was put on the schedule, which had four tracks in parallel:

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

A new community Wapuu

The pitch was done aroud 11am, so we had an hour to prepare for the talks. But before that, the organizing team had one more thing to announce. In May, one of our valuable community members got his very own Wapuu. At WordCamp Cologne, the second Wapuu was presented. And it was the Wapuu for the new community member of the year, Carole:

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Start of the sessions

I will not go into details on each of the many very good sessions. But as you might have guessed, I couldn’t resist to pitch some sessions as well. On the first day in the first slot, I gave a talk on how to set up a local dev environment using Docker:

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Awesome swag

Any WordCamp attendee knows, that you usually get a T-Shirt. But this year, they had something extra for each attendee: fan scarfs!

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Community party

After day one, there was yet another party, this time with all attendees. The organizers found a nice restaurant/bar with a small dance floor in the basement. And there we found some more swag 🙂

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Sunday: The second conference day

Just as on the first say, we had another pitch for the sessoins. But we skipped the introduction round 🙂

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Two more sessions for me

I pitched with my second talk in which I gave a short introduction into plugin development. In the afternoon I joined some other members of the Pluginkollektiv to present our work. We also found some new contributors for the project, which was amazing!

The  biggest WordPress BarCamp

WordCamp Cologne was more than twice the size of last year, not only in the number of attendees but also in days with talks. I think that most attendees really liked the concept. I had a very good time myself.

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Upcoming German WordCamps

At the end of the day, two upcoming WordCamps where announced. In May, we will meet for WordCamp Retreat Soltau. The ticket sale has just started. While Soltau was already presented at WordCamp Berlin earlier this year, another WordCamp was announced. In March, there will be a WordCamp in Würzburg and it will most likely also be a WordCamp in a BarCamp style. There is not fixed date yet, but if you are interrested, make sure to subscribe to the newsletter on the website.

Conclusion

I really enjoyed my fourth WordCamp in Cologne and I like the BarCamp style very much. It would be nice to see WordCamps in other countries with a similar concept. The other BarCamp style WordCamp I have visited so far was WordCamp Europe last year, which had a BarCamp slot (out of three slots) on the second day only.

Why I volunteer my time and contribute to WordPress

This week Birgit Olzem asked some members of the German community about their motivation volunteering to WordPress. So this is going to be my response.

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Giving something back

That’s it! It’s as simple as that. OK, let’s make this blog post a bit longer 🙂 My first encounter with WordPress was more than 8 years ago. And the first thing I did was acutally contributing to WordPress by writing my very first plugin.

WordPress is free. In the Open Source world, the term free is often seen as “free as in free speech” vs. “free as in free beer”. WordPress is free in both senses. You have the freedom to do use, modify and redistriubute the code, which is quaranteed by the GPL license WordPress is using. But it’s also “free as in free beer”, as you get the core software with no cost. Sure there are premium plugins, themes, services and hostings. But the WordPress core is free.

I benefit a lot of those two freedoms. And as all plugins on the official directory are also free, I also benefit from the volunteered contribitions of many other members of the WordPress community. So whenever I write a plugin, that might be useful for others, I publish it for free, usually here on my blog.

Improving my skills

Once you publish a WordPress plugin, you usually get instant feedback from your users. And you get that feedback for free. They will test your plugin, use it in ways you haven’t thought of and they will make suggestions on how you could improve it even further. Sometimes you get feedback from a more experienced developer on how you might to things not in the best way. So even though it might be frightening to publish your code and have it critizised others, I can highly encourage everyone to try it out.

Meeting the community

Beside of developing plugins, I contribute in many other ways. I help in Facebook groups answering questions, I am writing blog posts and I also organize meetups and WordCamps. But why am I spending hundreds of unpaid hours organizing an event for other people? Because I also attend many of this events. Next weekend I am going to attend WordCamp Cologne and two weeks later WordCamp US. This will be a total of 9 WordCamps I have attended this year, including WordCamp Berlin I organized with other members of the Berlin community. So because other members of the WordPress community are organizing WordCamps, I had a great time this year travelling around Europe and meeting old an new friends. Without their dedication, those WordCamps would not have been possible. So in organizing a WordCamp in my city, I am also giving something back, because I want to enable as many people as possible to experience the WordPress community.

Making the web a better place

My first contribution to core was a small patch making the forms more user friendly and accessible. I was annoyed that the ticket order form for WordCamps was not easy to use mobile devices. So I fixed it. And then I fixed a similar ticket in core, which was about 4 years old. This patch improved millions of website. WordPress is used on 29% of all websites, so any contribution made to it’s code will have a big impact.

Conclusion

WordPress and it’s community has becoming an important parts of my life. I’ve learned a lot along the way an found some very good friends. This is why I happily volunteer hours of work per week on many different teams. It always makes me happy, when I get positive feedback on my plugins, my help in Facebook groups, in comments or as an organizer. Many people volunteer their time in many ways in our society and I decided to contribute to an Open Source community. And there are probably a hundred other things I could write down on this topic.

If you get a little bit inspired but not sure how you could contribute, just leave a comment and we can find something you can give back. And if you don’t want to, that’s just fine. No one is obligated to give something back, you will still have the two freedoms.

MailChimp changes all signup forms to single opt-in

A friend of mine who I set up a WordPress website for and who is using MailChimp for her newsletter just received an email some hours ago she forwarded to me:

In this email, MailChimp was informing her, that starting October 31, all signup forms will be switched to a single opt-in process, rather than the current default of a double opt-in.

A big problem for websites in Europe

She didn’t really knew, what that meant, but when I saw the email, I couldn’t believe what I’ve just read. The double opt-in ensures that anyone who ends up on your email subscribers list confirmed this by clicking a link in an email sent to them after filling out the signup form.

Read more →

Translate Customizer options with Polylang

When it comes to translations, my favorite plugin is MultilingualPress. For a new client project with a small budget and only a couple of static pages, we decided to choose Polylang, which doesn’t require a Multisite.

Issues translating content

One of the main reasons I prefere MultilingualPress over other solutions is the fact, that is uses the Multisite functionality of WordPress. In this case, it’s usually pretty easy to translate any aspect of the website. It’s even possible to use different theme, plugins, widgets, menu item, etc. With a single site installation, some texts are only available ones, like plugin properties and also Customizer options.

In the mentioned project, the theme provided a Customizer option for contact information used on the front page next to the header logo, as well as a textarea for a custom footer. As on a German website you usually link to the “Impressum” and the privacy policy, that was one of the options we needed to be able to translate. But also the contact information at the top had some texts that should be translated, such as dates.

Using a child theme for the translations

Unfortunately, I couldn’t find a way to solve the translation issues with the theme’s codebase. So I decided to implement a child theme, in which I’ve overwritten the header and footer templates. The header for example looked like this:

<div class="intro-wrap">
  <?php echo wp_kses_post( wpautop( get_theme_mod( 'header_intro' ) ) ); ?>
</div><!-- end .intro-wrap -->

The header_intro is a Customizer option that is taken from the option and just printed out. The can not change if with a filter. So how can we still translate it into the different languages? We simple have to wrap it with the pll__ function, the Polylang API is providing:

<div class="intro-wrap">
  <?php echo wp_kses_post( wpautop( pll__( get_theme_mod( 'header_intro' ) ) ) ); ?>
</div><!-- end .intro-wrap -->

Now the text would be translated, if such a translation exists for the current language. But how do you actually translate the option?

Register text for translations

To be able to translate any string, you have to tell Polylang about it. We use the function pll_register_string form the Polylang API to do that. In our functions.php file, we add these lines:

if ( function_exists( 'pll_register_string' ) ) :
	/**
	 * Register some string from the customizer to be translated with Polylang
	 */
	function child_theme_name_pll_register_string() {
		pll_register_string( 'header_intro', get_theme_mod( 'header_intro' ), 'child-theme-name', true );
	}

	add_action( 'after_setup_theme', 'child_theme_name_pll_register_string' );
endif;

First we check, if the functions exsists, as otherwise we might cause a fatal error, if we deactivate Polylang. Then we register a callback function and in this function we use the Polylang function to register the string. The first parameter is just a name to order the translatable string. The second parameter is the actual text we want to translate. In this case, we use the dynamic Customizer option value. The third parameter is to group strings and the last parameter tells the function, that it’s a multiline strings, so in the backend we see a textarea rather than a single line text input.

Doing the translation

All registered string can be translated using the settings page “Polylang -> Strings translations” in the backend. Here we should find the current value of the string an a textarea per language we have configured in Polylang. It’s important to keep in mind, that every time the Customizer option is changed, we have to update the translation as well, as the original string has changed.

Conclusion

As you can see, translating Customizer options per language is also possible when using Polylang. But we need to create a child theme in most cases. Translating options of plugins can be a lot trickier, as we can not simple create a child plugin. We usually have to use the filters, a plugin might provide to change it’s texts and option. This is also the reason why I still prefer a Multisite approach when I need a multilingual website.

Bonus tip

As Polylang is a rather new plugin, much newer then WPML (which I usually don’t use), it support the WPML language configuration file wpml-config.xml, so if your theme says, that it’s WPML ready and has this file, you can probably skip some of the steps I’ve shown you in this blog post, or even all of them.

Global WordPress Translation Day 3

On Saturday, the third Global WordPress Translation Day took place all around the world. Starting at midnight UTC, the day was filled with a 24h schedule with 22 presentations.

Local events

Just as on the first two WP Translation Days, there were many local events. On the second WP Translations Day, we had 38 events. This time, we almost doubled the number and had 71 events (maybe even more, which were not announced), six of them in Germany:

The Berlin meetup group, which I am a co-organizer of, participated on all events. This year we were a group of 8 translators and really got some work done. A big thank to the Designerei.berlin who donated us a room for free and some coffee, to get the day started:

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

Learning and translating

After setting up, I gave a quick introduction on how to get started translating WordPress. I first explained, how you get a WordPress.org account and how you connect to the international and German slack teams, to be able to ask questions in the polyplot channels. Then I introduced the glossary, the style guide and the quick start guide. We then worked together on the formal German translation of Contact Form 7. Then everyone worked on another plugin or theme and everyone was helping out each other on questions around translations. One of our attendees got CF7 translated to 100% and I could validate all strings the same day.

We also watched the live streamed session of Brigit Olzem from the official schedule. To round things up, I also gave a small presentation on internationalization, so our attendees also learned how to make you own theme or plugin translatable.

Summary

Unfortunately, I haven’t seen any numbers on how many people attended local events, watched the stream or how many strings, plugins and themes were translated. But our Berlin meetup group alone finished a couple of plugins and themes. On the website of the WP Translation Day, you can find some numbers of what has been achieved since the beginning of WPTD3. You can also watch all sessions from the schedule:

Click here to display content from Twitter.
Learn more in Twitter’s privacy policy.

This was probably not the last Global WordPress Transation Day and we will also most probably organize a local events on the next iteration. If you haven’t been able to participate (or didn’t even know about it), but you want to learn translating WordPress, find a WordCamp near you and join the polyglot team on it’s Contributor Day. Translating WordPress is really an easy start into contrubitung something back and it can have a big impact on users speaking your language.

Show tags of private posts in the tag cloud

No WordCamp last weekend, so today, you get a regular post 😉 This week, I had an interesting request in a client project. The WordPress website had a FAQ section and some of the questions were set as private posts. The website was using a tag cloud, to make it easier for visitors, to find questions. But even if the user was signed in, he couldn’t see the tags used only on private posts.

How are tags in the tag cloud generated?

When WordPress generates the HTML for the tag cloud, it queries a limited number of tags, ordered by count. But how is this count calculated. You would think, that a database query groups the tags on the posts, then groups by the tag ID and calculates the count. But this is not how it works in WordPress. For performance reasons, the count is stored statically in the database table. But why are private posts not showing up in the tag cloud? Once you change the visibility of a post to private, the posts is not added to the count of the tag. Only public posts are used to calculate the count. So there is not easy way using a filter/action to include the private posts. So how could it be solved?

Altering the query

Almost every query in WordPress, that resolves in the output of “a list”, is using a WP_Query. For the tag cloud, the WP_Term_Query is used. So we simple have to alter this query. But how do we know, that we are in the WP_Term_Query used in the tag cloud? Ther is no is_tag_cloud() or similar conditional tag.

The tag cloud is using two very specific arguments, that are only present in the tag cloud: largest and smallest. These two arguments are used, to set the minimum and maximum font size used in the tag cloud. So we can easily use one of this arguments, to catch the tag cloud’s WP_Term_Query. The rest is rather easy. We use the term_clauses filter to change the SQL query:

function private_posts_in_tc_terms_clauses( $pieces, $taxonomies, $args ) {
	if ( isset( $args['largest'] ) && is_user_logged_in() ) {
		// Count by the grouped term_id.
		$pieces['fields'] .= ', COUNT(t.term_id) AS grouped_count';
		// Join the relationship to the posts.
		$pieces['join'] .= ' INNER JOIN wp_term_relationships AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id';
		// Remove the "redundant" count (only for public posts).
		$pieces['where'] = str_replace( 'AND tt.count > 0', '', $pieces['where'] );
		// Group by the term_id to remove duplicates and calculate the count.
		$pieces['where'] .= ' GROUP BY tt.term_id HAVING grouped_count > 0';
	}

	return $pieces;
}
add_filter( 'terms_clauses', 'private_posts_in_tc_terms_clauses', 10, 3 );

We also make sure, that we only show the private tags to logged in users. In the altered SQL query, we join the term_relationship table, group by the term ID and count on the terms. This way, we get the correct count including the private posts. The query itself is a bit slower than the original one, as it is not using the “cached” count from the table. But every query to the WP_Term_Query is cached in a transient anyways, so the performance impact should not be too high.

Conclusion

It’s not always as easy as using a simple hook, to change a query in WordPress. But as usually, there is always a way to get the desired result. If you want to use this function yourself, you can find the code as a plugin in a GIST. So simply download it as a ZIP file and install it as a plugin on your site.