How to prevent WordPress Media Library from removing image? - wordpress

I have a custom post type that must have its feature image. Its feature image also appears in the Media Library where user can delete the image/attachment file permanently. But I want to prevent user from deleting the feature image of my custom post type. So I use the following hook to intercept the ajax request, validate if user is deleting the image of my custom post type and stop the process by using wp_die().
add_filter('pre_delete_attachment', 'check_my_custom_post_type', 0, 2);
function check_my_custom_post_type($delete, $post) {
if (Yes it is image of my custom type) {
wp_die('My message', 'My title', ['response' => 400]);
}
}
It works fine on the server side. The image of my custom post type cannot be deleted. But Media Library, on the client side, still removes the image from its views even the image on the server side has not been deleted and an exception 400 has been thrown.
How to prevent Media Library from removing images from its views on the client side if image has not been deleted on the server side?

In documentation second parameter is bool|null. Try to return false instead of wp_die

You can remove the delete link totally using the wp_prepare_attachment_for_js hook.
function remove_media_delete_link_in_grid_view( $response ) {
$response['nonces']['delete'] = false;
return $response;
}
add_filter( 'wp_prepare_attachment_for_js', 'remove_media_delete_link_in_grid_view' );
This works also with the "Bulk Delete" action. If the attachment has this nonce value false, it will not be deleted with the other bulk selected attachments 👍🏻.
But this works only with the Grid View that uses the wp_prepare_attachment_for_js hook. The List view doesn't use any Javascript. So, to prevent deleting media files in the List view, you have to:
Remove the individual delete link of each media item in the list using the media_row_actions hook.
And remove the delete option from the bulk actions menu using the bulk_actions-{$screen} hook.
function remove_media_delete_link_in_list_view( $actions ) {
unset( $actions['delete'] );
return $actions;
}
add_filter( 'media_row_actions', 'remove_media_delete_link_in_list_view' );
add_filter( 'bulk_actions-upload', 'remove_media_delete_link_in_list_view' );

Related

How to hide a content on WordPress after receiving a certain number of views

How can I hide content in a post or page after it has received a certain amount of views I have set using a shortcode in WordPress?
Let's say I make a post. I enclose some content in the shortcode. I set the content to be shown for just 500 views. Then once the post reaches 500 views, the content should disappear from the post or page.
I have tried so many other plugins but couldn't find any solutions to this. wp-limit-post-views plugin also didn't solve my problem. I need help on this.
You could try something like that:
function hide_contents_function($atts, $content) {
$attributes = shortcode_atts(
array(
'count' => 500
),
$atts
);
// Get the max counts for the current post from the DB.
// You could use either an options if the counter is global, or the post meta.
// For this example I am using options, but it's up to you the implementation
$total_count = get_option('total_count', 0);
// Alternative way using post meta to get the counter per page/post
$total_count = get_post_meta(get_the_ID(), 'post_view_count', true);
if ( ! $total_count ) {
$total_count = 0;
}
// If the count loaded from the DB is bigger than the count
// property value then return nothing.
if ( $total_count > (int)$attributes['count'] ) {
return '';
}
return do_shortcode($content);
}
add_shortcode('hide_contents', 'hide_contents_function');
The above code, will register a short code that accepts an attribute allowing you to control how many views you want to have before you hide the contents.
In this example I used a single value from options table, but you are free to use any method you like to count the total views of a single post.
To use this short code you can do something like that:
[hide_contents count="345"]I will be hidden after 345 views.[/hide_contents]
Note that if you have installed any cache system, your content will not be hidden if the page is cached! That's not a problem of the short code, but the problem will occur because of the cache.
Finally, remember to update the counter of the views on each post refresh :)

WP / Elementor Intercept Form and Redirect with Data

