Wordpress : excluding images 'inserted into post' from get_children - wordpress

I have a page which has a slideshow at the top, and images inserted inline into the content area.
I need to exclude the images that have been inserted into the post from the slideshow.
Currently I am excluding the 'Featured Image', but this limits me to one image that can be inserted into the post.
Here is my existing code:
$thumbnail = get_post_thumbnail_id();
$images = get_children( 'post_type=attachment&post_mime_type=image&order=asc&orderby=menu_order&post_parent='.$post->ID .'&exclude='.$thumbnail);
Previously I have used the description field of the image meta data to exclude images by entering 'exclude'. This isn't as nice for the end user as I'd like it to be.
Any suggestions, plugins or code based!
Update:
I've updated the code, so now I get any image URLs from the post_content and check them against the slideshow images.
$content = $post->post_content;
$inlineImages = array();
preg_match( '/src="([^"]*)"/i', $content, $inlineImages ) ;
$thumbnail = get_post_thumbnail_id($post->ID);
$images = get_children( 'post_type=attachment&post_mime_type=image&order=asc&orderby=menu_order&post_parent='.$post->ID .'&exclude='.$thumbnail);
if ($images) {
echo '<div id="slideshow">';
foreach ( $images as $attachment_id => $attachment ) {
$image = wp_get_attachment_image_src( $attachment_id,array(900,265));
if (!in_array($image[0],$inlineImages)) {
echo '<img src="'.$image[0].'" width="'. $image[1] .'" height="'. $image[2].'">';
}
}
echo '</div>';
}
This is an OK solution, although the regex could be improved.
A nicer step would be to add the array of images to a custom field field, which
is updated on post / page update or publish.
Any suggestions on how to go about this?

Just needed to do the same thing. Your original approach is the way I wanted to do it -- simply exclude any images that had been inserted into the post from appearing in the slider. But I didn't want the client to have to do anything special to make it happen. Here's my code.
$args = array( 'post_type' => 'attachment', 'post_mime_type'=>'image','numberposts' => -1, 'post_status' => null, 'post_parent' => $post->ID );
$attachments = get_posts($args);
preg_match_all("/<img[^']*?src=\"([^']*?)\"[^']*?>/", $post->post_content, $matches, PREG_PATTERN_ORDER);
/* $matches[1] holds the urls as an array */
foreach ( $attachments as $attachment ) {
if(in_array($attachment->guid, $matches[1])){ continue;}
wp_get_attachment_image( $attachment->ID , 'slider_size');
}
First bit gets all of the images associated with the post. The $preg_match_all gets all of the images in the post body. Then as we loop through the images to display them in the slider the in_array checks the urls of the images that were inserted with the url of the image about to be added to the slider and skips on to the next one if there is a match.
Thanks for you post, got me thinking in the right direction.

I've updated the code, so now I get any image URLs from the post_content and check them against the slideshow images.
$content = $post->post_content;
$inlineImages = array();
preg_match( '/src="([^"]*)"/i', $content, $inlineImages ) ;
$thumbnail = get_post_thumbnail_id($post->ID);
$images = get_children( 'post_type=attachment&post_mime_type=image&order=asc&orderby=menu_order&post_parent='.$post->ID .'&exclude='.$thumbnail);
if ($images) {
echo '<div id="slideshow">';
foreach ( $images as $attachment_id => $attachment ) {
$image = wp_get_attachment_image_src( $attachment_id,array(900,265));
if (!in_array($image[0],$inlineImages)) {
echo '<img src="'.$image[0].'" width="'. $image[1] .'" height="'. $image[2].'">';
}
}
echo '</div>';
}

I think the easiest thing to do would be using the Meteor Slideshow plugin to create a slideshow for each page, then insert the shortcode for the proper slideshow in the content area of the proper page. Yes, it means you'll have to edit each page "outside" the page editor, but it also gives you easy, complete control over which photos do and do not appear in each slideshow, and the shortcode is very easy to put in with the page editor.

