I am using the Silverstripe comments module by Willr along with an implementation of DataObjects as Pages.
The comments module allows you to attach comments to Data Objects - which I have done. The problem I am having is that when I attempt to pass through customfields from the Datobject to a template using renderwith, the CommentsForm that gets passed renders the form, but does not associate any comments made through the passed form with the DataObject.
Here is my action and renderWith method on my PostsPageHolder:
public function view($request) {
$segment = $request->param('ID');
if ($obj = Post::get()->filter('URLSegment', $segment)->First()) :
switch ($obj->Type) {
case 'News-Post' :
return $this->renderWith(
array('PostsPage_view_news', 'Page'),
array(
'Object' => $obj,
'Type' => $obj->Type,
'Title' => $obj->Title,
'Entry' => $obj->Entry,
'CommentsForm' => $obj->CommentsForm
)
);
break;
...
}
Does anyone know how I can pass through the form using the RenderWith() array?
try customise(array) as shown here https://docs.silverstripe.org/en/3/tutorials/site_search/#showing-the-results
return $this->customise(array(
'Object' => $obj,
'Type' => $obj->Type,
'Title' => $obj->Title,
'Entry' => $obj->Entry,
'CommentsForm' => $obj->CommentsForm
))->renderWith(
array('PostsPage_view_news', 'Page')
);
Related
I have created a custom endpoint, that basically just grabs a few different posts from each category and returns it. This endpoint works fine, but the schema of each post being returned is not the same as when you just hit the default, built-in /posts endpoint. What do I have to do to keep the schemas consistent?
I have a feeling get_posts is the problem, but I have been doc crawling, and I cant seem to find anything that uses the same schema as /posts does.
// How the endpoint is built.
function anon_content_api_posts($category) {
$posts = get_posts(
array(
'posts_per_page' => 3,
'tax_query' => array(
array(
'taxonomy' => 'content_category',
'field' => 'term_id',
'terms' => $category->term_id,
)
)
)
);
$posts = array_map('get_extra_post_data', $posts); // just me appending more data to each post.
return $posts;
}
function anon_content_api_resources() {
$data = array();
$categories = get_categories(
array(
'taxonomy' => 'content_category',
)
);
foreach($categories as $category) {
$category->posts = anon_content_api_posts($category);
array_push($data, $category);
}
return $data;
}
Custom endpoint schema
ID:
author:
comment_count:
comment_status:
featured_image_url:
filter:
guid:
menu_order:
ping_status:
pinged:
post_author:
post_content:
post_content_filtered:
post_date:
post_date_gmt:
post_excerpt:
post_mime_type:
post_modified:
post_modified_gmt:
post_name:
post_parent:
post_password:
post_status:
post_title:
post_type:
to_ping:
Default /posts schema
_links:
author:
categories:
comment_status:
content:
date:
date_gmt:
excerpt:
featured_image_url:
featured_media:
format:
guid:
id:
link:
meta:
modified:
modified_gmt:
ping_status:
slug:
status:
sticky:
task_category:
template:
title:
type:
Any help would be appreciated!
Although this question is older, I had a difficult time finding the answer to getting the schema myself, so I wanted to share what I found.
Short Answer (to getting the schema information back): Use OPTIONS method on the route request
You are dealing with an endpoint that already exists /wp/v2/posts, so you probably want to modify the response of the existing route which you can do with register_rest_field() (this should keep the appropriate schema for all exposed post columns / fields, but allows you to modify the schema for the fields you are now exposing as well):
Something like this:
function demo_plugin_extend_route_rest_api()
{
register_rest_field(
'post',
'unexposed_column_in_wp_posts_table',
array(
'get_callback' => function( $post_arr ) {
$post_obj = get_post( $post_arr['id'] );
return $post_obj->unexposed_column_in_wp_posts_table;
},
'update_callback' => function( $unexposed_column_in_wp_posts_table, $post_obj ) {
$ret = wp_update_post(
array(
// ID is the name of the column in the posts table
// unexposed_column_in_wp_posts_table should be replaced throughout with the unexposed column in the posts table
'ID' => $post_obj->ID,
'unexposed_column_in_wp_posts_table' => $unexposed_column_in_wp_posts_table
)
);
if ( false === $ret )
{
return new WP_Error(
'rest_post_unexposed_column_in_wp_posts_table_failed',
__( 'Failed to update unexposed_column_in_wp_posts_table.' ),
array( 'status' => 500 )
);
}
return true;
},
'schema' => array(
'description' => __( 'Post unexposed_column_in_wp_posts_table' ),
'type' => 'integer'
),
)
);
}
// Not-in-class call (use only this add_action or the one below, but not both)
add_action( 'rest_api_init', 'demo_plugin_extend_route_rest_api' );
// In-class call
add_action( 'rest_api_init', array($this, 'demo_plugin_extend_route_rest_api') );
If what you are really wanting is to create a new route and endpoint with custom tables (or another endpoint to the posts table), something like this should work:
function demo_plugin_custom_rest_api()
{
// Adding Custom Endpoints (add tables and fields not currently exposed)
// register_rest_route()
// $namespace (string) (Required) The first URL segment after core prefix.
// Should be unique to your package/plugin.
// $route (string) (Required) The base URL for route you are adding.
// $args (array) (Optional) Either an array of options for the endpoint, or an
// array of arrays for multiple methods. Default value: array()
// array: If using schema element to define the schema, or multiple methods,
// then wrap the 'methods', 'args', and 'permission_callback' in an array,
// otherwise they do not need to be wrapped in an array. Best practice
// would be to wrap them in an array though
// 'methods' (array | string): GET, POST, DELETE, PUT, PATCH, etc.
// 'args' (array)
// '<schema property name>' (array) (ie. parameter name - if not including a schema
// then can include field names as valid parameters as a way to
// describe them):
// 'default': Used as the default value for the argument, if none is supplied
// Note: (if defined, then it will validate and sanitize regardless of if
// the parameter is passed in query).
// 'required': If defined as true, and no value is passed for that argument, an
// error will be returned. No effect if a default value is set, as the argument
// will always have a value.
// 'description': Field Description
// 'type': Data Type
// 'validate_callback' (function): Used to pass a function that will be passed the
// value of the argument. That function should return true if the value is valid,
// and false if not.
// 'sanitize_callback' (function): Used to pass a function that is used to sanitize
// the value of the argument before passing it to the main callback.
// 'permission_callback' (function): Checks if the user can perform the
// action (reading, updating, etc) before the real callback is called
// 'schema' (callback function) (optional): Defines the schema.
// NOTE: Can view this schema information by making OPTIONS method request.
// $override (bool) (Optional) If the route already exists, should we override it? True overrides,
// false merges (with newer overriding if duplicate keys exist). Default value: false
//
// View your describe page at: /wp-json/demo-plugin/v1
// View your JSON data at: /wp-json/demo-plugin/v1/demo-plugin_options
// View your schema at (with OPTIONS method) at: /wp-json/demo-plugin/v1/demo-plugin_options
// Note: For a browser method to see OPTIONS Request in Firefox:
// Inspect the JSON data endpoint (goto endpoint and click F12)
// > goto Network
// > find a GET request
// > click it
// > goto headers section
// > click Edit and Resend
// > change Method to OPTIONS
// > click Send
// > double click on last OPTIONS request
// > goto Response (the JSON data returned shows your schema)
register_rest_route(
'demo-plugin/v1',
'/demo-plugin_options/',
array(
// GET array options
array(
'methods' => array('GET'),
'callback' => function ( WP_REST_Request $request ){
// Get Data (here we are getting from options, but could be any data retrieval)
$options_data = get_option('demo_option_name');
// Set $param
$param = $request->get_params();
// Do Other things based upon Params
return $options_data;
},
'args' => array(
// Valid Parameters
'element_1' => array(
'description'=> 'Element text field',
'type'=> 'string',
),
'element_color' => array(
'description'=> 'Element color select box',
'type'=> 'string',
)
)
),
// POST array options
array(
'methods' => array('POST'),
'callback' => function ( WP_REST_Request $request ){
// Get Data (here we are getting from options, but could be any data retrieval)
$options_data = get_option('demo_option_name');
// Set $param
$param = $request->get_params();
// Do Other things based upon Params
if (is_array($param) && isset($param))
{
foreach ($param as $k=>$v)
{
// $param is in an array($key => array($key => $value), ...)
if (is_array($v) && array_key_exists($k, $options_data) && array_key_exists($k, $v))
{
$options_data[$k] = $v;
}
}
}
update_option('demo_option_name', $options_data);
return $options_data;
},
'args' => array(
'element_1' => array(
'default' => '',
'required' => false,
'description'=> 'Element text field',
'type'=> 'string',
'validate_callback' => function($param, $request, $key) { //validation function },
'sanitize_callback' => function($param, $request, $key) { //sanitization function }
),
'element_color' => array(
'default' => 'red',
'required' => true,
'description'=> 'Element color select box',
'type'=> 'integer',
'validate_callback' => function($param, $request, $key) {
$colors = array('red', 'blue');
return in_array($param, $colors);
},
'sanitize_callback' => function($param, $request, $key) {
// If it includes a default, and sanitize callback for other properties above are set, it seems to need it here as well
return true;
})
),
'permission_callback' => function () {
// See Capabilities here: https://wordpress.org/support/article/roles-and-capabilities/
$approved = current_user_can( 'activate_plugins' );
return $approved;
}
),
'schema' => function() {
$schema = array(
// This tells the spec of JSON Schema we are using which is draft 4.
'$schema' => 'http://json-schema.org/draft-04/schema#',
// The title property marks the identity of the resource.
'title' => 'demo-plugin_options',
'type' => 'object',
// In JSON Schema you can specify object properties in the properties attribute.
'properties' => array(
'element_1' => array(
'description' => esc_html__( 'Element text field', 'demo-plugin' ),
'type' => 'string',
'context' => array( 'view', 'edit', 'embed' ),
'readonly' => false,
),
'element_color' => array(
'description' => esc_html__( 'Element color select box', 'demo-plugin' ),
'type' => 'string',
'readonly' => false,
),
),
);
return $schema;
})
);
}
// Not-in-class call (use only this add_action or the one below, but not both)
add_action( 'rest_api_init', 'demo_plugin_custom_rest_api' );
// In-class call
add_action( 'rest_api_init', array($this, 'demo_plugin_custom_rest_api') );
Of course, this is just a basic outline. Here is a caveat:
According to the documentation listed below, it is best to use a Controller Pattern class extension method (rather than the method I outlined above): https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-custom-endpoints/#the-controller-pattern
These were very helpful links in finally putting this all together for myself:
REST API Handbook: https://developer.wordpress.org/rest-api/
See Sub-Pages:
REST API Modifying Responses,
REST API Adding Custom Endpoints,
REST API Schema
Register REST Field Function: https://developer.wordpress.org/reference/functions/register_rest_field/
Register REST Route Function: https://developer.wordpress.org/reference/functions/register_rest_route/
I mean to have custom date handler in views ,according to documentation of views i should implement hook_views_api and hook_views_data .
my pseudo
function mymodule_views_api() {
return array(
'api' => views_api_version(),
);
}
and in hook_views_data()
function mymodule_views_data() {
$data = array();
$data['node']['created'] = array(
'group' => t('Mul2'),
'title' => t('Post date'), // The item it appears as on the UI,
'help' => t('The date the content was posted.'), // The help that appears on the UI,
'field' => array(
'handler' => 'views_handler_field_date',
'click sortable' => TRUE,
),
'sort' => array(
'handler' => 'views_handler_sort_date',
),
'filter' => array(
'handler' => 'views_handler_filter_date',
),
);
return $data;
}
It's ok and create a views field gorup (Mul2),
I set it hanlder date handler for test, but it not work correctly and just show Mul2: Array , and broken/missing handler in configuration of it.
I try successfully get data of custom table with views data .Is it correct to set handler for a field before handlered (like created in node ) ?
any solution?any idea?
Method of Impelementation is correct, my mistake was in use a field that before set handler for it $data['node']['created'] .
if you want to change default handler of handled field you have use hook_views_data_alter(&$data) instead of try to set handler for it again!!!(Mul2:array because of you try to set handler for handled field).
I've constructed a custom module to create a form. Now I'm stuck on the theming. I already have a CSS stylesheet for the form, since my company is part of the government and they have a preset branding. So I wanted to change the HTML used by the default form theme functions of Drupal thus implementing the correct style.
But only the form-tag of the form gets rendered. The fieldset and elements are not rendered. When the theme functions are removed the default theming kicks in and the form renders normally (but of course without the requested theming).
What I have tried so far:
Added a hook_theme function to add theme functions
function publicatieaanvraagformulier_theme() {
return array(
'publicatieaanvraagformulier_form' => array(
'arguments' => array("element" => NULL)
),
'publicatieaanvraagformulier_fieldset' => array(
'arguments' => array("element" => NULL)
),
'publicatieaanvraagformulier_form_element' => array(
'arguments' => array(
"element" => NULL,
"value" => NULL
)
)
);
}
Added ['#theme'] to the form-element, fieldset-element and the form-elements
$form['#theme'] = "publicatieaanvraagformulier_form";
$form['groep'] = array(
'#title' => t("Please fill in your details"),
'#type' => "fieldset",
'#theme' => "publicatieaanvraagformulier_fieldset"
);
$form['groep']['organisatie'] = array(
'#title' => t("Organization"),
'#type' => "textfield",
'#attributes' => array("class" => "text"),
'#theme' => "publicatieaanvraagformulier_form_element"
);
Added the actual theme function based on the default ones in form.inc
function theme_publicatieaanvraagformulier_form($element) {
function theme_publicatieaanvraagformulier_fieldset($element)
function theme_publicatieaanvraagformulier_form_element($element, $value)
I haven't included the code of these functions because even with the default themefunctions code, they don't work. Therefor I assume they are not the source of the problem.
The form is called
//Get the form
$form = drupal_get_form('publicatieaanvraagformulier');
//Add messages
$errors = form_get_errors();
if (!empty($errors)) {
$output .= theme("status_messages","error");
}
//Show form
$output .= $form;
return $output;
I haven't found similar 'complicated' examples of theming a form, but have pieced together the former from books and online searches.
Hopefully someone has an answer to this problem (point out the mistake I made).
Greetings
Jeroen
What I am trying to do is generate some raw output within a module.
I would like to pass an array of data through to a template file, and then use that data to populate the code from the template. The template is represented by a file in my theme folder.
I have a hook set up for a certain URL (/itunes):
$items['itunes'] = array(
'page callback' => 'itunespromo_buildpage',
'type' => MENU_SUGGESTED_ITEM,
'access arguments' => array('access content'),
);
..inside itunespromo_buildpage...
function itunespromo_buildpage() {
//grab some data to pass through to template file, put into $promo_data
$details = theme('itunes_page', array(
'promo_data' => $promo_data,
));
return $details;
}
Here is the hook_theme():
function itunespromo_theme() {
return array(
'itunes_page' => array(
'template' => 'itunes_page',
),
);
}
Inside my theme's template.php:
function geddystyle_itunes_page($vars) {
return print_r($vars['promo_data'], true);
}
Right now, $promo_data is being passed through fine, and it is print_r'd on to the result page. However, I'd like to then take this $promo_data variable and use it in my itunes_page.tpl.php template file.
I'm kind of certain I'm close here. Am I supposed to call some sort of render function and pass the $promo_data variable to it from function itunespromo_theme()?
I believe you just need to update your hook_theme() to provide the ability to send variables to your template file.
Something like this should do the trick:
function itunespromo_theme($existing, $type, $theme, $path) {
return array(
'itunes_page' => array(
'variables' => array(
'promo_data' => NULL,
),
'template' => 'itunes_page',
)
);
}
Also, instead of calling the theme() function directly what you want to be doing is actually constructing a renderable array and letting Drupal call the theme() function. What you should be doing is calling drupal_render which in turn calls theme() for you. Look at this piece of advice here for a little more clarity:
http://drupal.org/node/1351674#comment-5288046
In your case you would change your function itunespromo_buildpage to look something like this:
function itunespromo_buildpage() {
//grab some data to pass through to template file, put into $promo_data
$output = array(
'#theme' => 'itunes_page',
'#promo_data' => $promo_data //call $promo_data from the tpl.php page to access the variable
);
$details = drupal_render($output);
return $details;
}
I have a module with four node types declared. My problem is, hook_load, hook_view is never called. I used drupal_set_message to find out if certain hook is being called. And I found out hook_load, hook_view isn't. Just to give you clear picture, here's my structure of hook_load
HERE'S UPDATED ONE
function mymodule_node_info(){
return array(
'nodetype1' => array(
'name' => t('nodetype1'),
'module' => 'mymodule_nodetype1',
'description' => t('....'),
'has_title' => TRUE,
'title_label' => t('Title'),
'has_body' => TRUE,
'body_label' => t('Body'),
),
'nodetype2' => array(
......
'module' => 'mymodule_nodetype2',
......
),
'nodetype3' => array(
......
'module' => 'mymodule_nodetype3',
......
),
'nodetype4' => array(
......
'module' => 'mymodule_nodetype4',
.......
),
);
}
function mymodule_nodetype1_load($node){
$result = db_query('SELECT * from {nodetype1table} WHERE vid = %d'
$node->vid
);
drupal_set_message("hook_load is provoked.","status");
return db_fetch_object($result);
}
I don't know why it is not called. I wrote this code base on drupal module writing book and follow the instructions. I've tried sample code from that book and it works ok. Only my code isn't working. Probably because of multiple node types in one module. Any help would be highly appreciated.
Your code doesn't work because hook_load() and hook_view() aren't module hooks: they're node hooks. The invocation is based off of content type names, not module names.
So, first you need to have declared your content types using hook_node_info():
function mymodule_node_info() {
$items = array();
$items['nodetype1'] = array(
'name' => t('Node Type 2'),
'module' => 'mymodule_nodetype1',
'description' => t("Nodetype 1 description"),
);
$items['nodetype2'] = array(
'name' => t('Node Type 2'),
'module' => 'mymodule_nodetype2',
'description' => t("Nodetype 2 description"),
);
$items['nodetype3'] = array(
'name' => t('Node Type 2'),
'module' => 'mymodule_nodetype3',
'description' => t("Nodetype 3 description"),
);
return $items;
}
Then, you need to use the name of the module you specified for each content type declared in hook_node_info() for your node hooks. That is, mymodule_nodetype1_load(), mymodule_nodetype2_view(), etc.
Edit
If you're trying to have a non-node based module fire when a node is viewed or loaded, you need to use hook_nodeapi():
function mymodule_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
switch ($op) {
case 'view':
mymodule_view_function($node);
break;
case 'load':
mymodule_load_function($node);
break;
}
}
Replace mymodule_load_function() and mymodule_load_function() with your own custom functions that are designed to act on the $node object.
Edit 2
Besides the syntax error in your hook_load() implementations, there's a piece of your code outside of what you're providing that's preventing the correct invocation. The following code works (if you create a nodetype1 node, the message "mymodule_nodetype1_load invoked" appears on the node): perhaps you can compare your entire code to see what you're missing.
function mymodule_node_info() {
return array(
'mymodule_nodetype1' => array(
'name' => t('nodetype1'),
'module' => 'mymodule_nodetype1',
'description' => t('....'),
'has_title' => TRUE,
'title_label' => t('Title'),
'has_body' => TRUE,
'body_label' => t('Body'),
),
'mymodule_nodetype2' => array(
'name' => t('nodetype2'),
'module' => 'mymodule_nodetype2',
'description' => t('....'),
'has_title' => TRUE,
'title_label' => t('Title'),
'has_body' => TRUE,
'body_label' => t('Body'),
),
);
}
function mymodule_nodetype1_form(&$node, $form_state) {
// nodetype1 form elements go here
return $form;
}
function mymodule_nodetype2_form(&$node, $form_state) {
// nodetype2 form elements go here
return $form;
}
function mymodule_nodetype1_load($node) {
$additions = new stdClass();
drupal_set_message('mymodule_nodetype1_load invoked');
return $additions;
}
function mymodule_nodetype2_load($node) {
$additions = new stdClass();
drupal_set_message('mymodule_nodetype2_load invoked');
return $additions;
}
If you're not reseting your environment after changes to your module, you might be running into caching issues. You should test your code in a sandbox environment that can be reset to a clean Drupal installation to ensure you're not focusing on old cruft from previous, incorrect node implementations.
Additionally, you should only be using hook_nodeapi() if you are trying to act on content types that are not defined by your module. Your content types should be using the node hooks (hook_load(), hook_view(), etc.).
Finally, it may be the case that you're using the wrong hooks because you're expecting them to fire in places they are not designed to. If you've gone through everything above, please update your post with the functionality you're expecting to achieve and where you expect the hook to fire.
I found the culprit why your code doesn't work. It's because I was using the test data created by the old codes. In my old codes, because of node declaration inside hook_node_info uses the same module value, I could only create one hook_form implementation and use "switch" statement to return appropriate form. Just to give you clear picture of my old codes-
function mymodule_node_info(){
return array(
'nodetype1' => array(
.....
'module' => 'mymodule',
.....
),
'nodetype2' => array(
......
'module' => 'mymodule',
......
),
.......
);
}
function mymodule_form(&$node, $form_state){
switch($node->type){
case 'nodetype1':
return nodetype1_form();
break;
case 'nodetype2':
return nodetype2_form();
break;
.....
}
}
When I created new data after I made those changes you have provided, hook_load is called. It works! I've tested several times(testing with old data created by previous code and testing with new data created after those changes) to make sure if that's the root cause and, I got the same result.I think drupal store form_id or module entry value of node declaration along with data and determine the hook_load call. That's probably the reason why it doesn't think it's a data of this node and thus hook_load isn't invoked.
And Thank you so much for your help.