Detecting XSS risk in a plugin - wordpress

I am working on a plugin. Unfortunately it is no longer updated. The function below is marked as "risk". Because it is thought to cause XSS.
Sanitization (the process of cleaning or filtering your input data.) has been performed in the relevant function. The data_id value is generated by another function and the Wordpress function is used for this at the last stage. Necessary actions were taken in the related Wordpress function.
What else could be causing XSS?
//...
private function __construct() {
//Add plugin to new content admin bar menu
add_action('wp_ajax_data_new_data_area', array($this, 'ajax_add_data'));
}
public function ajax_add_data{
if (!current_user_can('administrator'))
wp_die('-');
$data_title = sanitize_text_field(wp_unslash($_POST['title']));
$data_type = sanitize_text_field(wp_unslash($_POST['data_type']));
$data_type = empty($data_type) ? "reg" : $data_type;
$data_id = $this->add_new_area($data_title, $data_type);
if ($data_id) {
$response = array(
'id' => $data_id,
'redirect' => add_query_arg(
array(
'tab' => 'build',
'data_id' => $data_id,
'newdata' => '1',
), admin_url('admin.php?page=plugin-dashboard')
),
);
wp_send_json_success($response);
} else {
die(__('Error', 'plugin'));
}
}

Related

Adding elements to a field widget in Drupal 8

What I need to accomplish is adding a link to a node to field widgets (the node contains the formatted instructions to properly compile the field).
So far I've been able to add the node reference field in the field config form, using Third Party Settings:
function mymodule_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
if ($form_id === 'field_config_edit_form' && $form_state->getFormObject()->getEntity()->get('entity_type') == 'myentity') {
$field = $form_state->getFormObject()->getEntity();
$help_page_id = $field->getThirdPartySetting('mymodule', 'help_page_id');
$form['help_page_id'] = array(
'#type' => 'entity_autocomplete',
'#title' => t('Help page'),
'#target_type' => 'node',
'#selection_handler' => 'default',
'#selection_settings' => array(
'target_bundles' => array('help_page'),
),
'#default_value' => $help_page_id ? Node::load($help_page_id) : NULL,
'#weight' => 100,
);
$form['#entity_builders'][] = 'mymodule_form_field_config_edit_form_builder';
}
}
Now I'm having troubles retrieving this information when showing the form, and altering the field widget in order to display the link.
Using hook_field_widget_form_alter, I cannot get the custom configuration value from the arguments I have:
function mymodule_field_widget_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
}
I guess I have to load the field configuration entity, but I don't know how.
I didn't test it, but something like this should work:
function mymodule_field_widget_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
// Add a suffix to widget form elements for all fields of type entity_reference.
$field_definition = $context['items']->getFieldDefinition();
if ($field_definition->getType() == 'entity_reference') {
// TODO: Render the node link to $link
$element['#suffix'] = $link;
}
}

wordpress rest api v2 how to list taxonomy terms?

