As of WordPress 4.6 it is possible to hook into the wp_send_new_user_notifications action to disable the new user notifications send to site admins or the new user.

// The Parameter "$behaviour" can be set to:
// "none" (no notifications are send); 
// "default" (no changes); 
// "admin" (notifications send to admin only); 
// "user" (notification send to user only); 
// "both" (notifications send to admin + user)
public static function limitNewRegistrationNotifications($behaviour){
    // do nothing
    if ($behaviour == 'default'){
        return;
    }

    // handle user registrations (self registered users)
    remove_action('register_new_user', 'wp_send_new_user_notifications');

    // new users added via wp-admin are created using add_user() -> edit_user() chain, NOT register_new_user()
    // @see https://developer.wordpress.org/reference/functions/add_user/
    remove_action('edit_user_created_user', 'wp_send_new_user_notifications', 10, 2);

    // notifications disabled ?
    if ($behaviour == 'none'){
        return;
    }

    // add custom callback and override the $notify setting with custom behaviour
    add_action('register_new_user', function($user_id) use ($behaviour){
        // trigger notification
        wp_new_user_notification($user_id, null, $behaviour);
    });
    add_action('edit_user_created_user', function($user_id) use ($behaviour){
        // trigger notification
        wp_new_user_notification($user_id, null, $behaviour);
    });
}

 

This Tweak is available as part of the Tweakr WordPress Plugin.

WordPress: Get Raw Document Title without Blog Name

document_title_parts, wp_get_document_title

Sometimes it is necessary to retrieve the current Document Title (used in the title tag) without the blog name or separators. As of WordPress 4.4 the wp_get_document_title() function become available which should be used to fetch the title – unfortunately it doesn’t accept any arguments and it is not possible to access the pure page title directly. Instead we can hook into the document_title_parts filter which allows us to access all title parts (title, page, tagline, site).

Workaround#

// workaround to retrieve the document title
function getDocumentTitle(){
    // temporary title
    $documentTitle = 'Unknown';

    // extractor function
    $extractor = function($parts) use (&$documentTitle){
        if (isset($parts['title'])){
            $documentTitle = $parts['title'];
        }
        return $parts;
    };

    // add filter to retrieve the page title
    add_filter('document_title_parts', $extractor);

    // trigger title generation
    wp_get_document_title();

    // remove filter
    remove_filter('document_title_parts', $extractor);

    // return result
    return $documentTitle;
}

 

Render Markdown/GFM Documents online using the GitHub v3 API

simple code snipped to convert markdown to html, public github api

Sometimes, you need to render parts of your Markdown documents – e.g. README.md or CHANGES.md – as html to embed it into your application, documentation or project website. There are a several markdown or especially GFM (GitHub Flavored Markdown) libraries are out there, but they require an additional setup and have to be maintained.

The simple Way#

Thanks to GitHub, there is a public API available which allows you to render your documents by the GitHub webservices.

PHP Client#

/**
 * Render Markdown content using the GitHub v3 Markdown API
 * @see https://developer.github.com/v3/markdown/
 * @source https://andidittrich.com/2016/05/render-markdown-gfm-documents-online-using-the-github-v3-api
 * @license: MIT
 * @return string(html)
 */
function renderGFM($text, $repositoryContext = null){

    // create the payload
    // @see https://developer.github.com/v3/markdown/
    $postdata = json_encode(
        array(
            'text' => $text,
            'mode' => ($repositoryContext != null ? 'gfm' : 'markdown'),
            'context' => $repositoryContext
        )
    );

    // prepare the HTTP 1.1 POST Request
    $opts = array('http' =>
        array(
            'method'  => 'POST',
            'protocol_version' => '1.1',
            'user_agent' => $repositoryContext,
            'header'  => array(
                'Content-type: application/x-www-form-urlencoded;charset=UTF-8',
                'Connection: close',
                'Accept: application/vnd.github.v3+json'
            ),
            'content' => $postdata
        )
    );

    // send request
    return file_get_contents('https://api.github.com/markdown', false, stream_context_create($opts));
}

