Plugin Directory

Changeset 3478245


Ignore:
Timestamp:
03/09/2026 03:02:26 PM (4 weeks ago)
Author:
a1tools
Message:

v1.10.0: Unified Save + Delete, full-width fields, merged rename into save

Location:
a1-tools/trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • a1-tools/trunk/a1-tools.php

    r3478238 r3478245  
    44 * Plugin URI:        https://tools.a-1chimney.com
    55 * Description:       Connects your WordPress site to the A1 Tools platform for centralized management of contact information, social media links, and business details.
    6  * Version:           1.9.9
     6 * Version:           1.10.0
    77 * Requires at least: 5.0
    88 * Requires PHP:      7.4
     
    2121
    2222// Plugin constants.
    23 define( 'A1TOOLS_VERSION', '1.9.9' );
     23define( 'A1TOOLS_VERSION', '1.10.0' );
    2424define( 'A1TOOLS_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
    2525define( 'A1TOOLS_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
  • a1-tools/trunk/includes/class-a1-tools-media-management.php

    r3478238 r3478245  
    4242        add_action( 'wp_ajax_a1tools_find_duplicates',  array( $this, 'ajax_find_duplicates' ) );
    4343        add_action( 'wp_ajax_a1tools_scan_unused',      array( $this, 'ajax_scan_unused' ) );
     44        add_action( 'wp_ajax_a1tools_delete_media',     array( $this, 'ajax_delete_media' ) );
    4445        add_action( 'admin_enqueue_scripts',            array( $this, 'enqueue_admin_assets' ) );
    4546    }
     
    7071            'nonceDuplicates' => wp_create_nonce( 'a1tools_find_duplicates' ),
    7172            'nonceUnused'     => wp_create_nonce( 'a1tools_scan_unused' ),
     73            'nonceDelete'     => wp_create_nonce( 'a1tools_delete_media' ),
    7274        ) );
    7375
     
    589591
    590592    // =========================================================================
     593    // AJAX: Delete Media
     594    // =========================================================================
     595
     596    public function ajax_delete_media() {
     597        check_ajax_referer( 'a1tools_delete_media', 'nonce' );
     598        if ( ! current_user_can( 'delete_posts' ) ) {
     599            wp_send_json_error( array( 'message' => 'Permission denied.' ) );
     600        }
     601
     602        $id = absint( $_POST['attachment_id'] ?? 0 );
     603        if ( ! $id ) {
     604            wp_send_json_error( array( 'message' => 'Invalid attachment ID.' ) );
     605        }
     606
     607        $post = get_post( $id );
     608        if ( ! $post || 'attachment' !== $post->post_type ) {
     609            wp_send_json_error( array( 'message' => 'Attachment not found.' ) );
     610        }
     611
     612        $deleted = wp_delete_attachment( $id, true );
     613        if ( ! $deleted ) {
     614            wp_send_json_error( array( 'message' => 'Could not delete the file.' ) );
     615        }
     616
     617        wp_send_json_success( array( 'message' => 'File deleted.', 'id' => $id ) );
     618    }
     619
     620    // =========================================================================
    591621    // Rename Logic (preserved from v1)
    592622    // =========================================================================
     
    906936        .a1mm-field-input-wrap { flex:1; display:flex; align-items:center; gap:4px; flex-wrap:wrap; min-width:0; }
    907937
    908         /* Rename Row */
    909         .a1mm-rename-row { display:flex; align-items:center; gap:4px; flex-wrap:wrap; }
    910         .a1mm-new-name { flex:1; min-width:180px; max-width:420px; padding:5px 8px; font-size:13px; font-family:monospace; }
    911         .a1mm-ext { font-size:13px; color:#666; font-family:monospace; margin-right:6px; }
    912         .a1mm-refs-label { font-size:12px; color:#555; margin-right:6px; cursor:pointer; display:inline-flex; align-items:center; gap:3px; }
     938        /* Ext label */
     939        .a1mm-ext { font-size:13px; color:#666; font-family:monospace; flex-shrink:0; }
     940
     941        /* Meta Inputs — full width */
     942        .a1mm-meta-input { flex:1; min-width:0; padding:5px 8px; font-size:13px; }
     943        .a1mm-new-name { font-family:monospace; }
     944        textarea.a1mm-meta-input { resize:vertical; font-family:inherit; }
     945
     946        /* Actions Row */
     947        .a1mm-actions-row { gap:8px !important; }
     948        .a1mm-refs-label { font-size:12px; color:#555; cursor:pointer; display:inline-flex; align-items:center; gap:3px; margin-right:4px; }
    913949        .a1mm-item-msg { font-size:12px; margin-left:4px; }
    914950        .a1mm-item-msg.success { color:#00a32a; }
    915951        .a1mm-item-msg.error { color:#d63638; }
    916         .a1mm-undo-link { font-size:12px; color:#2271b1; cursor:pointer; margin-left:6px; text-decoration:underline; }
     952        .a1mm-undo-link { font-size:12px; color:#2271b1; cursor:pointer; margin-left:4px; text-decoration:underline; }
    917953        .a1mm-undo-link:hover { color:#135e96; }
    918 
    919         /* Meta Inputs */
    920         .a1mm-meta-input { flex:1; min-width:180px; max-width:420px; padding:4px 8px; font-size:13px; }
    921         textarea.a1mm-meta-input { resize:vertical; font-family:inherit; }
    922         .a1mm-meta-msg { font-size:12px; margin-left:8px; }
     954        .a1mm-delete-btn { color:#b32d2e !important; border-color:#b32d2e !important; }
     955        .a1mm-delete-btn:hover { background:#b32d2e !important; color:#fff !important; }
     956        .a1mm-meta-msg { font-size:12px; margin-left:4px; }
    923957        .a1mm-meta-msg.success { color:#00a32a; }
    924958        .a1mm-meta-msg.error { color:#d63638; }
     
    10471081        + '<div class="a1mm-field-row">'
    10481082        +   '<span class="a1mm-field-label">File Name:</span>'
    1049         +   '<div class="a1mm-field-input-wrap a1mm-rename-row">'
    1050         +     '<input type="text" class="a1mm-new-name" value="'+escA(noExt)+'" />'
    1051         +     '<span class="a1mm-ext">.'+esc(ext)+'</span>'
    1052         +     '<label class="a1mm-refs-label"><input type="checkbox" class="a1mm-refs-cb" checked /> Update refs</label>'
    1053         +     '<button type="button" class="button button-primary a1mm-rename-btn">Rename</button>'
    1054         +     '<span class="a1mm-item-msg"></span>'
    1055         +   '</div>'
     1083        +   '<div class="a1mm-field-input-wrap"><input type="text" class="a1mm-meta-input a1mm-new-name" value="'+escA(noExt)+'" /><span class="a1mm-ext">.'+esc(ext)+'</span></div>'
    10561084        + '</div>'
    10571085        + '<div class="a1mm-field-row">'
    10581086        +   '<span class="a1mm-field-label">Alt Text:</span>'
    1059         +   '<div class="a1mm-field-input-wrap"><input type="text" class="a1mm-meta-input a1mm-alt-input" data-field="alt_text" value="'+escA(it.alt_text)+'" placeholder="Describe this image for SEO..." /></div>'
     1087        +   '<div class="a1mm-field-input-wrap"><input type="text" class="a1mm-meta-input a1mm-alt-input" value="'+escA(it.alt_text)+'" placeholder="Describe this image for SEO..." /></div>'
    10601088        + '</div>'
    10611089        + '<div class="a1mm-field-row">'
    10621090        +   '<span class="a1mm-field-label">Title:</span>'
    1063         +   '<div class="a1mm-field-input-wrap"><input type="text" class="a1mm-meta-input a1mm-title-input" data-field="title" value="'+escA(it.title)+'" /></div>'
     1091        +   '<div class="a1mm-field-input-wrap"><input type="text" class="a1mm-meta-input a1mm-title-input" value="'+escA(it.title)+'" /></div>'
    10641092        + '</div>'
    10651093        + '<div class="a1mm-field-row">'
    10661094        +   '<span class="a1mm-field-label">Caption:</span>'
    1067         +   '<div class="a1mm-field-input-wrap"><input type="text" class="a1mm-meta-input a1mm-caption-input" data-field="caption" value="'+escA(it.caption)+'" placeholder="Short caption..." /></div>'
     1095        +   '<div class="a1mm-field-input-wrap"><input type="text" class="a1mm-meta-input a1mm-caption-input" value="'+escA(it.caption)+'" placeholder="Short caption..." /></div>'
    10681096        + '</div>'
    10691097        + '<div class="a1mm-field-row">'
    10701098        +   '<span class="a1mm-field-label">Description:</span>'
    1071         +   '<div class="a1mm-field-input-wrap"><textarea class="a1mm-meta-input a1mm-desc-input" data-field="description" rows="2" placeholder="Longer description...">'+esc(it.description)+'</textarea></div>'
     1099        +   '<div class="a1mm-field-input-wrap"><textarea class="a1mm-meta-input a1mm-desc-input" rows="2" placeholder="Longer description...">'+esc(it.description)+'</textarea></div>'
    10721100        + '</div>'
    10731101        + '<div class="a1mm-field-row">'
    10741102        +   '<span class="a1mm-field-label"></span>'
    1075         +   '<div class="a1mm-field-input-wrap"><button type="button" class="button a1mm-save-meta-btn">Save Details</button><span class="a1mm-meta-msg"></span></div>'
     1103        +   '<div class="a1mm-field-input-wrap a1mm-actions-row">'
     1104        +     '<label class="a1mm-refs-label"><input type="checkbox" class="a1mm-refs-cb" checked /> Update refs</label>'
     1105        +     '<button type="button" class="button button-primary a1mm-save-meta-btn">Save</button>'
     1106        +     '<button type="button" class="button a1mm-delete-btn">Delete</button>'
     1107        +     '<span class="a1mm-item-msg"></span>'
     1108        +   '</div>'
    10761109        + '</div>';
    10771110
     
    11131146});
    11141147
    1115 // ---- Rename ----
    1116 function doRename($it) {
     1148// ---- Save (rename if changed + meta) ----
     1149$(document).on('click', '.a1mm-save-meta-btn', function() {
     1150    var $it = $(this).closest('.a1mm-item');
    11171151    var id = $it.data('id');
    1118     var nn = $it.find('.a1mm-new-name').val().trim();
     1152    var $b = $(this);
     1153    var $m = $it.find('.a1mm-item-msg');
     1154    var newName = $it.find('.a1mm-new-name').val().trim();
     1155    var curFile = $it.find('.a1mm-current code').text();
     1156    var curName = curFile.replace(/\.[^.]+$/,'');
    11191157    var ur = $it.find('.a1mm-refs-cb').is(':checked') ? 1 : 0;
    1120     var $b = $it.find('.a1mm-rename-btn');
    1121     var $m = $it.find('.a1mm-item-msg');
    1122     if (!nn) { $m.text('Name cannot be empty.').attr('class','a1mm-item-msg error'); return; }
    1123     $b.prop('disabled',true).text('Renaming...');
     1158    $b.prop('disabled',true).text('Saving...');
    11241159    $m.text('').attr('class','a1mm-item-msg');
    1125 
    1126     $.post(a1mm.ajax, {
    1127         action:'a1tools_rename_media', nonce:a1mm.nonceRename,
    1128         attachment_id:id, new_filename:nn, update_refs:ur
    1129     }, function(r) {
    1130         $b.prop('disabled',false).text('Rename');
    1131         if (r.success) {
    1132             var d=r.data;
    1133             var mt = 'Renamed to ' + d.new_filename;
    1134             if (ur && d.refs_updated > 0) mt += ' ('+d.refs_updated+' refs updated)';
    1135             $m.text(mt).attr('class','a1mm-item-msg success');
    1136             $it.addClass('a1mm-renamed');
    1137             $it.data('old-name', d.old_filename);
    1138             $it.find('.a1mm-current code').text(d.new_filename);
    1139             $it.find('.a1mm-new-name').val(d.new_filename.replace(/\.[^.]+$/,''));
    1140             if (d.new_thumbnail_url) $it.find('.a1mm-thumb img').attr('src', d.new_thumbnail_url);
    1141             $it.data('url', d.new_url);
    1142             // Show undo link
    1143             $m.after('<span class="a1mm-undo-link">Undo</span>');
    1144             // Update badges: remove "Needs rename" badge
    1145             $it.find('.a1mm-badge-random').remove();
    1146         } else {
    1147             $m.text(r.data.message||'Failed.').attr('class','a1mm-item-msg error');
    1148         }
    1149     }).fail(function() {
    1150         $b.prop('disabled',false).text('Rename');
    1151         $m.text('Network error.').attr('class','a1mm-item-msg error');
    1152     });
    1153 }
     1160    $it.find('.a1mm-undo-link').remove();
     1161
     1162    function saveMeta(msg) {
     1163        $.post(a1mm.ajax, {
     1164            action:'a1tools_update_media_meta', nonce:a1mm.nonceMeta,
     1165            attachment_id: id,
     1166            alt_text:    $it.find('.a1mm-alt-input').val(),
     1167            title:       $it.find('.a1mm-title-input').val(),
     1168            caption:     $it.find('.a1mm-caption-input').val(),
     1169            description: $it.find('.a1mm-desc-input').val()
     1170        }, function(r) {
     1171            $b.prop('disabled',false).text('Save');
     1172            if (r.success) {
     1173                var txt = msg || 'Saved!';
     1174                $m.text(txt).attr('class','a1mm-item-msg success');
     1175                if ($it.find('.a1mm-alt-input').val()) $it.find('.a1mm-badge-no-alt').remove();
     1176            } else {
     1177                $m.text(r.data.message||'Meta save failed.').attr('class','a1mm-item-msg error');
     1178            }
     1179        }).fail(function() {
     1180            $b.prop('disabled',false).text('Save');
     1181            $m.text('Network error.').attr('class','a1mm-item-msg error');
     1182        });
     1183    }
     1184
     1185    // Check if filename changed — rename first, then save meta.
     1186    if (newName && newName !== curName) {
     1187        $.post(a1mm.ajax, {
     1188            action:'a1tools_rename_media', nonce:a1mm.nonceRename,
     1189            attachment_id:id, new_filename:newName, update_refs:ur
     1190        }, function(r) {
     1191            if (r.success) {
     1192                var d = r.data;
     1193                $it.data('old-name', d.old_filename);
     1194                $it.find('.a1mm-current code').text(d.new_filename);
     1195                $it.find('.a1mm-new-name').val(d.new_filename.replace(/\.[^.]+$/,''));
     1196                if (d.new_thumbnail_url) $it.find('.a1mm-thumb img').attr('src', d.new_thumbnail_url);
     1197                $it.data('url', d.new_url);
     1198                $it.addClass('a1mm-renamed');
     1199                $it.find('.a1mm-badge-random').remove();
     1200                var mt = 'Renamed & saved!';
     1201                if (ur && d.refs_updated > 0) mt = 'Renamed (' + d.refs_updated + ' refs) & saved!';
     1202                saveMeta(mt);
     1203                // Show undo after msg
     1204                setTimeout(function(){
     1205                    $m.after('<span class="a1mm-undo-link">Undo rename</span>');
     1206                }, 100);
     1207            } else {
     1208                $b.prop('disabled',false).text('Save');
     1209                $m.text(r.data.message||'Rename failed.').attr('class','a1mm-item-msg error');
     1210            }
     1211        }).fail(function() {
     1212            $b.prop('disabled',false).text('Save');
     1213            $m.text('Network error.').attr('class','a1mm-item-msg error');
     1214        });
     1215    } else {
     1216        saveMeta();
     1217    }
     1218});
    11541219
    11551220// ---- Undo Rename ----
     
    11591224    if (!oldName) return;
    11601225    var noExt = oldName.replace(/\.[^.]+$/,'');
    1161     $it.find('.a1mm-new-name').val(noExt);
    1162     $it.find('.a1mm-undo-link').remove();
    1163     doRename($it);
     1226    var id = $it.data('id');
     1227    var ur = $it.find('.a1mm-refs-cb').is(':checked') ? 1 : 0;
     1228    var $m = $it.find('.a1mm-item-msg');
     1229    $(this).remove();
     1230    $m.text('Undoing...').attr('class','a1mm-item-msg');
     1231    $.post(a1mm.ajax, {
     1232        action:'a1tools_rename_media', nonce:a1mm.nonceRename,
     1233        attachment_id:id, new_filename:noExt, update_refs:ur
     1234    }, function(r) {
     1235        if (r.success) {
     1236            $it.find('.a1mm-current code').text(r.data.new_filename);
     1237            $it.find('.a1mm-new-name').val(r.data.new_filename.replace(/\.[^.]+$/,''));
     1238            if (r.data.new_thumbnail_url) $it.find('.a1mm-thumb img').attr('src', r.data.new_thumbnail_url);
     1239            $it.data('url', r.data.new_url);
     1240            $it.data('old-name','');
     1241            $it.removeClass('a1mm-renamed');
     1242            $m.text('Rename undone.').attr('class','a1mm-item-msg success');
     1243        } else {
     1244            $m.text(r.data.message||'Undo failed.').attr('class','a1mm-item-msg error');
     1245        }
     1246    });
    11641247});
    11651248
    1166 // ---- Save Meta (Alt, Title, Caption, Description) ----
    1167 $(document).on('click', '.a1mm-save-meta-btn', function() {
     1249// ---- Delete ----
     1250$(document).on('click', '.a1mm-delete-btn', function() {
    11681251    var $it = $(this).closest('.a1mm-item');
    11691252    var id = $it.data('id');
     1253    var fn = $it.find('.a1mm-current code').text();
     1254    if (!confirm('Permanently delete "' + fn + '"? This cannot be undone.')) return;
    11701255    var $b = $(this);
    1171     var $m = $it.find('.a1mm-meta-msg');
    1172     $b.prop('disabled',true).text('Saving...');
     1256    $b.prop('disabled',true).text('Deleting...');
    11731257
    11741258    $.post(a1mm.ajax, {
    1175         action:'a1tools_update_media_meta', nonce:a1mm.nonceMeta,
    1176         attachment_id: id,
    1177         alt_text:    $it.find('.a1mm-alt-input').val(),
    1178         title:       $it.find('.a1mm-title-input').val(),
    1179         caption:     $it.find('.a1mm-caption-input').val(),
    1180         description: $it.find('.a1mm-desc-input').val()
     1259        action:'a1tools_delete_media', nonce:a1mm.nonceDelete,
     1260        attachment_id: id
    11811261    }, function(r) {
    1182         $b.prop('disabled',false).text('Save Details');
    11831262        if (r.success) {
    1184             $m.text('Saved!').attr('class','a1mm-meta-msg success');
    1185             setTimeout(function(){ $m.text(''); }, 2500);
    1186             if ($it.find('.a1mm-alt-input').val()) $it.find('.a1mm-badge-no-alt').remove();
     1263            $it.slideUp(300, function() { $(this).remove(); });
    11871264        } else {
    1188             $m.text(r.data.message||'Failed.').attr('class','a1mm-meta-msg error');
     1265            $b.prop('disabled',false).text('Delete');
     1266            alert(r.data.message||'Delete failed.');
    11891267        }
    11901268    }).fail(function() {
    1191         $b.prop('disabled',false).text('Save Details');
    1192         $m.text('Network error.').attr('class','a1mm-meta-msg error');
     1269        $b.prop('disabled',false).text('Delete');
     1270        alert('Network error.');
    11931271    });
    11941272});
     
    12841362
    12851363// ---- Event Handlers ----
    1286 $(document).on('click', '.a1mm-rename-btn', function() { doRename($(this).closest('.a1mm-item')); });
    12871364$(document).on('click', '.a1mm-page-btn', function() {
    12881365    loadMedia(parseInt($(this).data('page'),10));
     
    12921369$('#a1mm-search').on('keypress', function(e) { if(e.which===13) loadMedia(1); });
    12931370$('#a1mm-mime-filter, #a1mm-sort').on('change', function() { loadMedia(1); });
    1294 $(document).on('keypress', '.a1mm-new-name', function(e) { if(e.which===13) doRename($(this).closest('.a1mm-item')); });
     1371$(document).on('keypress', '.a1mm-new-name', function(e) { if(e.which===13) $(this).closest('.a1mm-item').find('.a1mm-save-meta-btn').click(); });
    12951372$(document).on('keypress', '.a1mm-meta-input', function(e) {
    12961373    if(e.which===13 && !$(this).is('textarea')) $(this).closest('.a1mm-item').find('.a1mm-save-meta-btn').click();
  • a1-tools/trunk/readme.txt

    r3478238 r3478245  
    44Requires at least: 5.0
    55Tested up to: 6.9
    6 Stable tag: 1.9.9
     6Stable tag: 1.10.0
    77Requires PHP: 7.4
    88License: GPLv2 or later
     
    151151
    152152== Changelog ==
     153
     154= 1.10.0 =
     155* Improved: Unified Save button — renames file (if changed) and saves all meta in one click
     156* New: Delete button to permanently remove media files
     157* Improved: Full-width input fields for better usability
     158* Improved: Update refs checkbox moved to actions row with Save and Delete
    153159
    154160= 1.9.9 =
Note: See TracChangeset for help on using the changeset viewer.