i am new to v2, i use v1 for long time, currently upgrade to v2, i try to get all the terms belong to specific custom taxonomy.
In v1 i can do this to get terms
/taxonomies/location_category/terms
but in v2 i try
/taxonomies/terms
it return json error "code":"rest_no_route","message":"No route was found matching the URL and request method","data":{"status"
:404}}
if just /taxonomies/location_category/ it didn't show anything terms belong to taxonomy.
i search the question on google for few hours didn't show any result, anyone can help please, thank you
end out to write the custom code here
add blow code to functions.php
class all_terms
{
public function __construct()
{
$version = '2';
$namespace = 'wp/v' . $version;
$base = 'all-terms';
register_rest_route($namespace, '/' . $base, array(
'methods' => 'GET',
'callback' => array($this, 'get_all_terms'),
));
}
public function get_all_terms($object)
{
$return = array();
// $return['categories'] = get_terms('category');
// $return['tags'] = get_terms('post_tag');
// Get taxonomies
$args = array(
'public' => true,
'_builtin' => false
);
$output = 'names'; // or objects
$operator = 'and'; // 'and' or 'or'
$taxonomies = get_taxonomies($args, $output, $operator);
foreach ($taxonomies as $key => $taxonomy_name) {
if($taxonomy_name = $_GET['term']){
$return = get_terms($taxonomy_name);
}
}
return new WP_REST_Response($return, 200);
}
}
add_action('rest_api_init', function () {
$all_terms = new all_terms;
});
and enter url http://youdomain.com/wp-json/wp/v2/all-terms?term=you_taxonomy
so term = you_taxonomy, will get terms belong to job_category.
Taxonomy terms are simply called this way:
https://yoursite.com/wp-json/wp/v2/the-taxonomy-slug
For instance, to answer your question:
https://yoursite.com/wp-json/wp/v2/location_category
From terminal:
curl -X GET -i http://www.example.com/wp-json/wp/v2/location_category
For custom taxonomies, be sure that you're setting the 'show_in_rest' argument to be true (default is false) in your register_taxonomy() call.
The register_taxonomy() call is also where you can set the 'rest_base' argument (default will be the taxonomy name, /location_category/ in your example).
TLDR
Use the flag show_in_rest when you register the taxonomy. That's all.
Details
List all available taxonomies
All default taxonomies are available via REST API. Use the following endpoint to get a list of all available taxonomies:
https://exmaple.org/wp-json/wp/v2/taxonomies
It will tell you the correct endpoint for each taxonomy in the wp:items tag, e.g.
..., "wp:items":[{"href":"https://example.com/wp-json/wp/v2/categories"}], ...
..., "wp:items":[{"href":"https://example.com/wp-json/wp/v2/tags"}], ...
Adding new taxonomies to the REST endpoint
In case your taxonomy is not listed in this taxonomy overview, you need to enable the REST endpoint when calling register_taxonomy. You can do this by adding the argument 'show_in_rest' => true:
<php
register_taxonomy( 'location_category', 'post', [
// ...
'show_in_rest' => true, // ← make sure you have this line in the taxonomy args!
] );
Note: If you do NOT use show_in_rest, then the taxonomy is not available in the Block Editor ("Gutenberg").
I strongly advise against creating custom REST endpoints to duplicate built-in logic.
I.e. don't do this or this
Seems some confusion for some devs even for me.
Correct URL is
https://example.com/wp-json/wp/v2/{your_taxonomy}
Not
https://example.com/wp-json/wp/v2/taxonomies/{your_taxonomy}
"/taxonomies" not required
The accepted answer mostly worked for me. This is what I got
<?php
// your_theme/functions.php
/**
* how to list all taxonomy terms
*/
class all_terms
{
public function __construct()
{
$version = '2';
$namespace = 'wp/v' . $version;
$base = 'all-terms';
register_rest_route($namespace, '/' . $base, array(
'methods' => 'GET',
'callback' => array($this, 'get_all_terms'),
));
}
public function get_all_terms($object)
{
$args = array(
'public' => true,
'_builtin' => false
);
$output = 'names'; // or objects
$operator = 'and'; // 'and' or 'or'
$taxonomies = get_taxonomies($args, $output, $operator);
foreach ($taxonomies as $key => $taxonomy_name) {
if ($taxonomy_name = $_GET['term']) {
$return[] = get_terms(array(
'taxonomy' => $taxonomy_name,
'hide_empty' => false,
));
}
}
return new WP_REST_Response($return, 200);
}
}
add_action( 'rest_api_init', get_all_terms);
?>
matches the docs more closely https://developer.wordpress.org/reference/functions/get_terms/
If anyone is reading this in the future, I ran into an issue where the default WP category was outputting a parent key of 0, 1, 2 etc for each term object, which is a problem in itself, but a bigger problem when custom taxonomies do not have this parent value on objects
To solve this amend the ticked example with this:
foreach ($taxonomies as $key => $taxonomy_name) {
if($taxonomy_name = $_GET['term']){
$return = get_terms( array(
'taxonomy' => $taxonomy_name,
'hide_empty' => false,
));
}
}

Using Drupal's node form in a new page