Usage#

The optional $repositoryContext argument allows your to define the context which should be used for rendering to e.g. enable issue linking

// fetch the document (example)
$document = file_get_contents('https://raw.githubusercontent.com/AndiDittrich/WordPress.Enlighter/master/CHANGES.md');

// render html using the GitHub GFM API
$html = renderGFM($document, 'AndiDittrich/WordPress.Enlighter');

// show it!
echo $html;

 

 

You may have noticed, that normal users (especially Author’s and Contributor’s) are not allowed to use all kind of HTML Tags and related Attributes.

Those elements got removed by the WordPress buil-in KSES Filter – and it’s a very useful feature in matter of security to prevent html-code-injection.

But sometimes it is required to enable some additional html tags and/or attributes. You can modify the list of allowed html tags and attributes by appling a custom filter:

The Filter#

Example how to allow EnlighterJS related attributes for pre and code tags

function ksesAllowHtmlCodeAttributes($data, $context){
    // only apply filter on post-context
    if ($context === 'post'){

        // list of all available enlighterjs attributes
        $allowedAttributes = array(
            'data-enlighter-language' => true,
            'data-enlighter-theme' => true,
            'data-enlighter-group' => true,
            'data-enlighter-title' => true,
            'data-enlighter-linenumbers' => true,
            'data-enlighter-highlight' => true,
            'data-enlighter-lineoffset' => true
        );

        // apply to pre and code tags
        $data['pre'] = array_merge($data['pre'], $allowedAttributes);
        $data['code'] = array_merge($data['code'], $allowedAttributes);
    }

    return $data;
}

// add the filter function (2 arguments and priority 100)
add_filter('wp_kses_allowed_html', 'ksesAllowHtmlCodeAttributes', 100, 2);

 

 

 

Single File ReCaptcha 2 PHP Client

leading captcha system, curl, php, json-response

Today, a web-form without a proven captcha system generates a lot of spam entries and data-trash in your database. One of the best is ReCaptcha (even the latest v2).

Google provides an easy to use ReCaptcha PHP Client – but it’s a bit over engineered! You need a bunch of PHP files and a composer based environment to use it out of the box. This can cause some trouble in highly customized/optimized projects.

Therefore, here is a “one-file” solution which works without any configuration overhead:

Usage#

require('ReCaptcha.php');

// register your secret
ReCaptcha::setSecret('<your-secret>');

// some code ...

