I am currently developing for a site that uses Timber in Wordpress. I am using an API of a different site to fetch posts from a different site, so they are up to date with the current state on that site. The problem is that I’m using the post title field to fetch the correct ID from the API. This means that no title or content data is stored in the database.
Is there a way I can register this data so that Timber PostQuery objects will also fetch these pages properly? I am unable to access or alter the result of $result = new Timber\PostQuery() afterwards, since these fields are protected and private.
Thanks in advance!
#Stan this is definitely an edge edge edge case. If you can get what you need down to WordPress IDs, these can be sent straight to PostQuery()...
$result = new Timber\PostQuery(array(2342, 2661, 2344, 6345,));
You could try extending the PostQuery class yourself to see if this is custom functionality you could wrap inside of it so the API you end up working with at the top-level is clean and simple
Timber is designed to be extended and customised to your use case.
You can create a custom class that extends Timber\Post and write your own methods for fetching the data from the API as necessary.
<?php
class CustomPost extends \Timber\Post {
/* not necessary, this just caches it */
private $myCustomContent;
/* override Timber\Post::content */
public function content(){
/* if we've fetched it from the API this request, return the result */
if ( $this->myCustomContent ) return $myCustomContent;
/* otherwise fetch it, then return the result */
return $this->fetchCustomContent();
}
/* function to fetch from external API */
private function fetchCustomContent(){
/* whatever the API call is here.. */
$result = wp_remote_post....
/* maybe some error handling or defaults */
/* cache it on the object's property we setup earlier */
$this->myCustomContent = $result->content;
return $this->myCustomContent;
}
}
Now to use our custom class we have two choices. We can manually decide when to use it by specifying it as the 2nd argument in our PostQuery()
<?php
/* Note: Passing in 'null' as the first argument timber uses the global / main WP_Query */
$items = new PostQuery( null, CustomPost::class );
/* This examples is a custom query (not the global / main query ) */
$args = [
'post-type' => 'my-custom-post-type',
// etc etc
];
$items = new PostQuery( $args, CustomPost::class );
/* Each Post in $items will be the class CustomPost instead of Timber\Post */
If your custom post classes correspond with a particular post type, you can use the Timber Class Map to always get back the corresponding Custom Post Class..
<?php
/* functions.php or similar */
add_filter( 'Timber\PostClassMap', 'my_func_modify_class_map' );
function( $classMap ){
$classMap['my-custom-post-type'] = CustomPost::class;
return $classMap;
}
/* index.php or xxx.php template file... */
$args = [
'post-type' => 'my-custom-post-type',
// etc etc
];
/* No second argument needed */
$items = new PostQuery( $args );
/* Each Post in $items will be the class CustomPost instead of Timber\Post */
Hope this helps!
Related
Is posible to save SS template variable in database from CMS and after execute it in template?
Okay lets see example:
In CMS i have settings where i put social media links and contact informatios.
Also in CMS i have module where i create HTML block-s which after that i loop in website.
In that html block i want to put existing $SiteConfig.Email variable.
I Try that but that is rendered in template like $SiteConfig.Email not show real email?
Is this posible to do or i need some extra modification?
Check photo
The question you have written makes no sense to me, but I understand the screenshot.
So, SilverStripe renders .ss files with a class called SSViewer. Basically it reads the file as string and then runs it through SSViewer to generate the HTML output.
But, as you saw, the output of variables is not processed.
I can think of 3 ways to get what you want:
Run the variables through SSViewer aswell (in this example, use $RenderedHTMLContent in the template)
class MyDataObject extends DataObject {
private static array $db = [
'Title' => DBVarchar::class,
'HTMLContent' => DBText::class,
];
public function Foobar() { return "hello from foobar"; }
public function RenderedHTMLContent() {
$template = \SilverStripe\View\SSViewer::fromString($this->HTMLContent);
// using $this->renderWith() will allow you access to all things of $this in the template. so eg $ID, $Title or $Foobar. Probably also $SiteConfig because it's global
return $this->renderWith($template);
// if you want to add extra variables that are not part of $this, you can also do:
return $this->renderWith($template, ["ExtraVariable" => "Hello from extra variable"]);
// if you do not want $this, you can do:
return (new ArrayData(["MyVariable" => "my value"]))->renderWith($template);
}
}
Please be aware of the security implications this thing brings. SilverStripe is purposely built to not allow content authors to write template files. A content author can not only call the currently scoped object but also all global template variables. This includes $SiteConfig, $List, .... Therefore a "bad" content author can write a template like <% loop $List('SilverStripe\Security\Member') %>$ID $FirstName $LastName $Email $Salt $Password<% end_loop %> or perhaps might access methods that have file access. So only do this if you trust your content authors
Use shortcodes instead of variables. But I never liked shortcodes, so I don't remember how they work. You'll have to lookup the docs for that.
Build your own mini template system with str_replace.
class MyDataObject extends DataObject {
private static array $db = [
'Title' => DBVarchar::class,
'HTMLContent' => DBText::class,
];
public function Foobar() { return "hello from foobar"; }
public function RenderedHTMLContent() {
return str_replace(
[
'$SiteConfig.Title',
'$SiteConfig.Tagline',
'$Title',
'$Foobar',
],
[
SiteConfig::current_site_config()->Title,
SiteConfig::current_site_config()->Tagline,
$this->Title,
$this->Foobar(),
],
$this->HTMLContent
);
}
}
How to change the node preview title to be displayed instead of just "Preview"?
This is a way to solve your problem in your custom module
/**
* implements hook_form_BASE_FORM_ID_alter
* the form id that will build the node preview is page_node_form
* #param $form
* #param $form_state
*/
function yourmodulename_form_page_node_form_alter( $form, $form_state ){
if( !empty( $form_state['node']->in_preview ) ){
// security hint: do not pass the PASS_THROUGH param to the drupal_set_title
// because the node title may contain some xss. Without this parameter the
// drupal_set_title will check for xss and remove them if present
drupal_set_title(t('Preview') . ' ' . $form['#node']->title );
}
}
Here how to hack core to modify the title (quick and easy way) but better would be to override via a custom module (perhaps somebody else can post please).
In /modules/node/node.pages.inc add $node->title within drupal_set_title(t('Preview'), PASS_THROUGH);
like so:
// Display a preview of the node.
if (!form_get_errors()) {
$cloned_node->in_preview = TRUE;
$output = theme('node_preview', array('node' => $cloned_node));
unset($cloned_node->in_preview);
}
drupal_set_title(t('Preview: '.$node->title), PASS_THROUGH);
I would like to create a WordPress plugin/widget that I can add to my post either through the Visual Composer area (widget should be selectable) or simply as a shortcode.
The functionality of this widget is that it should allow me to take the settings of that post as an argument in the main function so that I can change the output of the widget based on what is selected for the post.
For example, I have categories for all my posts and if I have a certain category selected, I want to change the output of the widget based on that.
Does anyone know of a good boilerplate to get me started with this?
Thanks
You can create your own widget using the Widget API or the Shortcode API for shortcodes.
Since you want to change the content shown in your, for instance, widget, based on the current post, in the widget() method (which is the method that prints your widget's content in the frontend) you can add your conditionals or whatever you want to print there.
The $post is available in your widget, so you can use functions like get_post_meta() to retrieve the post's settings if you use custom fields, or any other function like get_the_ID() or has_category().
For example:
/**
* Outputs the content of the widget
*
* #param array $args
* #param array $instance
*/
public function widget( $args, $instance ) {
if ( has_category( 'books' ) ) {
echo 'Hey!, I have the Books category!';
} else {
echo "Hey... I don't.";
}
}
Is there an option or way to provide data to all instances or rendered pages when using the Timber library?
I would like to set some site wide data within the core functions.php file and have it be available to all templates without the need to manually add it before every Timber::render()
I’d use the timber_context filter (or timber\context) to add your own data when you use get_context.
Here’s an example for how to add a menu/navigation (from the Wiki page on TimberMenu):
add_filter( 'timber_context', function( $context ) {
/* So here you are adding data to Timber's context object, i.e... */
$context['foo'] = 'I am some other typical value set in your functions.php file, unrelated to the menu';
/* Now, in similar fashion, you add a Timber menu and send it along to the context. */
$context['menu'] = new Timber\Menu(); // This is where you can also send a WordPress menu slug or ID
return $context;
} );
The minimum you have to do to get your data into your template will then be:
$context = Timber::get_context();
Timber::render( 'template.twig', $context );
I put the widget category on the primary sidebar, from the dashboard. Than, on code, I use :
<?php get_sidebar('left'); ?>
and it create the code for the categories. Now, I'd like to hide the category with tag_ID=6, and all of its subcategories.
How can I do it?
Tried this tutorial, but seems that I don't have the $cat_args = "orderby=name&show_count={$c}&hierarchical={$h}"; line? I'm on last version of WordPress, 3.4.2
The tutorial seems outdated, so I wouldn't rely on that. It is not necessary to hack around in the WordPress-source - create a simple Plugin which hooks into the right filters.
In your case those filters are widget_categories_dropdown_args (when you select "Display as dropdown" in the Widget-options) and widget_categories_args (if the Widgets displays the list as normal text with links).
With that knowledge you can now code the actual plugin (I've called it Myplugin, I think you should rename it) - just put that PHP code into the file wp-content/plugins/myplugin.php:
<?php
/**
* #package Myplugin
* #version 1.0
*/
/*
Plugin Name: Myplugin
Plugin URI: http://example.com
Description:
Author: You
Version: 1.0
Author URI: http://example.com
*/
// Create a list with the ID's of all children for
// the given category-id
function myplugin_recursive_filter($catid) {
$result = array($catid);
$cats = get_categories(array(
'child_of' => $catid,
));
foreach($cats as $category) {
$result[] = $category->cat_ID;
}
return implode(",", $result);
}
// Actual filter function. Just set the "exclude"
// entry to a comma separated list of category ID's
// to hide.
function myplugin_filter_categories_args($args) {
// 6 is the "tag_ID"
$args['exclude'] = myplugin_recursive_filter(6);
// or hard code the list like that:
//$args['exclude'] = '6,10,11,12';
// but you'd have to include the ID's of the
// children, because "eclude" is not recursive.
return $args;
}
// Register the filter to the relevant tags
add_filter('widget_categories_dropdown_args',
'myplugin_filter_categories_args', 10, 1);
add_filter('widget_categories_args',
'myplugin_filter_categories_args', 10, 1);
The function myplugin_recursive_filter is necessary, because the exclude-entry is not recursive (except if you check "Show hierarchy" in the widget options). If your categories don't change that much you could replace function call with a hard-coded list of ID's (with the children) for better performance.