The Media Custom Fields plugin lets you add custom data to media items exactly as you would with custom post fields, providing a user-friendly way of flagging slideshow-only images. From the plugin FAQ:
In addition to giving you the option to add custom fields, your custom fields will show up in all appropriate places. If you go to the media section to manage your media like you usually do, they will show up on the edit media form. The custom fields also show up when you are uploading an image to a post, and in all other expected locations.
One approach is to install this plugin and add a custom field such as Slide with value TRUE to your slide images via the Dashboard -> Media menu. Then your filter code might be:
$thumbnail = get_post_thumbnail_id();
$images = get_children( /* removed for brevity */ );
if ($images) {
// ..
foreach ( $images as $attachment_id => $attachment ) {
// skip images not marked for slideshow
if ( !get_post_meta($attachment_id, 'Slide') ) continue;
// otherwise do slideshow things
}
// ..
}
Note that with this approach the value for Slide can be set to anything except the empty string. You may want to define Slide as a class constant somewhere to avoid hard-coding.
Advantages
Doesn't require any work on the poster's part
Future-proof: the plugin appears to be based on good-old post functionality, and attachments are just one kind of post anyway, so there's nothing weird.

Here is another approach to this.
I would not prefer using urls, because the picture inserted inside the content area could be of different size like medium, thumbnail or full. So, the url only would not match.
Using the above code,
function printMeImages() {
$content = $post->post_content;
$inlineImages = array();
// populate ids of images from wp-image-id
preg_match_all( '/wp-image-([^"]*)"/i', $content, $inlineImages ) ;
$thumbnail = get_post_thumbnail_id($post->ID);
$images = get_children( 'post_type=attachment&post_mime_type=image&order=asc&orderby=menu_order&post_parent='.$post->ID .'&exclude='.$thumbnail);
$out = "";
if ($images) {
$out .= '<ul id="slideshow">';
foreach ( $images as $attachment_id => $attachment ) {
$image = wp_get_attachment_image_src( $attachment_id,'desiredImageSize');
// $inlineImages[1] has ids to be discarded
if (!in_array($attachment_id,$inlineImages[1])) {
$out .= '<li><img src="'.$image[0].'" width="'. $image[1] .'" height="'. $image[2].'"></li>';
}
}
$out .= '</ul>';
}
return $out;
}
The images which are not set as featured and not used inside the post are retrieved.

For performance reasons I would not execute the code while the page is being rendered, but rather bind the code to the save_post hook. When the post content is edited and saved, the hook will be called and all attached images (or their ids) which are not used in the content are saved into a postmeta table. When the slider is rendered the image ids can be accessed via get_post_meta($post_id, 'image_ids_for_slider').
Like this I can avoid to do the preg_match_all operation on page rendering. This might not be a performance reason when the post content is small and when there's only one slider loaded, but generally I find this a approach a little cleaner for scaling reasons.
//hook this function to save post action
function save_ids_of_image_attachments_not_used_in_the_content( $post_id, $post ) {
$args = array( 'post_type' => 'attachment',
'post_mime_type'=>'image',
'numberposts' => -1,
'post_status' => null,
'post_parent' => $post_id
);
$attachments = get_posts($args);
preg_match_all("/<img[^']*?src=\"([^']*?)\"[^']*?>/", $post->post_content, $matches, PREG_PATTERN_ORDER);
$ids_of_attached_images_not_in_content = array();
/* $matches[1] holds the urls as an array */
foreach ( $attachments as $attachment ) {
if(in_array($attachment->guid, $matches[1])){
continue;
}
$ids_of_attached_images_not_in_content[] = $attachment->ID;
}
// save the image_ids as postmeta
update_post_meta($post_id, 'image_ids_for_slider', $ids_of_attached_images_not_in_content);
}
add_action( 'save_post', 'save_ids_of_image_attachments_not_used_in_the_content', 10, 2 );

I haven't fully understood your problem, but how about excluding the div ID within which the slideshow is present?
$thumbnail = get_post_thumbnail_id();
$images = get_children('post_type=attachment&post_mime_type=image&order=asc&orderby=menu_order&post_parent='.$post->ID .'&exclude='.$thumbnail. '&NAME');
Replace 'NAME' after thumbnail.' in the parenthesis.
Hope this helps.

Related

Wordpress: Query to show all Custom Post Types and set a Post Type icon for each post instead of Featured Images