// check user form
if (ReCaptcha::isValid()){ ...

One-File Solution#

// Developer Guide: https://developers.google.com/recaptcha/docs/verify
class ReCaptcha{

    // ReCaptcha API Endpoint
    const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify';

    // the last result
    private static $_result = null;

    // client secret
    private static $_secret = null;

    // validate
    public static function isValid(){
        // token available ?
        if (!isset($_POST['g-recaptcha-response'])){
            return false;
        }

        // extract token
        $token = trim($_POST['g-recaptcha-response']);

        // generate url
        $params = http_build_query(array(
            'secret' => self::$_secret,
            'response' => $token,
            'remoteIp' => $_SERVER['REMOTE_ADDR']
        ), '', '&');

        // create curl based post request
        $handle = curl_init(self::SITE_VERIFY_URL);
        $options = array(
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $params,
            CURLOPT_HTTPHEADER => array(
                'Content-Type: application/x-www-form-urlencoded'
            ),
            CURLINFO_HEADER_OUT => false,
            CURLOPT_HEADER => false,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_SSL_VERIFYPEER => true
        );
        curl_setopt_array($handle, $options);
        $response = curl_exec($handle);
        curl_close($handle);

        // decode response
        self::$_result = json_decode($response, true);

        // check
        return (self::$_result['success'] === true);
    }

    // error occurred ?
    public static function isError(){
        return (self::$_result['success'] === false);
    }

    // get error message from last request
    public static function getErrorMessages(){
        return self::$_result['error-codes'];
    }

    // set client secret
    public static function setSecret($s){
        self::$_secret = $s;
    }
}

 

Maybe your wondering about some HTTP Headers sent to your browser which are not set in your script by the header() function ? Especially these Cache-Control headers:

Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache

First of all: everything is ok! These headers are automatically set by the PHP Session module to prevent browser/proxy based caching of your pages. Depending on your environment setup, it’s possible to control these headers by using the session_cache_limiter() function or use the php.ini

To disable these behaviour just pass an empty string to the session_cache_limiter() function as mentioned in the documentation:

Solution#

// add this line to the beginning of your php script to disable the cache limiter funktion:
session_cache_limiter('');

HowTo: Upgrade Notification for WordPress Plugins

add version based notices to the wordpress plugin page; wp 4.2

Notify your users!# Sometimes, your WordPress plugin will have a major release which may causes groundbreaking API changes. In the past, WordPress displays the “Upgrade Notice” section from readme.txt directly to the users but it seems currently broken – i’ve got no notification for any plugin in the last few month. Therefore i’ve carried out […]

PHP-FPM “Access Denied” on .phtml Files

a problem which took about 1h of research..

Some Weeks ago, i switched most of the webserver setups from custom spawn-fcgi init scritps to php-fpm and everything seems to work fine until today. The php-version of GitHubButtons won’t work anymore – just a text-message appears: “Access Denied.” First of all i thougt it was a problem with lighttpd and the fastcgi.map-extensions directive, but the error message doesn’t seem to be served by lighttpd…and well…it was a php-fpm related issue, beacause php-fpm only processes .php files by default!

You will not find these directive in the official FPM Documentation on php.net – it’s missing including tons of other directives. To get an overview about all possible php-fpm config keys, you should take a look into to default php-fpm.conf file included into the php-sources (sapi/fpm/php-fpm.conf) – also attached to this post!

Important: This directive can’t be used in global context, it’s a pool based config key!

Examle Pool: php-fpm.conf#

[pool-testwww]
; Limits the extensions of the main script FPM will allow to parse. This can
; prevent configuration mistakes on the web server side. You should only limit
; FPM to .php extensions to prevent malicious users to use other extensions to
; exectute php code.
; Note: set an empty value to allow all extensions.
; Default Value: .php
; Recommended: .php .phtml
security.limit_extensions = .php .php3 .php4 .php5 .phtml

Add Links to WordPress Plugin Page (Metadata Row)

Additional News/Update or Settings Links

Sometime you want to add special links directly to the plugin-page. For example, you can enable an easy access to the plugin’s settings-page or to related docs/resources. All the magic is done within the plugin_row_meta hook.

It’s important to check which plugin is currently processed (there are no plugin/namespace specific hooks). Therefore you have to check which plugin-(main)file is selected: if ($file == 'enlighter/Enlighter.php'){

Additional links can be added to the $links array including I18n support.

Appearance#

additional_plugin_links

How it’s done#

// add links
add_filter('plugin_row_meta', 'addPluginPageLinks', 10, 2);

// links on the plugin page
function addPluginPageLinks($links, $file){
  // current plugin ?
  if ($file == 'enlighter/Enlighter.php'){
    $links[] = '<a href="'.admin_url('options-general.php?page='.plugin_basename(__FILE__)).'">'.__('Settings', 'enlighter').'</a>';
    $links[] = '<a href="https://twitter.com/andidittrich">'.__('News & Updates', 'enlighter').'</a>';
  }
  
  return $links;
}

Cryptex 4.0 with Retina/HighDPI support

behind the scene – high-resolution, css based images using media queries

The Story# No scrapers. No harvesters. No spambots. That’s our goal. A several years ago, i’ve released the first version of the Crypex Plugin to protect E-Mail-Addresses on WordPress based websites. It works great but currently many mobile devices like tablets, smartphones are using high-dpi displays which results in blurred E-Mail-Addresses. Therefore i’ve create a […]