In a custom module I want to have a page defined in hook_menu, that shows the add form for a specific content type, with some modifications to the form.
So far it's working, and even saving the new node, but only with the default values I'm setting in the code, i.e. it's not picking up anything the user types into the form. I checked and $form_state['input'] contains the inputted values, but $form_state['values'] doesn't, so the new node gets saved wrong.
Here's the relevant code:
function mymodule_menu() {
return array(
'admin/content/myadd/%' => array(
'title' => 'my custom add page',
'page callback' => 'mymodule_node_add',
'page arguments' => array(3),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
),
);
}
function mymodule_node_add() {
module_load_include('inc', 'node', 'node.pages');
//I'm doing a print here instead of returning because I'm calling this page
//in an AJAX popup, so I don't want the whole page to output, only the form.
print render(drupal_get_form('mymodule_node_add_form'));
}
function mymodule_node_add_form($form, &$form_state) {
if (!isset($form_state['node']) {
global $user;
$node = (object) array(
'uid' => $user->uid,
'type' => 'mycontenttype',
'language' => LANGUAGE_NONE,
);
//this is setting a default value
$node->myfield = array(LANGUAGE_NONE => array(array('value' => arg(3))));
$form_state['build_info']['args'] = array($node);
$form = drupal_build_form('mycontenttype_node_form', $form_state);
$form['actions']['submit']['#submit'][0] = 'mymodule_node_add_form_submit';
//there's a lot more customization of the form here, like adding fields, etc.
}
return $form;
}
function mymodule_node_add_form_submit($form, &$form_state) {
//here's where $form_state['input'] is correct but $form_state['values'] isn't.
$node = node_form_submit_build_node($form, $form_state);
node_save($node);
$form_state['values']['nid'] = $node->nid;
$form_state['nid'] = $node->nid;
$form_state['redirect'] = 'some/other/page';
}
So, am I doing something wrong here? Should I be concerned about form ids being wrong? (my form's id is mymodule_node_add_form, but the actual form might output mycontenttype_node_form), would this affect me?
You want hook_form_alter() (see api.drupal.org). I would try to use the existing content type's form and simply alter it with hook_form_alter(). I would also try to first get it working as a standard, non-AJAX page, so you can get all the advantages of dpm() and other debugging techniques. When you have it down solid, then modify it to take advantage of the AJAX techniques.
mymodule_form_alter(&$form, &$form_state, $form_id) {
// use this with your devel module turned on to verify
// your $form_id and contents of all forms that load on a given page
dpm($form);
// once you verify your $form_id, you can begin accessing your form and altering it
switch( $form_id ) {
case 'my_target_form_id' :
// this part is just pseudocode, I haven't memorized the $form structure,
// you can get it from your dpm().
if( $form['node']->type == 'my_target_content_type' ) {
$form['actions']['submit']['#submit'][0] = 'mymodule_node_add_form_submit';
}
break;
}
}

wordpress custom post types - broken publish button

I created a gallery post type (as part of a plugin) that contains besides a title and some meta information also a wp_list_table that queries those attachments which have the current post as post_parent. I ran into a problem when suddenly my publish button stopped working. No matter if I'm creating a new gallery or if I'm editing an old one, once I click on update/publish my changes get lost and I end up on edit.php.
Anybody knows what that's all about?
I where able to figure out where the problem seems to be. It's in my list_table class which inherits from wp_list_table. after commenting out some unimportant functions i ended up with a different error but still no new or updated gallery. Now I get the are you sure you want to do this page.
Even the most basic class won't work...
if( ! class_exists( 'WP_List_Table' ) ) {
require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}
class Yard_Attachment_Table extends WP_List_Table {
function __construct() {
global $status, $page;
parent::__construct( array(
'singular' => 'yard Attachment',
'plural' => 'yard Attachments',
'ajax' => false
) );
}
function column_default($item, $column_name) {
return 'default';
}
function get_columns(){
$columns = array(
'checkbox' => '<input type="checkbox" />', //for simplicity its not 'cb'
'thumb' => 'Thumbnail',
'title' => 'Titel',
'pos' => 'Position'
);
return $columns;
}
function prepare_items() {
global $wpdb;
$columns = $this->get_columns();
$hidden = array();
$sortable = $this->get_sortable_columns();
$this->_column_headers = array($columns, $hidden, $sortable);
if (isset($_REQUEST['post'])) {
$query = " SELECT *
FROM $wpdb->posts
WHERE post_type = 'attachment'
AND post_parent = {$_REQUEST['post']}";
$data = $wpdb->get_results($query, ARRAY_A);
} else {
$data = array();
}
$this->items = $data;
}
}
In the plugin class' constructor I use
add_action('add_meta_boxes_yard_gallery', array($this, 'yard_metaboxes'));.
In yard_metaboxes I use add_meta_box and in the function I have as a callback i'm creating a new instance of my table class and I call prepare_items() and display()
Turning error_reporting on my page dies with these messages:
Strict Standards: Only variables should be passed by reference in /Applications/MAMP/htdocs/Web/ChristophRokitta/wp v.1.0/wp-includes/pomo/mo.php on line 210
Warning: Cannot modify header information - headers already sent by (output started at /Applications/MAMP/htdocs/Web/ChristophRokitta/wp v.1.0/wp-includes/pomo/mo.php:210) in /Applications/MAMP/htdocs/Web/ChristophRokitta/wp v.1.0/wp-includes/pluggable.php on line 876
BTW I'm not localizing.
Please help! If i had more reputation I'd offer it.
Adding the meta box code
in my plugin file:
require_once( plugin_dir_path( __FILE__ ) . 'class-yard.php' );
Yard::get_instance();
in my class-yard file the meta box methods are at the bottom:
class Yard {
protected static $instance = null;
private function __construct() {
include_once('class-yard-attachments.php');
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_styles' ) );
add_action('after_setup_theme', array($this, 'yard_thumbnails'));
add_action('init', array($this, 'yard_post_type'));
add_action('init', array($this, 'yard_taxonomies'));
add_filter('manage_yard_gallery_posts_columns', array($this, 'yard_add_columns'));
add_action('manage_posts_custom_column', array($this, 'yard_fill_columns'));
add_action('add_meta_boxes_yard_gallery', array($this, 'yard_metaboxes'));
}
public static function get_instance() {// If the single instance hasn't been set, set it now.
if (null == self::$instance ) {
self::$instance = new self;
}
return self::$instance;
}
public function enqueue_admin_styles() {
$screen = get_current_screen();
if ($screen->post_type == 'yard_gallery') {
wp_register_style( 'yard-gallery-style', plugins_url('css/yard-gallery-style.css', __FILE__) );
wp_enqueue_style( 'yard-gallery-style' );
}
}
public function yard_thumbnails() {
//add_image_size('yard-thumbnail', 100, 100, true);
}
public function yard_post_type() {
$gallery_labels = array(
'name' => 'Galerien',
'singular_name' => 'Galerie',
'all_items' => 'Alle Galerien',
'add_new' => 'Erstellen',
'add_new_item' => 'Neue Galerie erstellen',
'edit_item' => 'Galerie bearbeiten',
'new_item' => 'Neue Galerie',
'view' => 'Galerie anzeigen',
'view_item' => 'Gallerie anzeigen',
'search_items' => 'Galerie durchsuchen',
'not_found' => 'Keine Galerien gefunden',
'not_found_in_trash' => 'Es befinden sich keine Galerien im Papierkorb',
'parent_item_colon' => ''
);
$gallery_args = array(
'labels' => $gallery_labels,
'public' => true,
// 'publicly_queryable' => true,
// 'show_ui' => true,
// 'show_in_menu' => true,
// 'query_var' => true,
'rewrite' => true,
// 'capability_type' => 'post',
// 'hierarchical' => false,
'menu_position' => 12,
'supports' => array(
'title'
)
// 'menu_icon' => plugin_dir_url(__FILE__) . '/assets/icon_16_grey.png'//16x16 png if you want an icon
);
register_post_type('yard_gallery', $gallery_args);
}
public function yard_taxonomies() {
register_taxonomy(
'yard_work_type',
'yard_gallery',
array(
'hierarchical' => true,
'label' => 'Art der Arbeit'
)
);
register_taxonomy(
'yard_subject',
'yard_gallery',
array(
'hierarchical' => true,
'label' => 'Motiv'
)
);
}
public function yard_add_columns( $columns ){
$columns = array(
'cb' => '<input type="checkbox">',
'yard_post_thumb' => 'Thumbnail',
'title' => 'Bezeichnung',
'yard_pos' => 'Position',
'date' => 'Datum'
);
return $columns;
}
public function yard_fill_columns( $column ) {
global $post;
switch ($column) {
case 'yard_post_thumb' :
echo the_post_thumbnail('admin-list-thumb');
break;
}
}
public function yard_metaboxes( $post ) {
global $wp_meta_boxes;
add_meta_box(
'yard-attachments',
'Bilder',
array($this, 'get_yard_attachment_table'),
'yard_gallery'
);
}
public function get_yard_attachment_table() {
$yard_list_table = new Yard_Attachment_Table();
$yard_list_table->prepare_items();
$yard_list_table->display();
}
}
Strict Standards: Only variables should be passed by reference in /Applications/MAMP/htdocs/Web/ChristophRokitta/wp v.1.0/wp-includes/pomo/mo.php on line 210
The error message tells it all. The ".../popo/mo.php" file has to do with (is related to) the Wordpress translation. (I bet you're using Wordpress with German language files).
I can't see what could be wrong in the code you've posted here, but something is interfering with translation. Looking at what the error message tells us, some variable that Wordpress tries to translate fails to be translated since it's not the correct type.
Warning: Cannot modify header information - headers already sent by (output started at /Applications/MAMP/htdocs/Web/ChristophRokitta/wp v.1.0/wp-includes/pomo/mo.php:210) in /Applications/MAMP/htdocs/Web/ChristophRokitta/wp v.1.0/wp-includes/pluggable.php on line 876
This is a logic result of the previous error message and the headers Wordpress tries to send to the browser.
What's happening: the first error message is being pushed to the client and flushed to the browser screen. Next, Wordpress tries to send it's usual headers and produces an error while doing so because headers have to be send before ANY content is being send to the client.
In other words: the "Cannot modify header information" error always comes up when you echo something to screen and then try to send "header(...)" information. In this case, the translation problem produces the first error message and then Wordpress tries to send headers, which fails and produces the second error message.
TIPS
Check everything you're doing which is related to translation (read: wherever you are passing "German" and/or "English" language strings)
and even more important
Make sure that you're actually passing the correct type(s)... looking at the error and your code, it could well be you're passing a class, a class reference, or another object somewhere instead of the expected variable.

Drupal add an action on the content listing

Is it possible to add an action on the /admin/content page. We got "publish selected content" or "delete selected content" etc etc...
http://screencast.com/t/v2ZMedCqy3g
I'm trying to add an action here from a module installation.
Thanks
You need to implement hook_node_operations(). You can look at the pathauto_node_operations() as example.
function pathauto_node_operations() {
$operations['pathauto_update_alias'] = array(
'label' => t('Update URL alias'),
'callback' => 'pathauto_node_update_alias_multiple',
'callback arguments' => array('bulkupdate', array('message' => TRUE)),
);
return $operations;
}
Notices that the callback specified takes an array of node ids, plus the additional callback arguments as you specified in your hook implementation (see above example).
// The call back specified above ^
function pathauto_node_update_alias_multiple(array $nids, $op, array $options = array()) {
$options += array('message' => FALSE);
$nodes = node_load_multiple($nids);
foreach ($nodes as $node) {
pathauto_node_update_alias($node, $op, $options);
}
if (!empty($options['message'])) {
drupal_set_message(format_plural(count($nids), 'Updated URL alias for 1 node.', 'Updated URL aliases for #count nodes.'));
}
}

Resources