Tutorial: Column "Application" in Oxygen Templates List
There's a current version of this snippet.
Contents
Task
Oxygen is a powerful plugin for WordPress that deactivates the WordPress theme, supports the creation of own templates with a visual builder and thus enables a fully individual design for your website.
Within each Oxygen template you define the conditions under which it will be used, e.g. for the archive or blog, search results, single pages or posts, for all or only certain categories, authors, etc.
Unfortunately, these conditions are not visible in the list of templates.
When working with a larger number of templates, the overview is quickly lost.
Solution
So I developed a code snippet that displays the defined conditions for each template in a new column "Application" in the list of Oxygen templates.

Development
In this article I describe how I implemented the requirement in a code snippet.
With this I want to give an insight into how the development is done and how to achieve the end result in this case.
Some basic knowledge of WordPress development is a prerequisite for understanding this article.
Data source
To solve this task I first have to understand how the conditions for the conditions for using the templates are stored in the WordPress database. For this purpose I create a template "Demo", where I check or fill in all conditions.
In the table postmeta
I inspect the corresponding meta data:

postmeta
The structure and meaning of the data is clear to me quite fast.
Code Snippet
My favorite method to implement code is the plugin Code Snippets. Here I create a new snippet.
It only needs to be executed in the WordPress administration area. So I set the toggle "Only run in administration areas".
New Column in Template List
From previous projects I already know the procedure to add new columns to administrative list views in WordPress and to fill them with data.
Basically these are two steps for which WordPress provides filter hooks:
- Add a column with title using a filter hook to
manage_xxx_posts_columns
, where thexxx
is replaced by the post type slug (post, page, …) - Add column content using a filter hook to
manage_xxx_posts_custom_column
, where thexxx
is replaced by the … see above.
Oxygen templates have the post type ct_template
.
This results in the world changing formula xxx = ct_template
.
With this knowledge I can create the basic framework in my Code Snippet as follows:
<?php // add column title add_filter('manage_ct_template_posts_columns', 'maltmann_oxygen_template_table_head'); function maltmann_oxygen_template_table_head( $columns ) { $columns['maltmann_oxy_tpl_apply'] = 'Application'; return $columns; } // add column content add_action( 'manage_ct_template_posts_custom_column', 'maltmann_oxygen_template_table_content', 10, 2 ); function maltmann_oxygen_template_table_content( $column_name, $post_id ) { if ($column_name != 'maltmann_oxy_tpl_apply') return; echo 'my column content'; }
In line 2 I add a new filter to the hook manage_ct_template_posts_columns
.
Here xxx
is already replaced by the correct post type ct_template
.
This hook calls my own function maltmann_oxygen_template_table_head()
.
In the parameter $columns
an array with the previously registered table columns is passed to my function.
I add my new column named maltmann_oxy_tpl_apply
to the array and return the new array $columns
.
After that in line 9 follows my filter for the hook manage_ct_template_posts_custom_column
, which I use to output the column contents.
At the very beginning I check in line 11 whether the hook was actually called for my new column. If not, get out!
Then I output the content of the column in line 12. Let's make a provisional "my column content" first.
Save it. Test it.
Looks already acceptable:

Determine metadata
To access the data stored in the table postmeta
from my snippet in the first place, I have to fetch it.
This works quite well with the function get_post_meta()
.
Since this function requires a $post_id
parameter, I have to get access to the current $post
first.
<?php function maltmann_oxygen_template_table_content( $column_name, $post_id ) { if ($column_name != 'maltmann_oxy_tpl_apply') return; global $post; $meta = get_post_meta($post->ID); }
I'm a bit worried because the function get_post_meta()
returns all meta data for this $post
from the database without any restriction to certain meta data. This would also include all tens of revisions for each template. Of course I want to avoid this.
However, by debugging I quickly find out that the meta data was read in one go when the templates were listed and with the function get_post_meta()
I access the data already in the cache. All fine.
Populate column: 1) Other
The easiest seems to be the conditions for the "Other" section.
Although the assignment is quite clear, I set and delete these conditions alternately to have the changes confirmed in the database.

With this knowledge I can output the first conditions in my new column.
<?php function maltmann_oxygen_template_table_content( $column_name, $post_id ) { if ($column_name != 'maltmann_oxy_tpl_apply') return; global $post; // get_post_meta does not fire a db query since post meta is still cached from listing $meta = get_post_meta($post->ID); // --- Others ---------------------------------------------------------- // collect settings for Oxygen's Other section $apply = []; if (@$meta['ct_template_front_page'][0] == 'true') {$apply[] = 'Front Page';} if (@$meta['ct_template_blog_posts'][0] == 'true') {$apply[] = 'Blog Posts Index';} if (@$meta['ct_template_search_page'][0] == 'true') {$apply[] = 'Search Page';} if (@$meta['ct_template_404_page'][0] == 'true') {$apply[] = '404';} if (@$meta['ct_template_inner_content'][0] == 'true') {$apply[] = 'Inner Content';} if (@$meta['ct_template_index'][0] == 'true') {$apply[] = '<i>Catch All</i>';} // Others Final Output if (count($apply)) {echo '<b>Others:</b><br/>' . join('<br/>',$apply).'<br/>';} }
In line 10 I first define an array $apply
, where I will store all conditions.
Then I can read all previously identified conditions from the metadata and store them in an understandable form in my array.
WordPress delivers meta data as array. Therefore I have to access the first element of the array with [0]
. And since it is not guaranteed that I get back valid data at all, I access the elements with the prefix @
. This suppresses warnings in the debug log.
Afterwards I check in line 18 if I have collected any conditions in the section "Other" and output them a bit prettified.
Save. Test.
I am satisfied:

Populate column: 2) Singular
Next level of difficulty are the conditions in the section "Singular". Here we have the definition of single post types, taxonomies and parent IDs. This will be more exciting.
In the database I identify the following links:

Beside the simple boolean fields I see here serialized stored data ("a:2:{i:0;s:11…
"). I have to convert them back into data structures first. In PHP this is normally done with unserialize()
. But WordPress offers an even more practical function maybe_unserialize()
, which doesn't fail if the data cannot be deserialized.
The additional code looks like this:
<?php // --- Singular -------------------------------------------------------- // collect settings for Oxygen's Sigular section $apply= []; // Singular Post Types $post_types = []; if (@$meta['ct_template_single_all'][0] == 'true') {$post_types[] = '<i>All</i>';} $specific_post_types = maybe_unserialize(@$meta['ct_template_post_types'][0]); if (is_array($specific_post_types) && count($specific_post_types)) { $post_types = array_merge($post_types,$specific_post_types); } if (is_array($post_types) && count($post_types)) { $apply[] = 'Post Types: ' . join(', ',$post_types); } // Singular Taxonomies if (@$meta['ct_use_template_taxonomies'][0]=='true') { $taxonomies = maybe_unserialize(@$meta['ct_template_taxonomies'][0]); if (is_array($taxonomies) && count($taxonomies)) { $apply[] = 'Taxonomies: ' . join(', ',$taxonomies); } } // Singular Parents if (@$meta['ct_template_apply_if_post_of_parents'][0]=='true') { $parents = maybe_unserialize(@$meta['ct_template_post_of_parents'][0]); $apply[] = 'Parent IDs: '.join(', ',$parents); } // Singular Final Output if (count($apply)) {echo '<b>Singular:</b><br/>'.join('<br/>',$apply).'<br/>';}
Save. Test.
Not quite how I imagine it yet:

Then I probably need some helper functions to display post types localized and to convert taxonomies (categories, keywords) from ID (or array) to text.
<?php //----------------------------------------------------------------------------- // Helper function to translate post type slugs (page, post, ...) to localized names (Seiten, Beiträge, ...) function _maltmann_parse_oxy_post_types($post_types) { $retval = []; if (is_array($post_types) && count($post_types)) { foreach($post_types as $post_type) { $post_type_object = get_post_type_object($post_type); if ($post_type_object) {$retval[] = $post_type_object->label;} } } return $retval; } //------------------------------------------------------------------------------ // Helper function to retrieve term names from term IDs list or Oxygen names,values array function _maltmann_parse_oxy_taxonomies($taxonomies) { $retval = []; if (is_array($taxonomies) && count($taxonomies)) { if (array_key_exists('values',$taxonomies)) {$taxonomies = $taxonomies['values'];} foreach ($taxonomies as $term_id) { $term = get_term($term_id); if ($term) {$retval[] = $term->name;} } } return $retval; }
The function _maltmann_parse_oxy_post_types()
determines the localized label for post types.
With the function _maltmann_parse_oxy_taxonomies()
I determine the actual names of the taxonomies, i.e. categories and keywords, based on IDs. You can pass a simple array with IDs to the function as well as an array with the fields names and values.
These are two variants of how Oxygen stores taxonomies for different conditions. The interested reader is welcome to debug this in detail by himself. For 5 Euro I add the detailed explanations here.
In my opinion I have implemented sufficient error handling in both help functions to be prepared for all cases.
The function maltmann_oxygen_template_table_content()
must now of course be adjusted a bit to implement the new functions.
<?php // --- Singular -------------------------------------------------------- // collect settings for Oxygen's Sigular section $apply= []; // Singular Post Types $post_types = []; if (@$meta['ct_template_single_all'][0] == 'true') {$post_types[] = '<i>All</i>';} $specific_post_types = maybe_unserialize(@$meta['ct_template_post_types'][0]); if (is_array($specific_post_types) && count($specific_post_types)) { $post_types = array_merge($post_types,_maltmann_parse_oxy_post_types($specific_post_types)); } if (is_array($post_types) && count($post_types)) { $apply[] = 'Post Types: ' . join(', ',$post_types); } // Singular Taxonomies if (@$meta['ct_use_template_taxonomies'][0]=='true') { $taxonomies = maybe_unserialize(@$meta['ct_template_taxonomies'][0]); $taxonomies = _maltmann_parse_oxy_taxonomies($taxonomies); if (is_array($taxonomies) && count($taxonomies)) { $apply[] = 'Taxonomies: ' . join(', ',$taxonomies); } } // Singular Parents if (@$meta['ct_template_apply_if_post_of_parents'][0]=='true') { $parents = maybe_unserialize(@$meta['ct_template_post_of_parents'][0]); $apply[] = 'Parent IDs: '.join(', ',$parents); } // Singular Final Output if (count($apply)) {echo '<b>Singular:</b><br/>'.join('<br/>',$apply).'<br/>';}
Save. Test.

Populate column: 3) Archives
Now it will get funny. The "Archives" section consists mostly of references to something.
I search the references in the database again:

