Keep search parameters when using WP_List_Table and navigation

WP_List_Table is a reasonably flexible way of creating data tables in WordPress Admin Plugins. While there are plenty of warnings and words of caution against using WP_List_Table throughout the WordPress documentation and elsewhere on the Internet, it is a very popular method in use by many plugin authors.

Whether or not it’s a good idea to use WP_List_Table for your plugin is beyond the scope of this post.

While developing a plugin that is using WP_List_Table, I came across a rather well-known issue. The issue is that once the user enters search data, you would (logically) think that the search data is maintaned while navigating the search result. Unfortunately, when the user clicks on the previous and next links, or clicks on one of the table headers to re-sort the search result, the search is “lost”.

This can, technically speaking, be avoided if you resort to using METHOD="GET" rather than METHOD="POST" in your table form. But I wanted to avoid this since the entire GET request is passed in the URL and thus ends up in logs, etc.

So, how to use POST instead of get, and still maintain the search parameter across page loads?

The WP_List_Table class makes extensive use of the set_url_scheme() function to create various headers and clickable links in the table view. Unfortunately, this function is rather generic in nature and does not take the parameters from WP_List_Table::search_box() into account. But, as is the case with most functions in WordPress, as a last action, the set_url_scheme() function calls any hooked filters before it returns.

Keep search parameters between page loads

The example assumes $your_table is an object derived from WP_List_Table.

echo '<div class="wrap">';
echo '<form action="admin.php?page=your_plugin_table" method="post" name="your_table_form">';
$your_table->prepare_items();
$your_table->mangle_url_scheme_start();
$your_table->search_box();
$your_table->display();
$your_table->mangle_url_scheme_stop();
echo '</form>';
echo '</div>';

The two functions of interest here are mangle_url_scheme_start() and its companion, mangle_url_scheme_stop(). These go into your class derived from WP_List_Table like this:

public function mangle_url_scheme_start() {
    add_filter( 'set_url_scheme', [$this, 'mangle_url_scheme'], 10, 3 );
}

and

public function mangle_url_scheme_stop() {
    remove_filter( 'set_url_scheme', [$this, 'mangle_url_scheme'], 10 );
}

The last piece of the workaround is the mangle_url_scheme() function:

public function mangle_url_scheme( string $url, 
                                   string $scheme, 
                                   $orig_scheme ) {
    if ( ! empty( $url ) 
        && mb_strpos( $url, '?page=your_plugin_table' ) !== false 
            && isset( $_REQUEST['s'] ) ) {
        $url = add_query_arg( 's', urlencode( $_REQUEST['s'] ), $url );
    }
    return( $url );
}

This is only done on your plugin table page, and it’s only done if the $_REQUEST['s'] parameter is set. I can’t see how this will cause any issues further down the road, but you may experience different results. You may want to sanitize the contents of $_REQUEST['s'] further before you urlencode it.

I am reasonably convinced that there are other, and better, ways of doing this. But I haven’t found one yet.

 

var_dump() for Javascript

PHP, for example, has var_dump() and print_r() to facilitate quick variable dumps to log files, etc.

For Javascript, one can use console.log() in combination with JSON.stringify() to achieve something similar.

  console.log( JSON.stringify(object) );

It’s also possible to “pretty-print” the output like so:

  console.log( JSON.stringify(object, null, 2) );

For more information about JSON.stringify(), check out this page

Cloudbridge Mattermost

Cloudbridge Mattermost is a WordPress plugin that provides integration between WordPress and Mattermost. In the initial release, we focus on login notifications (successful, failed, unknown) from the various WordPress user roles.

We will, of course, be adding more functionality in the future.

You can download it from the official WordPress repository:
wordpress.org/plugins/cloudbridge-mattermost

You can also get it from GitHub, should you prefer that source:
github.com/joho1968/cloudbridge-mattermost

This is the initial release of this plugin.

Displaying WordPress roles in the current language

While writing a WordPress plugin that displays the available user roles, I came across a snag: the user roles that I had fetched from WordPress weren’t translated into the site’s current language.

I’m not quite sure I understand the reasoning behind this since WordPress offers functions to return an array with the actual role as the key, and the display string as the array value, like:

array( 'administrator' => 'Administrator' );

So why can’t WordPress return the “correct” (i18n) language string for the array value … anyway, there’s a solution.

Looking at how WordPress does it under “Settings > General” in the WordPress Admin, I eventually found a call to translate_user_role(), which requires one parameter, the role array name value, e.g. “Administrator”. The function will then return the correct (language context aware) display string.

So to put it into a functional context, it may look something like this:

function i18n_get_wp_roles() {
    $wp_roles = wp_roles();
    if ( is_object( $wp_roles ) ) {
        $roles = array_keys( $wp_roles->roles );
        $role_names = $wp_roles->get_names();
    } else {
        $roles = false;
        $role_names = array();
    }
    $return_roles = array();
    if ( is_array( $roles ) ) {
        foreach( $roles as $role_k => $role_v ) {
            if ( ! empty( $role_names[$role_v] ) ) {
                $return_roles[$role_v] = translate_user_role( $role_names[$role_v] );
            } else {
                $return_roles[$role_v] = 'Unknown role (' . $role_v . ')';
            }
        }
    } else {
        error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): wp_roles() returned empty' );
    }
    return( $return_roles );
}

 

Where did my Emacs color-theme go in Ubuntu 20.04.LTS?!

