I'm developing a social network with Buddypress, I created a RSS plugin to pull the RSS feed from the specified websites.
Everything is working, except when the RSS is posted to the activity stream. When I create the activity content to print a link, I set the link target to "_new" to open it in a new page.
Here's the code:
function wprss_add_to_activity_feed($item, $inserted_ID) {
$permalink = $item->get_permalink();
$title = $item->get_title();
$admin = get_user_by('login', 'admin');
# Generates the link
$activity_action = sprintf( __( '%s published a new RSS link: %s - ', 'buddypress'), bp_core_get_userlink( $admin->ID ), '' . attribute_escape( wprss_limit_rss_title_chars($title) ) . '');
/* Record this in activity streams */
bp_activity_add( array(
'user_id' => $admin->ID,
'item_id' => $inserted_ID,
'action' => $activity_action,
'component' => 'rss',
'primary_link' => $permalink,
'type' => 'activity_update',
'hide_sitewide' => false
));
}
It should come up with something like that:
Test
But it prints like that:
Test
Why is this happening?
The 'target' attribute is probably getting stripped by BuddyPress's implementation of the kses filters. You can whitelist the attribute as follows:
function se16329156_whitelist_target_in_activity_action( $allowedtags ) {
$allowedtags['a']['target'] = array();
return $allowedtags;
}
add_filter( 'bp_activity_allowed_tags', 'se16329156_whitelist_target_in_activity_action' );
This probably won't retroactively fix the issue for existing activity items - it's likely that they had the offending attribute stripped before being stored in the database. But it should help for future items.
Related
I have an odd request, i'm not sure if this is even possible. But i'll try to work out the process below, and if anyone can help me work this out that would be amazing!
Ideally the process is as follows:
Admin goes to parent options page, within the options page there is a repeater field, called add new company. This will just be a field with a title.
Admin fills in field and presses save. This will generate a sub options page with that name, within the options field, there will be a set of fields like logo, a colour picker and some text fields (these could be a set of fields from within ACF if thats possible).
Also when this original Repeater Field is made/saved a set of pages is generated from a set of templates. Essentially using the name from the repeater field to be the main page title for the top level page and all the sub pages below are just dynamically generated. They don't need to have anything different about them, they just need to generate from a set of page templates. It needs to be able to associate with the newly generated company bits from the sub options field.
This will then essentially give the admin a new set of pages which will use the new options logo / colours etc. It would almost need to generate a new set of templates based off the master templates to dynamically make sure it picked up the correct information from the sub options page.
I'm not sure if this is possible, I have seen it work elsewhere on another job I have worked on (not exactly the same as the above but similar), but I can't work out the process to make it work sadly, as I have a horrid feeling that there is some complex bits within the database going on to do the duplication dynamically.
My other option is to run everything as a WordPress Multisite but I was trying to avoid that if possible on this occasion, but I may have to use Multisite to achieve the above.
If anyone can help me work this out that would be amazing!
Thanks in advance for any help :)
You should be able to plug into the save_post action and create new subpages from there.
add_action( 'save_post', 'create_sub_pages' ); //Plug into save_post action
//Function create sub_pages
function create_sub_pages($post_ID) {
//Repeater field name
$repeater_field_array = get_field('repeater_field_name');
//Loops through all of the items in the repeater field
foreach($repeater_field_array as $key => $value) {
//Check to see if there is already a sub page with that post name
$child_pages = get_pages(array( 'child_of' => $post_ID ));
$child_page_exists = false;
foreach($child_pages as $pages) {
if ($pages->post_title === $key) {
$child_page_exists = true;
}
}
//If not, set up the creation of the new post
if ($child_page_exists === false) {
$new_page_title = esc_html__( $key );
$new_page_content = '';
$new_page = array(
'post_type' => 'page',
'post_date' => esc_attr( date('Y-m-d H:i:s', time()) ),
'post_date_gmt' => esc_attr( date('Y-m-d H:i:s', time()) ),
'post_title' => esc_attr( $new_page_title ),
'post_name' => sanitize_title( $new_page_title ), //This could from the sub_field in the repeater
'post_content' => $new_page_content,
'post_status' => 'publish',
'post_parent' => $post_ID,
'menu_order' => $new_page_order
);
$new_page_id = wp_insert_post( $new_page );
update_post_meta( $new_page_id, '_wp_page_template', $value );
}
}
}
Again, this is just a spitball since there was not much code to review, but it could help you get going in the right direction.
For the last few years I have been developing a custom theme and various plugins that add options to WPBakery Page Builder elements and create new custom elements, etc.
Everything has worked perfectly up to Page Builder v5.7. But then when v6.0.x came out, suddenly every Checkbox option I have added to every element, be it a custom element or one of Page Builder's standard elements, has a problem: As soon as I click the element in the Page Editor to open it's settings, all checkboxes clear and become unticked regardless of their default or saved values.
If I tick a checkbox and save settings, when I view the front end of the site the option has indeed saved and is working as it should be; but when I open the element's settings window again, they all clear.
I have looked at all my code and compared it to other plugin's code and all of WPBakery's documentation, etc, and I can't see a problem anywhere. I thought it might be a bug with Page Builder v6.0.x and I have sent them a support ticket, but they haven't been able to give me an answer. Other people's plugins seem to be working though, along with all of Page Builder's own built-in elements, which leads me to believe that it must be something to do with my code.
Here is an example:
// After VC Init
add_action( 'vc_after_init', 'gd_after_init_actions' );
// ADD OPTIONS TO VISUAL COMPOSER ELEMENTS //
function gd_after_init_actions() {
// ADD FULL WIDTH CHECKBOX TO SINGLE IMAGE ELEMENTS //
$single_image_attributes = array(
'type' => 'checkbox',
'class' => 'full_width_image',
'param_name' => 'full_width_image',
'value' => array('Force Full Width' => true),
'weight' => 1
);
vc_add_param('vc_single_image', $single_image_attributes);
}
This adds a "Force Full Width" checkbox to Page Builder's standard "Single Image" element. The checkbox appears perfectly and if I check it, save and then view the front end of the site, the checkbox does indeed work; the checkbox value has saved and the image is stretched to full width. But then if I go back to the back end and click on the Single Image element to edit it's settings again, the checkbox becomes unchecked and it's saved value is lost.
Similarly, with plugins in which I am adding options with vc_map(), here is a sample:
function vc_before_init_actions(){
// Stop all if VC is not enabled
if ( !defined( 'WPB_VC_VERSION' ) ) {
return;
}
// ELEMENT CLASS //
class vcResponsiveYouTubeVideo extends WPBakeryShortCode {
// ELEMENT INIT //
function __construct() {
add_action( 'init', array( $this, 'vc_youtube_video_mapping' ) );
add_shortcode( 'vc_youtube', array( $this, 'vc_youtube_video_html' ) );
}
// ELEMENT MAPPING //
public function vc_youtube_video_mapping() {
// Map the block with vc_map()
$youtubeIcon = plugins_url('responsive-youtube-icon.png',__FILE__ );
vc_map(
array(
'name' => __('Responsive YouTube Video', 'text-domain'),
'base' => 'vc_youtube',
'category' => __('Custom Elements', 'text-domain'),
'icon' => $youtubeIcon,
'params' => array(
array(
'type' => 'checkbox',
'param_name' => 'lightbox',
'value' => array('Pop up video in lightbox' => true),
'admin_label' => false,
'weight' => 0,
'group' => 'Custom Group'
),
)
)
);
}
[etc...]
}
This is just a snippet of the code and there is much more to it, but this is just the part pertaining to one of the checkbox options that I have in this particular plugin. Just like the previous example, if I check it and save the options, it works, but then if I come back into the element's settings, it clears.
Even checkboxes that are set to be checked by default:
array(
'type' => 'checkbox',
'param_name' => 'branding',
'value' => array('Keep YouTube Branding in Control Bar' => true),
'std' => true,
'admin_label' => false,
'weight' => 0,
'group' => 'Custom Group'
),
This checkbox still clears as soon as I click open the settings window.
I've done a fair bit of testing and it appears this issue exists (so far) only with Checkboxes. All the other inputs I've created, such as textfields, colorpickers, dropdowns, etc, all save fine and retain their values when I re-open the settings window.
Does anyone any have ideas on this?
we found an issue, it caused because we added strict compare for key-value pairs for checkboxes, but in your mapping value is boolean true, however shortcode saves this as string "1" and when edit form is opening then strict compare fails.
For BC we will fix this in our next release, there is a patch:
Index: include/params/default_params.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- include/params/default_params.php (revision afcb0ccf521c36ce176651964792d18c1f9b1bfc)
+++ include/params/default_params.php (date 1559032145000)
## -85,7 +85,9 ##
$values = isset( $settings['value'] ) && is_array( $settings['value'] ) ? $settings['value'] : array( esc_html__( 'Yes', 'js_composer' ) => 'true' );
if ( ! empty( $values ) ) {
foreach ( $values as $label => $v ) {
- $checked = in_array( $v, $current_value, true ) ? 'checked' : '';
+ // NOTE!! Don't use strict compare here for BC!
+ // #codingStandardsIgnoreLine
+ $checked = in_array( $v, $current_value ) ? 'checked' : '';
$output .= ' <label class="vc_checkbox-label"><input id="' . $settings['param_name'] . '-' . $v . '" value="' . $v . '" class="wpb_vc_param_value ' . $settings['param_name'] . ' ' . $settings['type'] . '" type="checkbox" name="' . $settings['param_name'] . '" ' . $checked . '>' . $label . '</label>';
}
}
I looked around here and by search engine, but unfortunately I couldn't find a solution for myself.
So, I now ask assistance with a function that I need to customize for the Contact Form 7 WordPress plugin. The function was from another question.
In a drop-down menu (select) I need two details (workshop name and date) in one option field. Both details come from the same post of a custom post type. The first detail is a post_title, the second is a custom-field from Meta-Box plugin.
The following function works in principle, but it only returns the one or the other detail. Probably the solution is within the foreach construct. But I don't know how it works.
I would be very grateful for support!
[UPDATE 2018-08-12]
After further research, I've found the solution at this post and changed the function accordingly.
The solution should look like this:
<select>
<option value="workshop name – date">workshop name – date</option>
...
</select>
This is the function:
add_filter( 'wpcf7_form_tag', 'dynamic_field_choose_workshop', 10, 2);
function dynamic_field_choose_workshop ( $tag, $unused ) {
if ( $tag['name'] != 'workshop' )
return $tag;
$args = array (
'post_type' => 'workshop',
'post_status' => 'publish',
'orderby' => 'name',
'order' => 'ASC',
'numberposts' => - 1,
);
$custom_posts = get_posts($args);
if ( ! $custom_posts )
return $tag;
foreach ( $custom_posts as $custom_post ) {
$ID = $custom_post->ID;
$tag['values'][] = $custom_post->post_title . ' - ' . rwmb_get_value('workshop_meta_boxes_date', '', $ID);
$tag['raw_values'][] = $custom_post->post_title . ' - ' . rwmb_get_value('workshop_meta_boxes_date', '', $ID);
$tag['labels'][] = $custom_post->post_title . ' - ' . rwmb_get_value('workshop_meta_boxes_date', '', $ID);
}
return $tag;
}
There is CF7 extension that will do this for you. Checkout the Smart Grid-Layout for CF7, it introduces a new tag called dynamic_dropdown. This is is what you want to use. The dynamic_dropdown creates a select field and allows you to populate the field options using either a taxonomy, titles of a post type, or a filter. You want to use the filter option to actually construct the options as per your requirement. The tag popup window is self explanatory, however if you get stuck post a comment below and I'll give you some more tips.
Using the following dynamic_dropdown tag,
[dynamic_select workshop-date-select class:select2 "source:filter"]
it creates a <select name="workshop-date-select"> dropdown field which will be converted into a select2 jquery field on the front end, and its values dynamically created using the following function placed in the functions.php file,
add_filter('cf7sg_dynamic_dropdown_custom_options', 'filter_options',10,3);
function filter_options($options, $field_name, $form_key){
/*first we verify if this is the right field from the right form
in case multiple forms with similar fieldd exiss.
the $form_key is a unique key exposed by the Smart Grid-layout plugin
instead of using form IDs to make forms and code more portable across servers.*/
if($form_key != 'my-form' && $field_name != 'workshop-date-select') return $options;
$options = array();
//load your options programmatically, as $value=>$name pairs.
$args = array (
'post_type' => 'workshop',
'post_status' => 'publish',
'orderby' => 'name',
'order' => 'ASC',
'numberposts' => - 1,
);
$workshops = get_posts( $args );
foreach($workshops as $workshop){
$val = $workshop->post_title . ' - ' . rwmb_get_value('workshop_meta_boxes_date', '', $workshop->ID);
$options[$val]=$val;
}
return $options;
}
this will create the desired dropdown select field in the front end.
NOTE of CAUTION: I would populate the option values as the workshop post ID rather than the same text as the option label. When the form is submitted the value of the post ID can be used to populate the notification email with the desired workshop title and date. This gives more flexibility to expand the reported information in the future.
I'm trying to create a custom page on my domain that has the url 'http://example.com/test/slug', however the slug does not seem to support slashes (the slug '/test/slug' turns into '/testslug'). Is it possible using the rest api to create a page on a custom url?
Also, I populate the 'meta' dictionary, however the page created does not contain a meta 'description' tag in the header. How can I correctly create custom meta tags?
import requests
url = 'http://example.com/wp-json/wp/v2/pages'
data = {'content': '<h2>test content</h2>',
'meta': {'description': 'this is a test meta field'},
'slug': 'test/slug',
'status': 'publish',
'title': 'test title'}
resp = requests.post(url, json=data, auth=('user','pass'), headers={'Content-Type':'application/json'})
the slug does not seem to support slashes (the slug '/test/slug' turns
into '/testslug').
Yes, because whether you use the REST API or the admin UI for creating Pages, the slug is limited to alphanumeric characters, plus dashes (-) and underscores (_). See https://developer.wordpress.org/rest-api/reference/pages/#schema-slug.
You can remove the limitation by adding remove_filter( 'sanitize_title', 'sanitize_title_with_dashes', 10, 3 ); to the theme's functions.php file (or a custom plugin); however, visiting the page (URL) would by default throw a 404 (not found) error. There are ways to fix that, but in short, you should not remove the filter.
Is it possible using the rest api to create a page on a custom url?
No it's not, not by default.
However, if you want http://example.com/test/slug to serve/show a Page/Post/etc. be it created via the REST API or not, then you can use custom URL rewrite rules, e.g. via add_rewrite_rule().
the page created does not contain a meta 'description' tag in the
header. How can I correctly create custom meta tags?
You need to register the meta key, which in your case is description.
See
Working with registered meta in the REST API
Read and write a post meta field in post responses
And you can register it using the register_meta() function in wp-includes/meta.php.
Example for your description meta:
<?php
// The object type. For custom post types, this is 'post';
// for custom comment types, this is 'comment'. For user meta,
// this is 'user'.
$object_type = 'post'; // 'post' even for Pages
$args1 = array( // Validate and sanitize the meta value.
// Note: currently (4.7) one of 'string', 'boolean', 'integer',
// 'number' must be used as 'type'. The default is 'string'.
'type' => 'string',
// Shown in the schema for the meta key.
'description' => 'A meta key associated with a string meta value.',
// Return a single value of the type.
'single' => true,
// Show in the WP REST API response. Default: false.
'show_in_rest' => true,
);
register_meta( $object_type, 'description', $args1 );
To quickly test if the meta description was successfully registered and that it's being available from the REST API, perform a GET request to http://example.com/wp-json/wp/v2/pages/<id> where <id> is the Page ID.
First, make sure your Permalinks settings is "Post Name".
This can be configured under http://<yoursite.com>/wp-admin/options-permalink.php
I have studied "Custom Permalinks" plugin code to understand how to answer this question, it saves a metadata called "custom_permalink" and does heavy INNER JOINS to make WordPress core work with "fake" sub-slugs.
I came up with a different solution. The trick here is to create a fake parent page to serve as a base URL for children pages. I have written it in PHP, since I don't know Python.
<?php
require_once('helpers.php');
define('API_URL', 'http://temp.localhost');
# Let's get all pages in an array
$pages = curlGet(API_URL.'/wp-json/wp/v2/pages');
# Your usual $args
$args = array(
'title' => 'API TEST',
'status' => 'draft',
'content' => 'content',
'slug' => 'some/thing'
);
# We intercept it here, before sending to WordPress
$args = maybe_add_parent($args);
$response = curlPost( API_URL.'/wp-json/wp/v2/pages/', $args );
/**
* Receives an $args array that would be sent to WordPress API and checks if we need to add a parent page
*/
function maybe_add_parent(array $args) {
if (array_key_exists('slug', $args)) {
# Has parent?
if (strpos($args['slug'], '/') !== false) {
$parent = explode('/', $args['slug']);
# For simplicity sake let's do it parent/chidren slugs only
if (count($parent) !== 2) {
die('This script can only run parent/children slugs');
}
$slug = array_pop($parent);
# Here, we will check the parent to see if it exists.
$parent_id = slug_exists($parent[0]);
if ($parent_id === false) {
# If it does not, it will create it and return it's ID
$parent_id = create_parent($parent[0]);
}
# Add parent ID to $args.
$args['parent'] = $parent_id;
# Rename the slug
$args['slug'] = $slug;
}
}
return $args;
}
/**
* Checks if a given slug exists in $pages
*/
function slug_exists(string $slug) {
global $pages;
foreach ($pages as $page) {
# Checks if a "Parent" page with this slug exists
if ($page['slug'] == $slug && $page['parent'] == 0) {
return true;
}
}
return false;
}
/**
* Creates a parent page
*/
function create_parent(string $slug) {
$args = array(
'title' => $slug,
'status' => 'draft',
'content' => '',
'slug' => $slug
);
$response = json_decode(curlPost( API_URL.'/wp-json/wp/v2/pages/', $args ));
return $response->id;
}
The script does the following:
Send a GET request for all the pages on the website.
Intercepts an $args array that would be sent to the WordPress API.
Checks if this $args contains a slug in the format parent/child
If it does, check if parent page exists
If it doesn't, it will create this page as a draft and return it's ID
It the parent already exists, it will just return it's ID
With the Parent ID, it will modify $args adding parent key and return $args
When WordPress sees that that page has a parent, it will automatically add it's slug in the url, as in parent/child
It works:
To get the URL structure you want, the most straightforward way is to create a child page that references an already existing page as it's parent.
To amend your example above:
import requests
url = 'http://example.com/wp-json/wp/v2/pages'
parent_page = 43 # assuming that page with URL '/test' has id of 42
data = {'content': '<h2>test content</h2>',
'meta': {'description': 'this is a test meta field'},
'slug': 'slug',
'status': 'publish',
'title': 'test title'
'parent': parent_page} # <--
resp = requests.post(url, json=data, auth=('user','pass'), headers={'Content-Type':'application/json'})
Would give you a new page at example.com/test/slug
Reference to arguments for "Create a Page", (POST /wp/v2/pages) on WordPress rest API Handbook: https://developer.wordpress.org/rest-api/reference/pages/#create-a-page
I'm trying to figure out a way for members on my Wordpress (Buddypress) site to pick their "favorite movies, books, etc."
It would be nice if, instead of members simply typing a list of these things, they could select from books already in the system, and add more as the please in the future.
I'm hoping that there is an easy answer to this, such as a plugin that I can use, or, at least, modify. Does anyone know of anything that I can look into?
Your title asks how to add a user profile field. Here is the code to add as many fields as you like. Once you add the field, you can easily place additional inputs or options on the custom tab page for users to enter their own favorites.
function my_test_setup_nav() {
global $bp;
$parent_slug = ‘test’;
$child_slug = ‘test_sub’;
//name, slug, screen, position, default subnav
bp_core_new_nav_item( array(‘name’ => __( ‘Test’ ),’slug’ => $parent_slug,’screen_function’ => ‘my_profile_page_function_to_show_screen’,'position’ => 40,’default_subnav_slug’ => $child_slug ) );
/* Add the subnav items to the profile */
// name, slug, parent_url, parent slug, screen function
bp_core_new_subnav_item( array( ‘name’ => __( ‘Home’ ), ‘slug’ => $child_slug, ‘parent_url’ => $bp->loggedin_user->domain . $parent_slug.’/', ‘parent_slug’ => $parent_slug, ‘screen_function’ => ‘my_profile_page_function_to_show_screen’ ) );
bp_core_new_subnav_item( array( ‘name’ => __( ‘Random Page’ ), ‘slug’ => ‘random’, ‘parent_url’ => $bp->loggedin_user->domain . $parent_slug.’/', ‘parent_slug’ => $parent_slug, ‘screen_function’ => ‘my_profile_page_function_to_show_screen234′ ) );
}
function my_profile_page_function_to_show_screen() {
//add title and content here – last is to call the members plugin.php template
add_action( ‘bp_template_title’, ‘my_profile_page_function_to_show_screen_title’ );
add_action( ‘bp_template_content’, ‘my_profile_page_function_to_show_screen_content’ );
bp_core_load_template( apply_filters( ‘bp_core_template_plugin’, ‘members/single/plugins’ ) );
}
function my_profile_page_function_to_show_screen_title() {
echo ‘wptaskforce title’;
}
function my_profile_page_function_to_show_screen_content() {
echo ‘wptaskforce content’;
}
//random page content:
function my_profile_page_function_to_show_screen234() {
//add content here – last is to call the members plugin.php template
add_action( ‘bp_template_content’, ‘my_profile_page_function_to_show_screen234_content’ );
bp_core_load_template( apply_filters( ‘bp_core_template_plugin’, ‘members/single/plugins’ ) );
}
function my_profile_page_function_to_show_screen234_content() {
echo ‘This is a random page.’;
}
add_action( ‘bp_setup_nav’, ‘my_test_setup_nav’ );