Probably it is something obvious and has escaped under my nose.
I did not understand how to hook my extended class to timber.
Let's take the example of the issues. How can I get an object like MySitePost when I call Timber::query_post()?
So now I’ve got an easy way to refer to the {{ post.issue }} in our twig templates.
Looks like an automatic process... but I'm not so sure about this!
For reference:
https://timber.github.io/docs/guides/extending-timber/
I think this is a good question that might not be so obvious.
To use your custom post, you’ll often find a parameter when fetching posts where you can pass the name of your custom class. The returned object will then be instances of your custom class.
$posts = Timber::get_posts( $your_args, 'MySitePost' );
$posts = new Timber\PostQuery( $your_args, 'MySitePost' );
When you want to fetch a single post, it works quite similar. You can either directly instantiate your post or pass your post class to the function.
$post = new MySitePost();
$post = Timber::get_post( $post_id, 'MySitePost' );
If you want to set a default class to be used for your posts, you can use the Timber\PostClassMap filter:
// Use one class for all Timber posts.
add_filter( 'Timber\PostClassMap', function( $post_class, $post_type ) {
return 'MySitePost';
}, 10, 2 );
// Use default class for all post types, except for pages.
add_filter( 'Timber\PostClassMap', function( $post_class, $post_type ) {
// Bailout if not a page
if ( 'page' !== $post_type ) {
return $post_class;
}
return 'MySitePost';
}, 10, 2 );
// Use a class map for different post types
add_filter( 'Timber\PostClassMap', function( $post_class, $post_type ) {
return array(
'post' => 'MySitePost',
'apartment' => 'MyApartmentPost',
'city' => 'MyCityPost',
);
}, 10, 2 );
Related
I have my custom options set like this...
function my_options_init(){
register_setting(
'myoptions',
'myoptions',
'myoptions_validate'
);
}
add_action( 'admin_init', 'my_options_init' );
...and I'm used to setting variables on my custom settings page in this manner...
$myoptions = get_option( 'myoptions' );
$foo = $myoptions['foo'];
But when debugging is enabled I get an error:
Notice: Undefined index: foo
The solution as I understand it is to do this...
if(isset($myoptions['foo'])) {
$foo = $myoptions['foo'];
}
..which makes the error go away.
The problem is that I have many variables I want to use in my plugin, and it seems like a lot of unnecessary work to do this every time I need to use a variable.
Then I stumbled on this in another topic:
"...to avoid having to include this check everytime you are getting a
setting from $myoptions - would be to review your
myoptions() function and make sure it returns an
array that includes every setting, including default values for those
settings that are not saved in the database yet."
My question is, how would I go about doing that?
You can setup a function for default values. Then when you fetch plugin option, you can fetch options with defaults. After that even if there is no options in the database, function will get value from default. So no PHP notice. Please check following example.
function wpso_get_default_options() {
// Default plugin options.
$default = array(
'foo' => 1,
'bar' => 'left',
);
return $default;
}
function wpso_get_plugin_option( $key ) {
$defaults = wpso_get_default_options();
$plugin_options = get_option( 'myoptions' );
$plugin_options = wp_parse_args( $plugin_options, $defaults );
$value = null;
if ( isset( $plugin_options[ $key ] ) ) {
$value = $plugin_options[ $key ];
}
return $value;
}
I'm trying to convert my standard PHP WordPress theme to Timber/Twig and am having trouble getting any output from custom functions. This one in particular looks to see if the post has a Yoast Primary Term set, which allows you to specify a primary category for a post that has multiple categories.
I need to do this in within The Loop and most of the documentation talks about how to do it in a single page. I have a function like this in my functions.php:
function my_theme_get_post_category() {
// regular list of categories set in WP
list( $wp_category ) = get_the_category();
// primary category set with Yoast plugin
$primary_category = new WPSEO_Primary_Term( 'category', get_the_ID() );
$primary_category = $primary_category->get_primary_term();
$primary_category = get_category( $primary_category );
// use only one or the other
if ( is_wp_error( $primary_category ) || $primary_category == null ) {
$category = $wp_category;
} else {
$category = $primary_category;
}
return $category;
}
Based on what I've read in the "Functions" section here (https://github.com/timber/timber/wiki/WP-Integration#functions), I should be able to call this in my template with {{ function('my_theme_get_post_category', post.ID) }}, but that does not work.
I tried making $postID a required parameter of the function, but that also didn't help anything.
I also tried using the TimberHelper::function_wrapper and then calling it in the template with {{ my_theme_get_post_category }} but, again, that didn't accomplish anything.
If you use {{ function('my_theme_get_post_category', post.ID) }}, then the function that you call needs to accept the argument that you pass. When you use…
function my_theme_get_post_category() {
// Your function code
}
… then your post ID won’t be passed to the function. As you mentioned, you probably already tried to add the post id as a parameter:
function my_theme_get_post_category( $post_id ) {
// Your function code
}
And nothing happened. That’s because your function uses functions that depend on The Loop, like get_the_category() or get_the_ID(). These functions get the current post id from global variables, which are not always set when you loop through posts in Timber.
When using Timber, you need to tell these functions to use a certain post id. If you look at the documentation for get_the_category(), you see that there’s one optional argument that you can pass: the post id.
For the other function, get_the_ID(), you can simply replace it with the parameter $post_id that you pass to your function.
function my_theme_get_post_category( $post_id ) {
// regular list of categories set in WP
list( $wp_category ) = get_the_category( $post_id );
// primary category set with Yoast plugin
$primary_category = new WPSEO_Primary_Term( 'category', $post_id );
$primary_category = $primary_category->get_primary_term();
$primary_category = get_category( $primary_category );
// use only one or the other
if ( is_wp_error( $primary_category ) || $primary_category == null ) {
$category = $wp_category;
} else {
$category = $primary_category;
}
return $category;
}
I am using the Custom Post Type UI plugin for worpdress and have created custom "pages". I have set these pages to be able to use the page template dropdown but would like to know if anyone knows of a way to show separate page templates for different post types?
You will need the theme_page_templates filter hook to remove the templates that you do not want to see for each post type. When the templates are loaded, including for display in the edit screen the templates that are returned are first passed to this function:
apply_filters ( 'theme_page_templates', array $page_templates, WP_Theme $this, WP_Post|null $post )
To implement you will use something like the following code:
function filter_theme_page_templates( $templates, $theme, $post ){
// make sure we have a post so we know what to filter
if ( !empty( $post ) ){
// get the post type for the current post
$post_type = get_post_type( $post->ID );
// switch on the post type
switch( $post_type ){
case 'custom-post-type':
// remove anything we don't want shown for the custom post type
// array is keyed on the template filename
unset( $templates['page-template-filename.php'] );
break;
default:
// if there is no match it will return everything
break;
}
}
// return the (maybe) filtered array of templates
return $templates;
}
add_filter( 'theme_page_templates', 'filter_theme_page_templates', 10, 3 );
I have created a custom post type "cinfo" and removed title and editor form the edit page. With the help of this code. Also displayed some custom meta fields which are relevant to my plugin.
function remove_box(){
remove_post_type_support('cinfo', 'title');
remove_post_type_support('cinfo', 'editor');
}
add_action("admin_init", "remove_box");
It looks something like this.
Now when i see the list page I still see the title with edit, view and delete button beneath it. which I don't want because the title field doesn't exist in the edit page So it looks a bit irrelevant in the listing page. Instead of that I tried to display the custom meta field "email" but I was only able to change the heading of the column. which looks something like this.
I just did some research and found one action and filter but they still didn't seems to be much of a help to me. Still for the better view of the problem. Also I tried to use a plugin Post List View Count but it also didn't accomplish my purpose. I hope You understand what I basically want to do. Thanks for your time to read my question.
add_filter('manage_cinfo_posts_columns', 'bs_cinfo_table_head');
function bs_cinfo_table_head( $defaults ) {
$defaults['title'] = 'Email';
return $defaults;
}
add_action( 'manage_cinfo_posts_custom_column', 'card_cinfo_table_content', 10, 2 );
function card_cinfo_table_content( $column_name, $post_id ) {
if ($column_name == 'title') {
echo "its working";
}
}
The action manage_cinfo_posts_custom_column will never be true. It's better to remove the defaults on manage_cinfo_posts_columns and do the regular custom stuff in the other filter.
I tried this with a fast class to test all together inside my setup:
class SO23467344
{
private $cpt = 'portfolio';
private $custom_field = 'video';
public function __construct()
{
add_filter( "manage_edit-{$this->cpt}_columns", array( $this, 'column_register' ), 20, 1 );
add_action( "manage_{$this->cpt}_posts_custom_column", array( $this, 'column_display' ), 20, 2 );
add_action( "admin_init", array( $this, "remove_box" ) );
}
function column_register( $columns )
{
$columns['my-col'] = 'My column';
# Remove default columns
unset( $columns['title'], $columns['categories'], $columns['comments'], $columns['date'] );
return $columns;
}
function column_display( $column_name, $post_id )
{
if ( 'my-col' != $column_name )
return;
if ( $field = get_post_meta( $post_id, $this->custom_field, true ) )
echo '<br/><strong style="color:#0f0;font-size:4em"> • </strong>';
}
function remove_box(){
remove_post_type_support( $this->cpt, 'title' );
remove_post_type_support( $this->cpt, 'editor' );
}
}
new SO23467344;
what is the earliest wordpress action where the post that will eventually be shown can be detected reliably ? (either by using global $post or detecting the wp_query object or other way)
eg. my plugin needs to detect an incoming request from another plugin on a different site, at the moment it checks for a $_POST var as early as possible by using add_action('plugins_loaded' and the callback function uses $post = get_page_by_path($_SERVER['REQUEST_URI'],'','post') to get the $post object and then uses the post data to get any other information that is used to process the response which gets sent back before any headers or other standard WP processing happens with the intention of lessening the load on the blog receiving the request.
is there a better way?, I know there isn't an earlier way because 'plugins_loaded' action gets called right after the , well, plugins get loaded but is there a more reliable way than using get_page_by_path ?
I’d try the filter 'the_posts'. You find it in wp-includes/query.php function get_posts(). It passes the found posts as a array by reference, so you can use it like an action.
Here is a plugin I use to inspect hooks:
<?php
/*
Plugin Name: Hook Check
Description: Inspects a hook and prints its information to the footer.
Version: 1.0
Required: 3.1
Author: Thomas Scholz
Author URI: http://toscho.de
License: GPL
*/
! defined( 'ABSPATH' ) and exit;
$GLOBALS['hook_checks'] = apply_filters(
'hook_check_filter'
, array ( 'the_posts' )
);
foreach ( $GLOBALS['hook_checks'] as $hc_hook )
{
add_action( $hc_hook, array( 'Hook_Check', 'catch_info' ) );
}
add_action( 'wp_footer', array( 'Hook_Check', 'print_info' ) );
class Hook_Check
{
static $info = array ();
public static function catch_info()
{
$args = func_get_args();
self::$info[ current_filter() ] = print_r( $args, TRUE );
return $args[0];
}
public static function print_info()
{
if ( empty ( self::$info ) )
{
return;
}
print '<pre>';
foreach ( self::$info as $filter => $catched )
{
print "<b>$filter</b>\n" . htmlspecialchars( $catched );
}
print '</pre>';
}
}
Reduced sample Output:
Array
(
[0] => Array
(
[0] => stdClass Object
(
[ID] => 112
[post_content] => The entire content …
[post_title] => An awesome title
[post_excerpt] =>
[post_status] => publish
)
)
)
This should give you all information you need as early as possible.
Oh, and I hope to see you on https://wordpress.stackexchange.com/ with such questions. :)