Last active
November 14, 2025 04:08
-
-
Save abidkakkur11/def35516d3764ad8f53ce8b839b455cb to your computer and use it in GitHub Desktop.
This snippet adds an "Update SEO Details button" in all media modals, which update SEO alt and description from title - remove special characters and numbers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * Media SEO Updater — Inline JS + AJAX save (single snippet) | |
| * Paste into Code Snippets or theme/plugin. Runs in admin. | |
| */ | |
| add_action( 'admin_enqueue_scripts', function() { | |
| // Ensure media scripts are available | |
| if ( function_exists( 'wp_enqueue_media' ) ) { | |
| wp_enqueue_media(); | |
| } | |
| // Print inline JS in footer of admin pages | |
| add_action( 'admin_print_footer_scripts', function() { | |
| $ajax_url = admin_url( 'admin-ajax.php' ); | |
| $nonce = wp_create_nonce( 'msu-save-nonce' ); | |
| ?> | |
| <script> | |
| (function($){ | |
| 'use strict'; | |
| if (!window.wp || !wp.media || !wp.media.view) return; | |
| // helper: show admin notice style toast (simple) | |
| function msuNotify(msg){ | |
| // fallback to alert if not desired | |
| try { | |
| // small unobtrusive message | |
| var $n = $('<div>').text(msg).css({ | |
| position: 'fixed', | |
| right: '20px', | |
| bottom: '20px', | |
| 'z-index': 999999, | |
| padding: '8px 12px', | |
| 'background-color': '#0073aa', | |
| color: '#fff', | |
| 'border-radius': '4px', | |
| 'box-shadow': '0 2px 6px rgba(0,0,0,0.2)' | |
| }).appendTo('body').delay(2200).fadeOut(400, function(){ $(this).remove(); }); | |
| } catch(e){ | |
| alert(msg); | |
| } | |
| } | |
| function injectButton(view){ | |
| if (!view || !view.$el || view.$el.find('.msu-update-seo-btn').length) return; | |
| var $btn = $('<button type="button" class="button button-primary msu-update-seo-btn" style="margin-top:10px; margin-left:8px;">Update SEO Details</button>'); | |
| $btn.on('click', function(e){ | |
| e.preventDefault(); | |
| var model = view.model; | |
| if (!model || !model.get) { | |
| msuNotify('No attachment data found.'); | |
| return; | |
| } | |
| var attachmentId = model.get('id') || model.get('ID') || 0; | |
| if (!attachmentId) { | |
| msuNotify('Attachment ID not found.'); | |
| return; | |
| } | |
| // Read current title from model (prefer model data) | |
| var title = String( model.get('title') || '' ); | |
| // Clean title: remove numbers & special chars, keep letters & spaces | |
| var clean = title.replace(/[^a-zA-Z\s]/g,' ').replace(/\s+/g,' ').trim(); | |
| // Update model (so modal fields update) | |
| model.set({ | |
| alt: clean, | |
| caption: clean, | |
| description: clean | |
| }); | |
| // Prepare payload for AJAX save | |
| var data = { | |
| action: 'msu_save_attachment', | |
| _ajax_nonce: '<?php echo $nonce; ?>', | |
| attachment_id: attachmentId, | |
| title: title, // keep original title for post_title | |
| alt: clean, | |
| caption: clean, | |
| description: clean | |
| }; | |
| // disable button while saving | |
| $btn.prop('disabled', true).text('Saving...'); | |
| $.post('<?php echo $ajax_url; ?>', data) | |
| .done(function(res){ | |
| $btn.prop('disabled', false).text('Update SEO Details'); | |
| if (res && res.success) { | |
| msuNotify('Saved ✓'); | |
| // sync returned values into model (in case WP made changes) | |
| if (res.data) { | |
| model.set({ | |
| title: res.data.title || model.get('title'), | |
| alt: res.data.alt || model.get('alt'), | |
| caption: res.data.caption || model.get('caption'), | |
| description: res.data.description || model.get('description') | |
| }); | |
| } | |
| } else { | |
| var msg = (res && res.data && res.data.message) ? res.data.message : 'Save failed'; | |
| msuNotify(msg); | |
| console.error('MSU save error', res); | |
| } | |
| }) | |
| .fail(function(xhr, status, err){ | |
| $btn.prop('disabled', false).text('Update SEO Details'); | |
| msuNotify('Request failed: ' + (xhr.status || status)); | |
| console.error('MSU AJAX failure', xhr, status, err); | |
| }); | |
| }); | |
| // Append to sensible locations in modal | |
| var $target = view.$el.find('.details, .attachment-info, .attachment-actions'); | |
| if ($target.length) { | |
| $target.first().append($btn); | |
| } else { | |
| view.$el.append($btn); | |
| } | |
| } | |
| // Attach to multiple modal view variants (robust) | |
| var targets = [ | |
| wp.media.view.Attachment.Details, | |
| (wp.media.view.Attachment.Details && wp.media.view.Attachment.Details.TwoColumn) ? wp.media.view.Attachment.Details.TwoColumn : null, | |
| wp.media.view.Attachment.Details.Sidebar | |
| ]; | |
| targets.forEach(function(ViewType){ | |
| if (!ViewType || !ViewType.prototype) return; | |
| var orig = ViewType.prototype.render; | |
| ViewType.prototype.render = function(){ | |
| var result = orig.apply(this, arguments); | |
| injectButton(this); | |
| return result; | |
| }; | |
| }); | |
| })(jQuery); | |
| </script> | |
| <?php | |
| }, 20 ); // footer scripts | |
| }); | |
| /** | |
| * AJAX handler: persist attachment fields to DB | |
| */ | |
| add_action( 'wp_ajax_msu_save_attachment', function() { | |
| // nonce check | |
| if ( ! isset( $_POST['_ajax_nonce'] ) || ! wp_verify_nonce( $_POST['_ajax_nonce'], 'msu-save-nonce' ) ) { | |
| wp_send_json_error( array( 'message' => 'Invalid nonce' ), 400 ); | |
| } | |
| $attachment_id = isset( $_POST['attachment_id'] ) ? intval( $_POST['attachment_id'] ) : 0; | |
| if ( ! $attachment_id ) { | |
| wp_send_json_error( array( 'message' => 'No attachment ID' ), 400 ); | |
| } | |
| // capability check: current user must be able to edit the attachment | |
| if ( ! current_user_can( 'edit_post', $attachment_id ) ) { | |
| wp_send_json_error( array( 'message' => 'Insufficient permissions' ), 403 ); | |
| } | |
| // Sanitize inputs | |
| $title = isset( $_POST['title'] ) ? wp_kses_post( wp_unslash( $_POST['title'] ) ) : ''; | |
| $alt = isset( $_POST['alt'] ) ? sanitize_text_field( wp_unslash( $_POST['alt'] ) ) : ''; | |
| $caption = isset( $_POST['caption'] ) ? wp_kses_post( wp_unslash( $_POST['caption'] ) ) : ''; | |
| $description = isset( $_POST['description'] ) ? wp_kses_post( wp_unslash( $_POST['description'] ) ) : ''; | |
| // Update post fields: post_title, post_excerpt (caption), post_content (description) | |
| $update = array( | |
| 'ID' => $attachment_id, | |
| 'post_title' => $title, | |
| 'post_excerpt' => $caption, | |
| 'post_content' => $description, | |
| ); | |
| $result = wp_update_post( $update, true ); | |
| if ( is_wp_error( $result ) ) { | |
| wp_send_json_error( array( 'message' => 'Could not update attachment' ), 500 ); | |
| } | |
| // Update alt meta | |
| update_post_meta( $attachment_id, '_wp_attachment_image_alt', $alt ); | |
| // Return saved values (sanitized) | |
| $saved = array( | |
| 'title' => get_the_title( $attachment_id ), | |
| 'alt' => get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ), | |
| 'caption' => wp_get_attachment_caption( $attachment_id ), | |
| 'description' => get_post_field( 'post_content', $attachment_id ), | |
| ); | |
| wp_send_json_success( $saved ); | |
| } ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment