I want to require that Wordpress editors must add alt text to images.
The site uses the classic editor and images are added into the content editor. I have chosen to do this in php by checking if images have alt text when saving the post. If all images do not have alt tags, then prevent the post from being published and show a message in the admin.
Here is the code I have. My problem is that the code to return the alt text always returns an empty string. Can someone help me pin down what I'm doing wrong? Also, not averse to a different approach altogether...
add_action('save_post', 'check_content_for_images');
function check_content_for_images($post_id) {
$attachments = get_children( array(
'post_parent' => $post_id,
'post_type' => 'attachment',
'numberposts' => -1, // show all -1
'post_status' => 'inherit',
'post_mime_type' => 'image',
) );
// loop through images and check that it has an alt attribute
$counter = 0;
foreach ($attachments as $attachment_id => $attachment) {
// $img = wp_get_attachment_image($attachment_id, 'full');
$alt_text = get_post_meta($attachment_id, '_wp_attachment_image_alt', true);
if(empty($alt_text)) {
$counter++;
}
}
if ( count($attachments) > $counter) {
// set a transient to show the users an admin message
set_transient( "post_not_ok", "not_ok" );
// // unhook this function so it doesn't loop infinitely
remove_action('save_post', 'check_content_for_images');
// // update the post set it to draft
wp_update_post(array('ID' => $post_id, 'post_status' => 'draft'));
// // re-hook this function
add_action('save_post', 'check_content_for_images');
} else {
delete_transient( "post_not_ok" );
}
}
/**
* Do not show post published message
*/
add_filter( 'post_updated_messages', 'abc_remove_all_messages' );
function abc_remove_all_messages( $messages ) {
if ( get_option( 'abc_post_error' ) ) {
return array();
} else {
return $messages;
}
}
// add a admin message to inform the user the post has been saved as draft.
function showAdminMessages()
{
// check if the transient is set, and display the error message
if ( get_transient( "post_not_ok" ) == "not_ok" ) {
// Shows as an error message. You could add a link to the right page if you wanted.
showMessage("One or more of the images included in this post is missing ALT text. Please ensure all images have ALT text", true);
// delete the transient to make sure it gets shown only once.
delete_transient( "post_not_ok" );
}
}
add_action('admin_notices', 'showAdminMessages');
// function to display the errormessage
function showMessage($message, $errormsg = false)
{
if ($errormsg) {
echo '<div id="message" class="error">';
}
else {
echo '<div id="message" class="updated fade">';
}
echo "<p><strong>$message</strong></p></div>";
}
Related
I have 2 different working methods at functions.php for backend. Each method below has 2 hooks; 1 to display the new custom field & another hook to save\update the values:
Method 1:
function media_hacks_attachment_field_to_edit( $form_fields, $post ){
// https://codex.wordpress.org/Function_Reference/wp_get_attachment_metadata
$media_author = get_post_meta( $post->ID, 'media_author', true );
$form_fields['media_author'] = array(
'value' => $media_author ? $media_author : '',
'label' => __( 'Author' )
);
return $form_fields;
}
add_filter( 'attachment_fields_to_edit', 'media_hacks_attachment_field_to_edit', null, 2 );
//Saving value on Update (method 1)
function media_hacks_edit_attachment( $attachment_id ){
if ( isset( $_REQUEST['attachments'][$attachment_id]['media_author'] ) ) {
$media_author = $_REQUEST['attachments'][$attachment_id]['media_author'];
update_post_meta( $attachment_id, 'media_author', $media_author );
}
}
add_action( 'edit_attachment', 'media_hacks_edit_attachment' );
Method 2:
function my_image_attachment_fields_to_edit($form_fields, $post) {
// $form_fields is a special array of fields to include in the attachment form
// $post is the attachment record in the database
// $post->post_type == 'attachment'
// (attachments are treated as posts in Wordpress)
// add our custom field to the $form_fields array
// input type="text" name/id="attachments[$attachment->ID][custom1]"
$form_fields["custom1"] = array(
"label" => __("Custom Text Field"),
"input" => "text", // this is default if "input" is omitted
"value" => get_post_meta($post->ID, "_custom1", true)
);
// if you will be adding error messages for your field,
// then in order to not overwrite them, as they are pre-attached
// to this array, you would need to set the field up like this:
$form_fields["custom1"]["label"] = __("Custom Text Field");
$form_fields["custom1"]["input"] = "text";
$form_fields["custom1"]["value"] = get_post_meta($post->ID, "_custom1", true);
return $form_fields;
}
// attach our function to the correct hook
add_filter("attachment_fields_to_edit", "my_image_attachment_fields_to_edit", null, 2);
//Saving value on Update (method 2)
function my_image_attachment_fields_to_save($post, $attachment) {
// $attachment part of the form $_POST ($_POST[attachments][postID])
// $post attachments wp post array - will be saved after returned
// $post['post_type'] == 'attachment'
if( isset($attachment['custom1']) ){
// update_post_meta(postID, meta_key, meta_value);
update_post_meta($post['ID'], '_custom1', $attachment['custom1']);
}
return $post;
}
add_filter("attachment_fields_to_save", "my_image_attachment_fields_to_save", null, 2);
Here's the good result at backend Media Library (Custom Text Field & Author):
This was it for the Backend dashboard.
My question is for the Frontend:
Now how can I retrieve & display values of these 2 custom fields at the FRONTEND?
Here's my failed try at a template php page:
<tr id='MySpecialRow'>
<td colspan='2' style='background:#000;color:#fff;'>
<?php
$args = array('cat' => 8);
$query = new WP_Query($args);
if ($query->have_posts()) {
// some code here if you want.
while ($query->have_posts()) {
$query->the_post();
$untitled_meta = rwmb_meta('image_advanced_8hswqfsoqai', '', get_the_ID());
foreach ($untitled_meta as $image) {
$media_author = get_post_meta( get_the_ID(), 'media_author', true );
echo get_the_ID();//correctly prints post id
echo $media_author;//prints nothing :(
}
}
}
?>
</td>
</tr>
Small notes:
get_the_ID() does print the post id, but $media_author has no value :(
I'm doing a WordPress posts query loop because the gallery containing the custom fields exists in a Post. In other words I don't have the post Id since I'm at a Page template.
The array you got has the image post object ID as the array keys, so you need to use the extended foreach syntax to get access to the key as well.
foreach ($untitled_meta as $id => $image) {
$media_author = get_post_meta( $id, 'media_author', true );
Normally when looping over array data you rather seldom need access to the key as well, but when you do, PHP offers the $key => $value syntax to get access to the key as well, https://www.php.net/manual/en/control-structures.foreach.php
I'm new to WordPress (duh) and trying something I'm very close to getting.
I have several Sub-Pages, each of which has attachments - images. I simply want to display these images. The client will regularly be adding more subpages so the code has to know to only display the images of its self, so to speak. An issue I'm running into is each subpage is displaying all the attachments of all subpages.
In my Functions.php I have this code to determine if the page is infact a subpage:
//find out if it is a suppage to use in page.php as function 'is_subpage'
function is_subpage() {
global $post; // load details about this page
if ( is_page() && $post->post_parent ) { // test to see if the page has a parent
return $post->post_parent; // return the ID of the parent post
} else { // there is no parent so ...
return false; // ... the answer to the question is false
}
}
In my page.php I have an If statement, for all my usual pages. It culminates in this:
elseif ( is_subpage() ) {
get_template_part( 'exhibition-template' );
get_template_part( 'normalfooter' );
}
In exhibition-template.php, where I want my images to appear I have this:
<?php
if ( is_subpage( $post->ID ) ) { //I think I screwed up here, innermost brackets
$args = array(
'order' => 'ASC',
'post_type' => 'attachment',
'post_parent' => $post->ID, //or is my issue here...?
'post_mime_type' => 'image',
'post_status' => null,
'numberposts' => -1,
);
$attachments = get_posts($args);
if ($attachments) {
foreach ($attachments as $attachment) {
echo wp_get_attachment_link($attachment->ID, 'large', false, false);
}
}
}
?>
However, what happens is each and every subpage simply displays all the images across all subpages, and frankly, I'm just too DUMB to know how to progress, or what to play with, where to start.
Any ideas?
It looks like you have a few issues.
When testing to see if a page is a subpage you should test to see if $post->post_parent is greater than 0. So like this:
if ( is_page() && $post->post_parent > 0 ) {
return $post->post_parent;
} else {
return false;
}
You do not need to pass any arguments into if ( is_subpage( $post->ID ) )... You could also just use WordPress' get_attached_media() function to get these attachements as well. This is how your exhibition-template.php could look:
if ( is_subpage() ) {
$attachments = get_attached_media('image', $post->ID);
foreach ($attachments as $attachment) {
echo wp_get_attachment_link($attachment->ID, 'large', false, false);
}
}
What I want to do
I have a website that uses a CMS I wrote some time ago, and now I am trying to migrate it to wordpress.
At the existing implementation, when someone writes a post, they can add some extra images that are shown as a gallery at the end of the post, as you can see in this page for example (sorry for non english page): http://apollonpatras.gr/news/562/i-bradia-ton-xorigon-parousiasi-xorigikou-programmatos-kai-eisitirion-diarkeias/.
How I think I can do it
I am thinking about letting the users create wordpress galleries and at post save time intercept the post contents and store the gallery image ids in a postmeta field so I can show them however I want.
Also, I will have to strip the galleries from the content before they are shown, since I will show them in my own way later.
What I am trying so far
add_filter('content_save_pre', 'intercept_galleries', 99);
function intercept_galleries($content) {
if (get_post_type() !== 'post') {
return $content;
}
if (has_shortcode($content, 'gallery')) {
// The [gallery] short code exists.
$a = get_post_gallery(0, false);
update_post_meta(get_the_ID(), 'has_gallery', 1);
update_post_meta(get_the_ID(), 'gallery_items', $a['ids']);
} else {
update_post_meta(get_the_ID(), 'has_gallery', 0);
update_post_meta(get_the_ID(), 'gallery_items', "");
}
return $content;
}
add_filter('the_content', 'remove_shortcodes_from_content');
function remove_shortcodes_from_content($content) {
return strip_shortcodes($content);
}
Where it goes wrong
Looks like when the post is originally saved, the postmeta field "has_gallery" is set to 1, but the field "gallery_items" is empty.
When I go to the wordpress editor and just hit update, the fields are absolutely correct.
Also the hook to remove shortcodes from the content is working.
What am I looking for
How can I fix this problem? Also, is there something wrong/stupid with the way I decided to do this? Would some other way be cleaner/easier/faster etc?
Thank you for your time
I've done this a few times and here's how I do it.
First I create a function that will display the gallery in the way that I want. You can modify this according to how you require you gallery markup to be:
function my_gallery_shortcode( $attr ) {
$post = get_post();
if ( ! empty( $attr['ids'] ) ) {
$attr['include'] = $attr['ids'];
}
extract( shortcode_atts( array(
'order' => 'ASC',
'orderby' => 'post__in',
'id' => $post->ID,
'columns' => 3,
'size' => 'large',
'include' => '',
), $attr));
$id = (int) $id;
$columns = (int) $columns;
if ( 'RAND' == $order ) {
$orderby = 'none';
}
if ( ! empty( $include ) ) {
$_attachments = get_posts( array( 'include' => $include, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $order, 'orderby' => $orderby ) );
$attachments = array();
foreach ( $_attachments as $key => $val ) {
$attachments[$val->ID] = $_attachments[$key];
}
}
if ( empty( $attachments ) ) {
return '';
}
$output = '<div class="slideshow"><ul>';
foreach ( $attachments as $id => $attachment ) {
$thumb = wp_get_attachment_image_src( $id, 'large', false );
$output .= '<li><img src="' . $thumb[0] . '" width="' . $thumb[1] . '" height="' . $thumb[2] . '" alt="' . get_post_meta( $id, '_wp_attachment_image_alt', true ) . '" /></li>';
}
$output .= '</ul></div>';
return $output;
}
You can complicate or simply the function above according to your requirements.
Then in your theme's functions.php add this:
add_shortcode( 'gallery', 'my_gallery_shortcode' );
Now you have two choices:
1) You can allow your user to add a gallery to the main page content by way of them editing the page in question and going to Media > Create Gallery
This will insert the gallery short code which will be formatted according to your function my_gallery_shortcode(), however the gallery can be managed via WordPress's gallery functionality in the admin area and is stored in the database by WordPress in the conventional way.
or
2) You could create a separate WYSIWYG field either through additional code in your functions.php file, or by using a plugin such as Advanced Custom Fields. You would then use this additional WYSIWYG field to allow the user to insert the gallery shortcode in the same way as above. This is virtually the same as option 1 above but you'd have more flexibility as to where you output and position the gallery on the page.
I hope this helps anyone looking to do the same thing.
Here's my situation:
I'm redesigning an existing Wordpress site.
The new design separates all images from the actual post content and puts it in a Featured Gallery.
Currently, each post has your typical inline images in the post content.
Is this even remotely possible to extract all inline images from the post content, and create Featured Galleries with those images for each post?
In the past I've done something like the below to grab the first image in the post content, and set it as the standard featured image, but nothing like what I have to do with this dilemma.
function wpforce_featured() {
global $post;
$already_has_thumb = has_post_thumbnail($post->ID);
if (!$already_has_thumb) {
$attached_image = get_children( "post_parent=$post->ID&post_type=attachment&post_mime_type=image&numberposts=1" );
if ($attached_image) {
foreach ($attached_image as $attachment_id => $attachment) {
set_post_thumbnail($post->ID, $attachment_id);
}
}
}
} //end function
add_action('the_post', 'wpforce_featured');
add_action('save_post', 'wpforce_featured');
add_action('draft_to_publish', 'wpforce_featured');
add_action('new_to_publish', 'wpforce_featured');
add_action('pending_to_publish', 'wpforce_featured');
add_action('future_to_publish', 'wpforce_featured');
Basically the method I used was to create a one time script that I ran to move all inline images from the post to the Featured Gallery, like this:
// Loop over every post
while ( have_posts() ) : the_post();
// Get the images attached to the post.
$imageids = array();
$images = get_attached_media( 'image', $post->ID );
foreach ($images as $image) {
$imageids[] = $image->ID;
}
$comma_separated = implode(",", $imageids);
// Save them to the Featured Gallery (Got this info by digging into the Featured Gallery plugin's source code)
if ( $post->post_type == 'revision' ) {return;}
if ( get_post_meta( $post->ID, 'fg_perm_metadata', FALSE ) ) {
update_post_meta( $post->ID, 'fg_perm_metadata', $comma_separated );
} else {
add_post_meta( $post->ID, 'fg_perm_metadata', $comma_separated );
}
if ( !$comma_separated ) delete_post_meta( $post->ID, 'fg_perm_metadata' );
endwhile;
// Reset Query
wp_reset_query();
Then, I created a function that removes all images in the post content, which I placed in functions.php:
function remove_images( $content )
{
$content =
preg_replace(
array('{<a(.*?)(wp-att|wp-content\/uploads)[^>]*><img}',
'{ wp-image-[0-9]*" /></a>}'),
array('<img','" />'),
$content
);
$content = preg_replace('/<img(.*)>/i','', $content, 1);
return $content;
}
add_filter( 'the_content', 'remove_images' );
First preg_replace removes the link around the images. Second removes the image.
the problem is if i read you correctly is that all the images are located as urls in the post content?
You can parse them:
$content = get_the_content();
$html = new DOMDocument;
$html->loadHTML($content);
//get the images
$images = $html->getElementsByTagName('img');
foreach($images as $image=>$key {
$imageurls[]=$key->attributes->getNamedItem("src")->value;//play with this cant remember how it returns object.
}
$content= preg_replace('/<img(.*)>/i','',$content,1);
echo $content;
// you also have a array of images to use.
I found a really helpful discussion here that outlines how to display the image caption, alt, title, etc. I've tested this function, and it works well, but for the images that don't have captions, the div for the caption still displays. How would I go about making this function display nothing, if there's no caption available?
function the_post_thumbnail_caption() {
global $post;
$thumb_id = get_post_thumbnail_id($post->id);
$args = array(
'post_type' => 'attachment',
'post_status' => null,
'post_parent' => $post->ID,
'include' => $thumb_id
);
$thumbnail_image = get_posts($args);
if ($thumbnail_image && isset($thumbnail_image[0])) {
//show thumbnail title
echo $thumbnail_image[0]->post_title;
//Uncomment to show the thumbnail caption
//echo $thumbnail_image[0]->post_excerpt;
//Uncomment to show the thumbnail description
//echo $thumbnail_image[0]->post_content;
//Uncomment to show the thumbnail alt field
//$alt = get_post_meta($thumbnail_id, '_wp_attachment_image_alt', true);
//if(count($alt)) echo $alt;
}
}
if ($thumbnail_image[0]->post_excerpt != '')
{
echo $thumbnail_image[0]->post_excerpt;
}