I've set up this query to show all Post from certain Custom Post Types but I would like to alter the query and change the featured image to a specific icon that relates to the Post Type instead of the set Featured Images
function my_query_by_post_types( $query ) {
$query->set( 'post_type', [ 'articles', 'events' ] );
}
add_action( 'elementor/query/all_post_types', 'my_query_by_post_types' );
For example instead of showing the featured images for the events, I'd like to show a calendar Icon for all the posts that appear.
You can filter the featured image output conditionally
function wpdocs_addTitleToThumbnail( $html,$post_id ) {
if ( get_post_type($post_id) === 'events' )
{
$html = '<img src="your_img_src"/>';
return $html;
}
return $html;
}
add_filter( 'post_thumbnail_html', 'wpdocs_addTitleToThumbnail' ,10,2);
You can do this using WP_Query, In your front-page.php you can put this
$args = array(
'post_type' => 'my-cpt'
);
$loop = new WP_Query($args);
while($loop->have_posts()) : $loop->the_post();
if( is_front_page()){
set_post_thumbnail(get_the_ID(),int $thumbnail_id );
}
// Shows the featured image of your choice with`$thumbnail_id`
the_post_thumbnail()
endwhile;
Get the $thumbnail_id of the image you want to set from "Media Library" and hovering/pressing Edit button .

The best way to display Pod based images in your template file

I am relatively new to the WordPress CMS and decided to use Pods for my custom field implementations including several image fields. While I love the admin UI I was a bit flustered trying output the images in my post template file.
After a lot of research and experimenting I wanted to share the technique I used. Obviously if there is a better way I'd love to know.
The first thing I learned from the Pods forum was that Pods saves images to the database as 'Attachment' posts. So, they can be accessed as you would access any regular old WordPress attachment.
Attachments have a parent-child relationship with their post which means you can programmatically grab all the attachments for a given post using this snippet adapted from WP beginners:
<?php
if ( $post->post_type == 'post-type' && $post->post_status == 'publish' ) {
$attachments = get_posts( array(
'post_type' => 'attachment',
'posts_per_page' => -1,
'post_parent' => $post->ID,
'exclude' => get_post_thumbnail_id()
) );
if ( $attachments ) {
foreach ( $attachments as $attachment ) {
$class = "post-attachment mime-" . sanitize_title( $attachment->post_mime_type );
$thumbimg = wp_get_attachment_image( $attachment->ID, 'thumbnail');
echo '<li class="' . $class . ' data-design-thumbnail">' . $thumbimg . '</li>';
}
}
}
?>
But this solution is sub-optimal because the parent-child relationship between the post and the image can only be broken if the image is deleted from the media library. So:
An image removed from it's original post but left in the library for future use will still be output by the above code on the original post
If you do re-purpose the image on a different post's Pod field the code above won't print it on the new entry.
The attachment system does not record field level relationships. So, if you have more than one image based Pod field on a Post the code above will print them all regardless if their field.
That said, I have found the best option for outputting Pod based image data by field is to combine the 'get_post_meta' function outlined here on the WordPress support forums with the 'wp_get_attachment_image' function as shown below.
<?php
if ( get_post_meta( get_the_ID(), 'image_field', false ) ){
$image_array = get_post_meta( get_the_ID(), 'image_field', false );
}
if ( $image_array ) {
echo '<ul>';
foreach ( $image_array as $image ) {
$class = "post-attachment mime-" . sanitize_title( $image->post_mime_type );
$thumbimg = wp_get_attachment_image( $image['ID'], 'thumbnail');
echo '<li class="' . $class . ' data-design-thumbnail">' . $thumbimg . '</li>';
}
echo '</ul>';
}
?>
The former function gives you an object with only the current images. The latter renders those images with size and alt info limited to the attachments system.

Custom Fields/Values displaying out of order