I already have helper functions for post types and taxonomies. For authors (users) I will need a new helper function:
<?php //------------------------------------------------------------------------------ // retrieves author IDs to names function _maltmann_parse_oxy_authors($authors) { $retval = []; if (is_array($authors) && count($authors)) { foreach ($authors as $user_id) { if ($user_id == 'all_authors') {$retval[] = '<i>All</i>'; continue;} $user = get_userdata($user_id); if ($user) {$retval[] = $user->user_login;} } } return $retval; }
This helper function can also handle errors well.
After that I can prepare the conditions for the section "Archives" with the already existing resources.
<?php // --- Archive --------------------------------------------------------- // collect settings for Oxygen's Archive section $apply = []; // All Archives if (@$meta['ct_template_all_archives'][0]=='true') {$apply[] = '<i>All</i>';} // Archive Taxonomies if (@$meta['ct_template_apply_if_archive_among_taxonomies'][0]=='true') { $taxonomies = maybe_unserialize(@$meta['ct_template_archive_among_taxonomies'][0]); $taxonomies = _maltmann_parse_oxy_taxonomies($taxonomies); if (is_array($taxonomies) && count($taxonomies)) { $apply[] = 'Taxonomies: ' . join(', ',$taxonomies); } } // Archive Post Types if (@$meta['ct_template_apply_if_archive_among_cpt'][0]=='true') { $post_types = maybe_unserialize(@$meta['ct_template_archive_post_types'][0]); $post_types = _maltmann_parse_oxy_post_types($post_types); if (is_array($post_types) && count($post_types)) { $apply[] = 'Post Types: ' . join(', ',$post_types); } } // Archive Authors if (@$meta['ct_template_apply_if_archive_among_authors'][0]=='true') { $authors = maybe_unserialize(@$meta['ct_template_authors_archives'][0]); $authors = _maltmann_parse_oxy_authors($authors); if (is_array($authors) && count($authors)) { $apply[] = 'Authors: ' . join(', ',$authors); } } // Archive Date if (@$meta['ct_template_date_archive'][0] == 'true') {$apply[] = 'Date Archive';} // Archive Final Output if (count($apply)) {echo '<b>Archive:</b><br/>'.join('<br/>',$apply).'<br/>';}
Save. Test.
THIS looks good!