Having recently upgraded a small VPS from Ubuntu 18.04.LTS to Ubuntu 20.04.LTS, I ran into a little snag with Emacs and its color-theme (from the emacs-goodies-el package).

After some digging, it seems this is now done somewhat differently in Ubuntu 20.04.LTS.

This is what my .emacs file used to contain:

(require 'color-theme) 
(color-theme-initialize) 
(color-theme-charcoal-black)

This is what I changed it to:

(add-to-list 'custom-theme-load-path 
(file-name-as-directory "/usr/share/emacs/site-lisp/elpa/color-theme-modern-0.0.2"))
(load-theme 'charcoal-black t t) 
(enable-theme 'charcoal-black)

In addition, I also had to install the elpa-color-theme-modern package (from the Ubuntu 20.04.LTS distribution).

 

pdftk and php-pdftk on Ubuntu 18.04 without using snap

During a product launch I recently came across an “interesting” issue involving pdftk (and php-pdftk). Some of the developers had made assumptions (ever heard that one before?) about the operating environment and how things were/are configured.

These assumptions were based on a development environment that in no way reflected the final production environment (ever heard that one before?). In this particular case, they were expecting the great PDF toolkit (pdftk) to be available and working just like it did in their development environment.

To summarize the issue: pdftk has been removed from Ubuntu 18.04 due to dependency issues. The “recommended” solution is to install pdftk using snap. This, in itself, is not a bad recommendation. But in a web server environment, it may put you in a place you don’t want to be in.

So after a few hours of toying with ideas and testing various things, I figured there must be some Debian-like package that would actually work when installed on Ubuntu 18.04 and that is not a snap package.

There is.

Later versions (or packages) of pdftk now exist as pdftk-java, and they do work with php-pdftk as well.

In my case, I located pdftk-java_3.1.1-1_all.deb and installed it. Or tried to rather. It has a number of dependencies that you will see for yourself. You’ll need to decide if their “weight” makes it worthwhile for you to go down this path. But it was one, reasonably good, way for us to solve the problem.

The developers you ask? They have been sent to /dev/codersgulag/cobol and will spend a number of solar iterations there.

(The file I ended up using was http://ftp.debian.org/debian/pool/main/p/pdftk-java/pdftk-java_3.1.1-1_all.deb, and it does work on Ubuntu 18.04.LTS)

 

 

Nextcloud and WebDAV “SEARCH” requests

Using WebDAV to access Nextcloud resources is a pretty nice way to write integrations for Nextcloud. While adding support for Nextcloud file browsing in a service we’re constantly improving, I ran into a minor snag: How to format the WebDAV “SEARCH” request when interfacing with Nextcloud.

1. The example in the official documentation is correct. You just have to read it carefully. In particular, pay attention to this:

Search requests can be made by sending a SEARCH http request to https://cloud.example.com/remote.php/dav/ with a content type of text/xml and the query as xml in the request body.

2. Make sure you structure your XML data properly, in particular, you need to make sure you include a d:prop of which “field” you want your search data to be applied to.

<d:searchrequest xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
  <d:basicsearch>
    <d:select>
      <d:prop>
        <oc:fileid/>
        <d:displayname/>
        <d:getcontenttype/>
      </d:prop>
    </d:select>
    <d:from>
      <d:scope>
        <d:href>/files/testuser/</d:href>
        <d:depth>infinity</d:depth>
      </d:scope>
    </d:from>
    <d:where>
      <d:like>
        <d:prop>
          <d:displayname/>
        </d:prop>
        <d:literal>Nextcloud%</d:literal>
      </d:like>
    </d:where>
    <d:orderby/>
  </d:basicsearch>
</d:searchrequest>

This would search for files beginning with Nextcloud and return fileid, displayname, and the content type; the search will be made in the folders that testuser has access to.

And that’s that.

 

Please use proper message headers (FFS)

Writing software that deals with automated message processing on the Internet is, in a word, painful. The number of people who cannot seem to read simple specifications and/or proposals is staggering.

And one would think that if you can’t read specifications, you’d at least bother to “View message source” on a few thousand e-mail message to get a feel for how some other (well known) companies are doing it, INSTEAD OF INVENTING YOUR OWN STANDARD.

So, if you’re sending out automated e-mail messages, for example, please add one of these headers:

auto-submitted
x-auto-response-supress
precedence
feedback-id
x-autoreply

I’ll leave it as an exercise for the reader to look them up … and if you’re doing some kind of mailing list stuff, please add:

list-id
list-unsubscribe

Thank you!

You may now resume sleep mode.

SwiftMailer 5, PHP5, and ISO-8859-15

I’ve been using SwiftMailer for as long as I can remember because it’s, IMHO, a great library and far more logical than PHPMailer and “others”. While maintaining a PHP5 codebase for a rather large project, I ran into an issue while using ISO-8859-15 encoded data. Most people won’t care about ISO-8859-15 over ISO-8859-1 (“Latin1”), but since I live in Europe, I prefer to have support for the €uro character 🙂

To make a long story short, if you need SwiftMailer 5 to properly handle ISO-8859-15, look for a line in “MimePart.php” that looks like this:

if (!in_array($charset, array('utf-8', 'iso-8859-1', ''))) {

and change it to this:

if (!in_array($charset, array('utf-8', 'iso-8859-15', 'iso-8859-1', ''))) {

(Yes, the project will move to a PHP7 codebase at some not so distant point in the future.)