I'm using the Types Wordpress plugin to enable custom fields. The plugin allows you to rearrange the order of the images in the admin page editor. Here's my code in my single.php to display multiple images in the custom field and have a link to itself to also use Fancybox:
<?php
$mykey_values_img = get_post_custom_values('wpcf-image');
if ($mykey_values_img != null) {
foreach ( $mykey_values_img as $key => $value ) {
?>
<img src="<?php echo $value; ?>" />
<?php
} //foreach
} //if
?>
Problem:
Right now this code works perfectly on my local copy running on MAMP. However, when I put it online hosted on iPage, the images are out of order. I don't know what's causing this discrepancy. When I use the shortcode from Types to display the images instead of the php above they are in order, but I don't have the option of using Fancybox. I was also wondering if there is a better way to display an image from Wordpress that will insert the alt tag for the image.
I just encountered this problem too, and your first answer led me to a tighter solution.
I also used types_render_field(), but if you include a 'raw' parameter, you can avoid the string manipulation.
$images_raw = types_render_field('image', array('raw'=>'true','separator'=>';'));
$images = explode(';', $images_raw);
foreach ($images as $link) {
echo '' . $link . '">"';
}
Then, if you're nasty, you can get the ID of the attachment from its SRC. Using that ID, you can get all the info you need about that attachment, like a caption, or whatnot.
I figured out a work around to get it working. Its not ideal but it works.
The Types plugin came with its own php function to display the custom field called types_render_field. It displayed my images in order. To get fancybox working I had to do sort of a hack on the string. Here's the code:
$images = ( types_render_field( "image", array( 'size' => 'full', 'alt' => get_the_title(), 'title' => get_the_title() ) ) );
$imgArray = explode(">", $images);
foreach ( $imgArray as $value ) {
$pos1 = strpos($value, 'src="', 0)+5;
$pos2 = strpos($value, '" ', $pos1);
$link = substr($value, $pos1, $pos2 - $pos1);
echo ''.$value.">";
}

Only show images/attachments within the gallery?

When i create a page, add a gallery, and browse this gallery on the front-end it will browse all the attachments that are associated with that page, instead of just the images within that gallery. Is there a way to filter all the other attachments and only show the images within a certain gallery? So that, for instance, when I delete the gallery and add a new gallery on the same page > only the new gallery is shown?
Any ideas?
This might not be the most elegant way, but i've found it very usefull.
Passing a post ID to the function below will load a gallery from the post_content of that post. So you would create a gallery and insert it into your post content, then in the template you run this function and will be returned with an array of attachments in that gallery which you are free to to whatever with, i.e slideshows and the likes.
function wp_load_gallery($post_id) {
$post = get_post( $post_id );
$regx = '/' . get_shortcode_regex() . '/';
preg_match( $regx, $post->post_content, $matches );
$ids = shortcode_parse_atts( $matches[3] );
$gallery = array( );
foreach( explode( ',', $ids['ids'] ) as $id ) {
if($id) {
$gallery[] = get_post( $id );
}
}
return $gallery;
}
Note that the shortcode is not cut from the content, so when you display the content you should run it through the strip_shortcodes function, i.e:
echo strip_shortcodes( get_the_content() );
This allows you to update the gallery whenever you want with whatever you want.
EDIT:
To simply display all images:
$gallery = wp_load_gallery($YOUR_POST_ID);
foreach($gallery as $image) {
echo wp_get_attachment_image($image->ID);
}

Setting body id for custom post types in Wordpress

I have developed a function in functions.php to add a specific id to the body tag depending on the page template or the parent page template if it's a child page.
function get_body_id() {
global $post;
$parents = get_post_ancestors( $post->ID );
$parent_id = ($parents) ? $parents[count($parents)-1]: $post->ID;
$temp = get_page_template_slug($parent_id);
$path = pathinfo($temp);
$temp = $path['filename'];
switch ( $temp ):
case 'page-recipe-landing':
$id = 'bg-green';
break;
case 'page-home':
default:
$id = 'bg-pink';
break;
endswitch;
echo "id='".$id."'";
}
This works well for pages and their children but for my custom post types events/news and recipes this obviously will not return anything for get_post_ancestors( $post->ID ).
Is there a way to alter my function to cater for these custom post types or to assign custom post types to a specific 'page'?
Thanks for help with this.
<?php
if(empty($parents)) {
$loop = new WP_Query(array('post_type' => 'NAME_OF_TYPE', 'post_status' => 'publish'));
while($loop->have_posts()): $loop->the_post();
the_ID();
endwhile;
}
?>
This code should at least output the ID of each post with a custom type of NAME_OF_TYPE. If you wanted to store those in an array and work with them from there, the premise would be pretty much the same. I haven't tested the code, but it should work with custom post types.

Resources