I have a form built in Elementor that I am looking to intercep, process the data and forward onto a third party then subsequently show the data on a "confirm" card.
I am able to build this whole process as a single page, setting each as display none with CSS then showing / hiding with JS as I receive AJAX responses. This isn't ideal as it breaks with JS turned off.
I haven't been able to find the right Elementor hook and way to populate a new page with PHP, has anyone had experience with this?
There are a few methods to POST data to another url from an Elementor web form.
One is using many of the API integrations such as Mailchimp, ActiveCampaign, Zapier etc. (see https://docs.elementor.com/article/281-form-faq) and (https://docs.elementor.com/category/405-integrations)
Another (very limited method) is by using the form's Action after Submit and choosing "redirect" and then using each field's short code as individual data strings in the url such as:
mysite.com/thank-you?fname=[field id="fname"]&lname=[field id="lname"]
You can even build your /thank-you/ page in Elementor to GET that data and populate Elementor elements such as text, title, links etc with the form field data. For example, I could drop a text element on the /thank-you/ page and choose dynamic instead of typing in the text area and from the dynamic drop down, choose "request parameter" and for the "type" choose GET and for the param name use your unique url keys such as fname, lname etc. You can even set prefix, suffix and even fallback text along with it.
Lastly, here is a write up on how to back end code passing data externally (https://developers.elementor.com/forms-api/#Form_New_Record_Action).
// A send custom WebHook
add_action( 'elementor_pro/forms/new_record', function( $record, $handler ) {
//make sure its our form
$form_name = $record->get_form_settings( 'form_name' );
// Replace MY_FORM_NAME with the name you gave your form
if ( 'MY_FORM_NAME' !== $form_name ) {
return;
}
$raw_fields = $record->get( 'fields' );
$fields = [];
foreach ( $raw_fields as $id => $field ) {
$fields[ $id ] = $field['value'];
}
// Replace HTTP://YOUR_WEBHOOK_URL with the actuall URL you want to post the form to
wp_remote_post( 'HTTP://YOUR_WEBHOOK_URL', [
'body' => $fields,
]);
}, 10, 2 );
And a thread with many more examples integrating with different APIs using that template as a primer (https://github.com/elementor/elementor/issues/2397)

Custom Post Type Action Hook / Transients

This question is in regards to a plug-in I'm developing.
I'm trying to fire a function each time a custom post type called "Product" is added or edited. In particular, I need a hook that fires before the meta boxes load on the add/edit page, but that only fires on that "Product" custom post type's edit page.
The function that will fire makes an API request, and caches the response in a transient.
The reason for the action hook is because in my current code, when the transient has expired, the add/edit page is broken during the first page load. However if you refresh the page after that, it shows up as intended. I'm fairly certain this is happening because the current conditional statement that checks the transient is located inside of the function that generates the meta box. So my theory is if I can set up an action hook to check the transient before the meta box is generated, it might solve the problem.
However I've got a second theory that the problem is being caused because of the time it takes to make the API request and return the response is longer than the time it takes for the page to load. So if there is an action hook that will delay page loading until the function finishes executing it would be an ideal solution, but I don't believe such an action hook exists. I'm not even certain if such a delay is possible.
I'd really appreciate any help or alternative suggestions you guys might have. Thanks for your time guys.
Code Example:
add_action( 'edit_product', 'llc_hook_campaign_find_active' );
function llc_hook_campaign_find_active() {
if (!$t_campaign_find_active){
limelight_cart_campaign_find_active();
return false;
}
}
Since you are using an action hook, it is not waiting for your API response.
Try using a filter hook instead.
Try using wp_insert_post_data
function filter_handler( $data , $postarr ) {
//make your API call, get the response and store it in post meta or data wherever you want
$response = 'your API response';
//e.g. update_post_meta($postarr['ID'], 'meta_key', $response); OR
//$data['post_content'] = $response;
return $data;
}
add_filter( 'wp_insert_post_data', 'filter_handler', '99', 2 );
In your case, following should work -
add_filter( 'wp_insert_post_data', 'llc_hook_campaign_find_active', '99', 2 );
function llc_hook_campaign_find_active( $data , $postarr ) {
if (!$t_campaign_find_active){
limelight_cart_campaign_find_active();
return $data;
}
}
I was able to make the API request before the meta boxes loaded on the Admin Add/Edit screen by using the action filter edit_form_top. That particular action hook is fired as soon as the Add/Edit page for any post/page/custom post type is loaded. In order to narrow it down so that the function only fires on the Add/Edit screen for my "product" custom post type, I used get_current_screen() along with an if statement.
add_action('edit_form_top', 'llc_hook_campaign_find_active');
function llc_hook_campaign_find_active() {
//Fetch current screen information
$screen = get_current_screen();
//Check if post type is "product"
if($screen->post_type == "product") {
//API Request that checks for an existing transient
$t_campaign_find_active = get_transient('campaign_find_active');
if (!$t_campaign_find_active){
limelight_cart_campaign_find_active();
return false;
}
}
}
Works like a charm.

Redirecting from Custom Admin Page Produces "Headers already sent"

I've registered a custom Admin page in my plugin through add_submenu_page. In the callback function (the one that generates the contents of the admin page), I have the following code:
wp_redirect('http://google.com');
exit;
However, when I visit the admin page I get an error:
Warning: Cannot modify header information - headers already sent by (output started at ..\wp-admin\includes\template.php:1637) in ..\wp-includes\pluggable.php on line 878
The callback from add_submenu_page happens too late (after the admin sidebar and header are rendered), this is why the location header can not be sent anymore.
To accomplish this, we need to hook a function a bit earlier in the WordPress admin area, before the headers are sent (e.g. admin_init).
A good way:
function myplugin_preprocess_pages($value){
global $pagenow;
$page = (isset($_REQUEST['page']) ? $_REQUEST['page'] : false);
if($pagenow=='admin.php' && $page=='myplugin-custom-page-slug'){
wp_redirect('http://google.com');
exit;
}
}
add_action('admin_init', 'myplugin_preprocess_pages');
The above code will redirect you to Google whenever you try to view wp-admin/admin.php?page=myplugin-custom-page-slug.
In my case, I've attached the custom page via add_submenu_page to the default (admin.php) parent in the Admin area and I've set the custom page's slug to myplugin-custom-page-slug. Feel free to replace the values in the code above or even add a PHP switch if you have a lot of custom admin pages.
This way we have hooked early enough to do a redirection whenever our custom admin page is viewed.
Update: (A different approach)
Thanks to this post, I've learned that WordPress creates a unique action that you can hook to for each custom admin page (load-{parent_page_slug}_page_{plugin_subpage_slug}). For example, if you've added a custom admin page with parent admin.php and slug myplugin-custom-page, you can hook to its "load" action in the following manner:
add_action( 'load-admin_page_myplugin-custom-page', 'myplugin_custom_page_redirect' );
function myplugin_custom_page_redirect() {
if ( 'myplugin-custom-page' == filter_input( INPUT_GET, 'page' ) ) {
wp_redirect( 'http://google.com' );
exit;
}
}
Note that the action name has some things to consider. It's a mixture of underscores and dashes and make sure you only include the parent page's name without the extension (so "admin" instead of "admin.php")

How do you remove or change the functionality of the Publish button on a custom WordPress post?

I have a custom post type and need keep the post status from getting set to 'Published' when you click the Publish button. Instead, it should work like the Save Draft button. So I either need to figure out how to just remove the Publish button so the user's can only click Save Draft our preferably, update the Publish button functionality so it doesn't set the post to publish.
You can use wordpress action hooks to modify default behaviors.
http://codex.wordpress.org/Function_Reference/add_action
In your case, you want to use the 'publish_post' hook.
So you can do
function dont_publish( $post_ID )
{
if(get_post_type($post_ID) == 'your_custom_type'){
exit;
}
}
//the dont_publish function will be called after the publish button is clicked
add_action( 'publish_post', 'dont_publish' );
The way it is above, nothing will happen at all if the publish button is clicked, but you can play around with the dont_publish function to get the results you want.
#PhoenixWing156 was close but one little change so the the other post types get updated as usual.
function dont_publish( $data , $postarr ) {
if($data['post_type'] == 'custom_post_type') {
$data['post_status'] = 'draft';
}
return $data;
}
add_filter('wp_insert_post_data' , 'dont_publish' , '99', 2);
The wp_insert_post_data hook is called before information about a post is saved to the database.
http://codex.wordpress.org/Plugin_API/Filter_Reference/wp_insert_post_data
You can try:
function dont_publish( $data , $postarr )
{
if($data['post_type'] == 'custom_post_type'){
$data['post_status'] = 'draft';
return $data;
}
}
add_filter('wp_insert_post_data' , 'dont_publish' , '99', 2);
WordPress provides the remove_meta_box() function exactly for this purpose.Just add this below code:-
add_action( 'admin_menu', function () {
remove_meta_box( 'submitdiv', 'Your_custom_post_type', 'side' );
} );
You could also disable the default saving metabox and add you own.
This is not documented well in the developer docs of wordpress.
To do this you have to hook into the "add_meta_boxes"-hook and in the hooked function yo have to call remove_meta_box('submitdiv','your-cpt','side');
The code should be something like this:
function your_cpt_metaboxes(){
remove_meta_box('submitdiv','your-cpt','side');
...
}
add_action('add_meta_boxes','function your_cpt_metaboxes');
your-cpt has to be changed to the name of your cpt of course.
I was also searching for this handy snippet and found it in the plugin Awesome Support.
The original saving metabox code can be found in /wp-admin/includes/metaboxes.php .
Just search for post_submit_meta_box (in WP 5.4 on line 22).

Resources