array( array( 'handle' => 'aiforya-sm-tab-schemas', 'src' => AIFORYA_SM_URL . 'assets/css/tab-schemas.css', 'ver' => AIFORYA_SM_VERSION, ), ), ); } public function render( array $args = [] ): void { // Récupération des paramètres de pagination et filtrage $paged = isset( $_GET['paged'] ) ? max( 1, intval( $_GET['paged'] ) ) : 1; $per_page = 20; $offset = ( $paged - 1 ) * $per_page; $filter_type = isset( $_GET['filter_type'] ) ? sanitize_text_field( wp_unslash( $_GET['filter_type'] ) ) : ''; $filter_status = isset( $_GET['filter_status'] ) ? sanitize_text_field( wp_unslash( $_GET['filter_status'] ) ) : ''; $search = isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : ''; // Construction des arguments de requête $query_args = array( 'limit' => $per_page, 'offset' => $offset, ); if ( ! empty( $filter_type ) ) { $query_args['schema_type'] = $filter_type; } if ( ! empty( $filter_status ) ) { $query_args['status'] = $filter_status; } if ( ! empty( $search ) ) { $query_args['search'] = $search; } // Récupération des données via Db $db = Aiforya_Schema_Markup_Db::get_instance(); $schemas = $db->get_schemas_for_listing( $query_args ); $total_count = $db->count_schemas( $query_args ); $total_pages = ceil( $total_count / $per_page ); // Types de schémas disponibles pour le filtre $schema_types = array( 'Article', 'Product', 'Recipe', 'Event', 'Organization', 'Person', 'Service', 'FAQ', 'HowTo' ); // Helper D2 : pastille d'aide « ? » charte (AiforyaTooltip). $text déjà traduit. $tip = static function ( $text ) { printf( '', esc_attr( $text ) ); }; ?>

post_id ); $edit_url = get_edit_post_link( $schema->post_id ); $view_url = get_permalink( $schema->post_id ); ?>
post_title : __( '(No title)', 'aiforya-schema-markup' ) ); ?>
|
is_manual ) ) : ?> post_type : 'post' ); echo esc_html( $post_type_obj ? $post_type_obj->labels->singular_name : ( $post ? $post->post_type : '' ) ); ?>
1 ) : ?>
add_query_arg( 'paged', '%#%' ), 'format' => '', 'prev_text' => '«', 'next_text' => '»', 'total' => $total_pages, 'current' => $paged, ); echo wp_kses_post( paginate_links( $pagination_args ) ); ?>
admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'aiforya_sm_admin_nonce' ), 'changerLeTypeDe' => __( 'Changing the schema type will regenerate the JSON-LD. Continue?', 'aiforya-schema-markup' ), 'regenererLeSchemaPour' => __( 'Regenerate the schema for this content?', 'aiforya-schema-markup' ), 'uneErreurEstSurvenue' => __( 'An error occurred.', 'aiforya-schema-markup' ), 'erreurReseau' => __( 'Network error.', 'aiforya-schema-markup' ), 'chargementDuJsonLd' => __( 'Loading JSON-LD…', 'aiforya-schema-markup' ), 'aucunSchemaStockePour' => __( 'No schema stored for this post. Please regenerate first.', 'aiforya-schema-markup' ), 'jsonLdCopieDans' => __( 'JSON-LD copied to clipboard.', 'aiforya-schema-markup' ), 'impossibleDeCopierSelection' => __( 'Unable to copy — manual selection available.', 'aiforya-schema-markup' ), 'validationLocale' => __( 'Local validation…', 'aiforya-schema-markup' ), 'regenerationEnCours' => __( 'Regenerating…', 'aiforya-schema-markup' ), 'schemaRegenereAvecSucces' => __( 'Schema successfully regenerated.', 'aiforya-schema-markup' ), 'ouvertureDuGoogleRich' => __( 'Opening Google Rich Results Test…', 'aiforya-schema-markup' ), 'confirmer' => __( 'Confirm', 'aiforya-schema-markup' ), 'annuler' => __( 'Cancel', 'aiforya-schema-markup' ), 'supprimerLeSchemaJson' => /* translators: %s : valeur insérée dynamiquement. */ __( 'Delete the JSON-LD schema for "%s"? The WordPress post itself is left untouched.', 'aiforya-schema-markup' ), 'aucuneLigneSelectionnee' => __( 'No rows selected.', 'aiforya-schema-markup' ), 'supprimerDefinitivementDSchema' => /* translators: %d : valeur insérée dynamiquement. */ __( 'Permanently delete %d schema(s)? WordPress content will not be affected.', 'aiforya-schema-markup' ), 'regenererDSchemaS' => /* translators: %d : valeur insérée dynamiquement. */ __( 'Regenerate %d schema(s)? This may take several seconds.', 'aiforya-schema-markup' ), 'appliquerLeTypeS' => /* translators: %1$s : valeur insérée dynamiquement, %2$d : valeur insérée dynamiquement. */ __( 'Apply type %1$s to %2$d schema(s)? JSON-LD will be regenerated.', 'aiforya-schema-markup' ), 'validerDSchemaS' => /* translators: %d : valeur insérée dynamiquement. */ __( 'Validate %d schema(s)?', 'aiforya-schema-markup' ), 'actionEnCours' => __( 'Action in progress…', 'aiforya-schema-markup' ), 'choisissezUnTypeSchema' => __( 'Choose a Schema.org type from the dropdown menu.', 'aiforya-schema-markup' ), ) ); $js = <<<'JS' var L = window.aiforyaSmSchemasL10n || {}; jQuery(function($){ var ajaxUrl = window.ajaxurl || L.ajaxUrl; var nonce = L.nonce; // FIX doctrine — shell AIFORYA wrap le tab content dans un parent avec transform/filter // qui casse position:fixed des enfants. Solution : déplacer les modals à document.body // au boot pour qu'ils sortent du containing block du parent transformé. $('#aiforya-sm-json-modal, #aiforya-sm-toast-host').appendTo('body'); var i18n = { changeTypeConfirm : L.changerLeTypeDe, regenConfirm : L.regenererLeSchemaPour, error : L.uneErreurEstSurvenue, network : L.erreurReseau, jsonLoading : L.chargementDuJsonLd, jsonNotFound : L.aucunSchemaStockePour, copied : L.jsonLdCopieDans, copyFail : L.impossibleDeCopierSelection, validatingLocal : L.validationLocale, regenerating : L.regenerationEnCours, regenOk : L.schemaRegenereAvecSucces, openingGoogle : L.ouvertureDuGoogleRich, yes : L.confirmer, no : L.annuler }; // ---------- Toast charte AIFORYA ---------- function smToast(message, type, ttl) { type = type || 'info'; ttl = ttl || 4500; var $host = $('#aiforya-sm-toast-host'); if (!$host.length) { $host = $('
').appendTo('body'); } var $t = $('
', { 'class': 'aiforya-sm-toast aiforya-sm-toast--' + type, role: 'status' }).text(message); $host.append($t); setTimeout(function(){ $t.addClass('is-visible'); }, 10); setTimeout(function(){ $t.removeClass('is-visible'); setTimeout(function(){ $t.remove(); }, 300); }, ttl); } // ---------- Confirm charte AIFORYA (remplace window.confirm) ---------- function smConfirm(message, onYes, onNo) { var $modal = $(''); $modal.find('p').text(message); $modal.find('.aiforya-sm-confirm-no').text(i18n.no); $modal.find('.aiforya-sm-confirm-yes').text(i18n.yes); $('body').append($modal); setTimeout(function(){ $modal.addClass('is-open'); $modal.find('.aiforya-sm-confirm-yes').trigger('focus'); }, 10); var settled = false; function close(){ $modal.removeClass('is-open'); setTimeout(function(){ $modal.remove(); }, 200); } function dismiss(){ if (settled) return; settled = true; close(); if (typeof onNo === 'function') onNo(); } function accept(){ if (settled) return; settled = true; close(); if (typeof onYes === 'function') onYes(); } $modal.on('click', '.aiforya-sm-confirm-no, .aiforya-sm-modal-backdrop', dismiss); $modal.on('click', '.aiforya-sm-confirm-yes', accept); $modal.on('keydown', function(e){ if (e.key === 'Escape') dismiss(); if (e.key === 'Enter') accept(); }); } function setBtnLoading($btn, loading, originalHtml) { if (loading) { $btn.data('orig-html', originalHtml || $btn.html()); $btn.prop('disabled', true).html(''); } else { $btn.prop('disabled', false).html($btn.data('orig-html') || $btn.html()); } } // ---------- Changement de type via select inline ---------- $(document).on('change', '.aiforya-sm-schema-type-selector', function(){ var $select = $(this); var postId = $select.data('post-id'); var newType = $select.val(); var origType = $select.data('original'); if (newType === origType) return; $select.prop('disabled', true); smConfirm(i18n.changeTypeConfirm, function(){ $.post(ajaxUrl, { action: 'aiforya_sm_override_schema_type', nonce: nonce, post_id: postId, schema_type: newType }).done(function(r){ if (r && r.success) { $select.data('original', newType); smToast((r.data && r.data.message) || 'OK', 'success'); setTimeout(function(){ location.reload(); }, 600); } else { smToast((r && r.data && r.data.message) || i18n.error, 'error'); $select.val(origType); } }).fail(function(){ smToast(i18n.network, 'error'); $select.val(origType); }).always(function(){ $select.prop('disabled', false); }); }, function(){ $select.val(origType).prop('disabled', false); }); }); // ---------- Voir JSON-LD ---------- $(document).on('click', '.aiforya-sm-action-view', function(){ var $btn = $(this); var postId = $btn.data('post-id'); var orig = $btn.html(); setBtnLoading($btn, true, orig); $('#aiforya-sm-json-modal .aiforya-sm-json-display').text(i18n.jsonLoading); openModal(); $.post(ajaxUrl, { action: 'aiforya_sm_get_schema_json', nonce: nonce, post_id: postId }).done(function(r){ if (r && r.success && r.data && r.data.json) { var pretty; try { pretty = JSON.stringify(JSON.parse(r.data.json), null, 2); } catch(e) { pretty = String(r.data.json); } $('#aiforya-sm-json-modal .aiforya-sm-json-display').text(pretty); } else { $('#aiforya-sm-json-modal .aiforya-sm-json-display').text((r && r.data && r.data.message) || i18n.jsonNotFound); } }).fail(function(){ $('#aiforya-sm-json-modal .aiforya-sm-json-display').text(i18n.network); }).always(function(){ setBtnLoading($btn, false); }); }); // ---------- Tester Google (validation locale + ouverture Rich Results Test) ---------- $(document).on('click', '.aiforya-sm-action-google', function(){ var $btn = $(this); var postId = $btn.data('post-id'); var orig = $btn.html(); setBtnLoading($btn, true, orig); smToast(i18n.validatingLocal, 'info', 2500); $.post(ajaxUrl, { action: 'aiforya_sm_validate_with_google', nonce: nonce, post_id: postId }).done(function(r){ if (r && r.success) { var data = r.data || {}; var msg = data.message || 'OK'; var status = data.status || 'valid'; var type = (status === 'valid') ? 'success' : (status === 'warning' ? 'warning' : 'error'); smToast(msg, type, 5500); if (data.external_url) { smToast(i18n.openingGoogle, 'info', 3500); window.open(data.external_url, '_blank', 'noopener'); } } else { smToast((r && r.data && r.data.message) || i18n.error, 'error'); } }).fail(function(){ smToast(i18n.network, 'error'); }).always(function(){ setBtnLoading($btn, false); }); }); // ---------- Supprimer (efface le schema JSON-LD seulement, post intact) ---------- $(document).on('click', '.aiforya-sm-action-delete', function(){ var $btn = $(this); var postId = $btn.data('post-id'); var title = String($btn.data('post-title') || ''); var msg = L.supprimerLeSchemaJson.replace('%s', title); smConfirm(msg, function(){ var orig = $btn.html(); setBtnLoading($btn, true, orig); $.post(ajaxUrl, { action : 'aiforya_sm_delete_schema_for_post', nonce : nonce, post_id : postId }).done(function(r){ if (r && r.success) { smToast((r.data && r.data.message) || 'OK', 'success'); $btn.closest('tr').fadeOut(220, function(){ $(this).remove(); }); } else { smToast((r && r.data && r.data.message) || i18n.error, 'error'); setBtnLoading($btn, false); } }).fail(function(){ smToast(i18n.network, 'error'); setBtnLoading($btn, false); }); }); }); // ---------- Régénérer ---------- $(document).on('click', '.aiforya-sm-action-regen', function(){ var $btn = $(this); var postId = $btn.data('post-id'); smConfirm(i18n.regenConfirm, function(){ var orig = $btn.html(); setBtnLoading($btn, true, orig); smToast(i18n.regenerating, 'info', 4000); $.post(ajaxUrl, { action: 'aiforya_sm_generate_one', nonce: nonce, post_id: postId }).done(function(r){ if (r && r.success) { smToast((r.data && r.data.message) || i18n.regenOk, 'success'); setTimeout(function(){ location.reload(); }, 800); } else { smToast((r && r.data && r.data.message) || i18n.error, 'error'); } }).fail(function(){ smToast(i18n.network, 'error'); }).always(function(){ setBtnLoading($btn, false); }); }); }); // ---------- Modal helpers ---------- function openModal() { $('#aiforya-sm-json-modal').addClass('is-open').fadeIn(120); } function closeModal() { $('#aiforya-sm-json-modal').removeClass('is-open').fadeOut(120); } $(document).on('click', '#aiforya-sm-json-modal .aiforya-sm-modal-close, #aiforya-sm-json-modal .aiforya-sm-modal-backdrop', closeModal); $(document).on('keydown', function(e){ if (e.key === 'Escape' && $('#aiforya-sm-json-modal').hasClass('is-open')) closeModal(); }); $(document).on('click', '#aiforya-sm-json-modal .aiforya-sm-modal-copy', function(){ var text = $('#aiforya-sm-json-modal .aiforya-sm-json-display').text() || ''; if (!text) return; if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(text).then( function(){ smToast(i18n.copied, 'success'); }, function(){ smToast(i18n.copyFail, 'warning'); } ); } else { var $ta = $('