Plugin Directory

Changeset 3472434


Ignore:
Timestamp:
03/02/2026 07:55:00 AM (less than one hour ago)
Author:
bsolveit
Message:

v1.1.0: Add domain verification for AI Visibility Directory

Location:
ai-discovery-files
Files:
2 added
22 edited
1 copied

Legend:

Unmodified
Added
Removed
  • ai-discovery-files/tags/1.1.0/admin/class-admin.php

    r3472044 r3472434  
    3636        add_action( 'wp_ajax_aidf_validate_files', array( __CLASS__, 'ajax_validate_files' ) );
    3737        add_action( 'wp_ajax_aidf_dismiss_welcome', array( __CLASS__, 'ajax_dismiss_welcome' ) );
     38        add_action( 'wp_ajax_aidf_save_verify_code', array( __CLASS__, 'ajax_save_verify_code' ) );
    3839        add_action( 'admin_notices', array( __CLASS__, 'maybe_show_welcome_notice' ) );
    3940        add_action( 'admin_notices', array( __CLASS__, 'maybe_show_conflict_notice' ) );
     
    114115                    'unknownError'       => __( 'Unknown error', 'ai-discovery-files' ),
    115116                    'crossFileConsistency' => __( 'Cross-File Consistency', 'ai-discovery-files' ),
     117                    'verifyFileActive'   => __( 'Verification file is live at', 'ai-discovery-files' ),
     118                    'verifyFileInactive' => __( 'No verification code set. Enter your code above to activate.', 'ai-discovery-files' ),
    116119                ),
    117120            )
     
    226229
    227230        wp_send_json_success( $results );
     231    }
     232
     233    /**
     234     * AJAX handler: save domain verification code.
     235     *
     236     * @return void
     237     */
     238    public static function ajax_save_verify_code() {
     239        check_ajax_referer( 'aidf_admin_nonce', 'nonce' );
     240
     241        if ( ! current_user_can( 'manage_options' ) ) {
     242            wp_send_json_error( __( 'Permission denied.', 'ai-discovery-files' ) );
     243        }
     244
     245        $code = isset( $_POST['verify_code'] ) ? sanitize_text_field( wp_unslash( $_POST['verify_code'] ) ) : '';
     246
     247        // Validate: must match "ai-visibility-verify=" followed by hex, or empty to clear.
     248        if ( '' !== $code && ! preg_match( '/^ai-visibility-verify=[a-f0-9]+$/i', $code ) ) {
     249            wp_send_json_error( __( 'Invalid verification code. The code should be in the format: ai-visibility-verify=abc123', 'ai-discovery-files' ) );
     250        }
     251
     252        $settings               = get_option( 'aidf_settings', array() );
     253        $settings['verify_code'] = $code;
     254        update_option( 'aidf_settings', $settings );
     255
     256        // Flush rewrite rules to ensure the verify file route is active.
     257        AIDF_Server::register_rewrite_rules();
     258        flush_rewrite_rules();
     259
     260        $data = array(
     261            'message' => empty( $code )
     262                ? __( 'Verification code removed.', 'ai-discovery-files' )
     263                : __( 'Verification code saved.', 'ai-discovery-files' ),
     264            'active'  => ! empty( $code ),
     265            'url'     => home_url( '/ai-visibility-verify.txt' ),
     266        );
     267
     268        wp_send_json_success( $data );
    228269    }
    229270
  • ai-discovery-files/tags/1.1.0/admin/class-settings.php

    r3472044 r3472434  
    270270        }
    271271
     272        // Directory verification code — "ai-visibility-verify=" followed by hex.
     273        if ( isset( $input['verify_code'] ) ) {
     274            $code = sanitize_text_field( $input['verify_code'] );
     275            $clean['verify_code'] = ( '' === $code || preg_match( '/^ai-visibility-verify=[a-f0-9]+$/i', $code ) ) ? $code : $fallback['verify_code'];
     276        } else {
     277            $clean['verify_code'] = isset( $fallback['verify_code'] ) ? $fallback['verify_code'] : '';
     278        }
     279
    272280        // Flush rewrite rules since active files may have changed.
    273281        AIDF_Server::register_rewrite_rules();
  • ai-discovery-files/tags/1.1.0/admin/css/admin.css

    r3472044 r3472434  
    18651865}
    18661866
     1867/* --------------------------------------------------------
     1868   23. Domain Verification Modal
     1869   -------------------------------------------------------- */
     1870
     1871/* Backdrop */
     1872.aidf-verify-modal {
     1873    position: fixed;
     1874    inset: 0;
     1875    z-index: 100100;
     1876    display: flex;
     1877    align-items: center;
     1878    justify-content: center;
     1879    padding: var(--aidf-sp-5);
     1880    animation: aidfModalFadeIn 0.2s ease-out;
     1881}
     1882
     1883.aidf-verify-modal__backdrop {
     1884    position: absolute;
     1885    inset: 0;
     1886    background: rgba(0, 0, 0, 0.6);
     1887    backdrop-filter: blur(4px);
     1888    -webkit-backdrop-filter: blur(4px);
     1889}
     1890
     1891/* Card */
     1892.aidf-verify-modal__card {
     1893    position: relative;
     1894    width: 100%;
     1895    max-width: 580px;
     1896    background: var(--aidf-white);
     1897    border-radius: var(--aidf-radius-lg);
     1898    box-shadow: 0 24px 80px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(0, 0, 0, 0.05);
     1899    overflow: hidden;
     1900    animation: aidfModalSlideUp 0.25s ease-out;
     1901}
     1902
     1903/* Header */
     1904.aidf-verify-modal__header {
     1905    display: flex;
     1906    align-items: center;
     1907    justify-content: space-between;
     1908    padding: var(--aidf-sp-5) var(--aidf-sp-6);
     1909    background: linear-gradient(135deg, var(--aidf-dark) 0%, var(--aidf-dark-mid) 100%);
     1910    color: var(--aidf-text-inverse);
     1911}
     1912
     1913.aidf-verify-modal__header-content {
     1914    display: flex;
     1915    align-items: center;
     1916    gap: var(--aidf-sp-3);
     1917}
     1918
     1919.aidf-verify-modal__header-icon {
     1920    display: flex;
     1921    align-items: center;
     1922    justify-content: center;
     1923    width: 44px;
     1924    height: 44px;
     1925    background: linear-gradient(135deg, var(--aidf-orange) 0%, #ff9d42 100%);
     1926    border-radius: 10px;
     1927    flex-shrink: 0;
     1928}
     1929
     1930.aidf-verify-modal__header-icon .dashicons {
     1931    width: 22px;
     1932    height: 22px;
     1933    font-size: 22px;
     1934    color: var(--aidf-dark);
     1935}
     1936
     1937.aidf-verify-modal__title {
     1938    font-size: 18px;
     1939    font-weight: 700;
     1940    color: var(--aidf-text-inverse);
     1941    letter-spacing: -0.01em;
     1942    line-height: 1.3;
     1943}
     1944
     1945.aidf-verify-modal__subtitle {
     1946    margin: 2px 0 0;
     1947    font-size: 12px;
     1948    color: rgba(255, 255, 255, 0.6);
     1949    font-weight: 500;
     1950    text-transform: uppercase;
     1951    letter-spacing: 0.04em;
     1952}
     1953
     1954.aidf-verify-modal__close {
     1955    display: flex;
     1956    align-items: center;
     1957    justify-content: center;
     1958    width: 32px;
     1959    height: 32px;
     1960    background: rgba(255, 255, 255, 0.1);
     1961    border: none;
     1962    border-radius: 6px;
     1963    cursor: pointer;
     1964    color: rgba(255, 255, 255, 0.7);
     1965    transition: all 0.15s ease;
     1966}
     1967
     1968.aidf-verify-modal__close:hover {
     1969    background: rgba(255, 255, 255, 0.2);
     1970    color: var(--aidf-text-inverse);
     1971}
     1972
     1973.aidf-verify-modal__close .dashicons {
     1974    width: 20px;
     1975    height: 20px;
     1976    font-size: 20px;
     1977}
     1978
     1979/* Body */
     1980.aidf-verify-modal__body {
     1981    padding: var(--aidf-sp-6);
     1982}
     1983
     1984.aidf-verify-modal__intro {
     1985    margin-bottom: var(--aidf-sp-5);
     1986}
     1987
     1988.aidf-verify-modal__intro p {
     1989    margin: 0 0 var(--aidf-sp-2) 0;
     1990    font-size: var(--aidf-text-sm);
     1991    color: var(--aidf-text-secondary);
     1992    line-height: 1.6;
     1993}
     1994
     1995.aidf-verify-modal__intro p:last-child {
     1996    margin-bottom: 0;
     1997}
     1998
     1999/* Steps */
     2000.aidf-verify-modal__steps {
     2001    display: flex;
     2002    flex-direction: column;
     2003    gap: var(--aidf-sp-3);
     2004    margin-bottom: var(--aidf-sp-5);
     2005    padding: var(--aidf-sp-4);
     2006    background: var(--aidf-bg);
     2007    border-radius: var(--aidf-radius);
     2008    border: 1px solid var(--aidf-border-light);
     2009}
     2010
     2011.aidf-verify-modal__step {
     2012    display: flex;
     2013    align-items: center;
     2014    gap: var(--aidf-sp-3);
     2015}
     2016
     2017.aidf-verify-modal__step-number {
     2018    display: flex;
     2019    align-items: center;
     2020    justify-content: center;
     2021    width: 24px;
     2022    height: 24px;
     2023    background: var(--aidf-orange);
     2024    color: var(--aidf-text-on-orange);
     2025    border-radius: 50%;
     2026    font-size: 12px;
     2027    font-weight: 700;
     2028    flex-shrink: 0;
     2029    line-height: 1;
     2030}
     2031
     2032.aidf-verify-modal__step-text {
     2033    font-size: var(--aidf-text-sm);
     2034    color: var(--aidf-text);
     2035    line-height: 1.4;
     2036}
     2037
     2038.aidf-verify-modal__step-text a {
     2039    color: var(--aidf-orange);
     2040    font-weight: 600;
     2041    text-decoration: none;
     2042}
     2043
     2044.aidf-verify-modal__step-text a:hover {
     2045    color: var(--aidf-orange-hover);
     2046    text-decoration: underline;
     2047}
     2048
     2049/* Code input */
     2050.aidf-verify-modal__field {
     2051    margin-bottom: var(--aidf-sp-4);
     2052}
     2053
     2054.aidf-verify-modal__label {
     2055    display: block;
     2056    margin-bottom: var(--aidf-sp-2);
     2057    font-size: var(--aidf-text-sm);
     2058    font-weight: 600;
     2059    color: var(--aidf-text);
     2060}
     2061
     2062.aidf-verify-modal__input {
     2063    width: 100%;
     2064    padding: 10px 14px;
     2065    font-size: 14px;
     2066    font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
     2067    letter-spacing: 0.02em;
     2068    color: var(--aidf-text);
     2069    background: var(--aidf-white);
     2070    border: 1px solid var(--aidf-border);
     2071    border-radius: var(--aidf-radius);
     2072    outline: none;
     2073    transition: border-color 0.15s ease, box-shadow 0.15s ease;
     2074    box-sizing: border-box;
     2075}
     2076
     2077.aidf-verify-modal__input:focus {
     2078    border-color: var(--aidf-orange);
     2079    box-shadow: 0 0 0 3px var(--aidf-orange-glow);
     2080}
     2081
     2082.aidf-verify-modal__input::placeholder {
     2083    color: var(--aidf-text-tertiary);
     2084    font-family: var(--aidf-font);
     2085    letter-spacing: normal;
     2086}
     2087
     2088/* Status */
     2089.aidf-verify-modal__status {
     2090    display: flex;
     2091    align-items: center;
     2092    gap: var(--aidf-sp-2);
     2093    padding: var(--aidf-sp-3) var(--aidf-sp-4);
     2094    border-radius: var(--aidf-radius);
     2095    font-size: var(--aidf-text-sm);
     2096    color: var(--aidf-text-secondary);
     2097    background: var(--aidf-bg);
     2098    border: 1px solid var(--aidf-border-light);
     2099}
     2100
     2101.aidf-verify-modal__status .dashicons {
     2102    width: 18px;
     2103    height: 18px;
     2104    font-size: 18px;
     2105    flex-shrink: 0;
     2106}
     2107
     2108.aidf-verify-modal__status--active {
     2109    background: var(--aidf-green-bg);
     2110    border-color: var(--aidf-green-border);
     2111    color: #15803d;
     2112}
     2113
     2114.aidf-verify-modal__status--active .dashicons {
     2115    color: var(--aidf-green);
     2116}
     2117
     2118.aidf-verify-modal__status a {
     2119    color: var(--aidf-orange);
     2120    font-weight: 500;
     2121    word-break: break-all;
     2122}
     2123
     2124/* Footer */
     2125.aidf-verify-modal__footer {
     2126    display: flex;
     2127    align-items: center;
     2128    justify-content: space-between;
     2129    padding: var(--aidf-sp-4) var(--aidf-sp-6);
     2130    background: var(--aidf-bg);
     2131    border-top: 1px solid var(--aidf-border-light);
     2132}
     2133
     2134.aidf-verify-modal__footer-right {
     2135    display: flex;
     2136    gap: var(--aidf-sp-2);
     2137}
     2138
     2139.aidf-verify-modal__dashboard-link {
     2140    display: inline-flex;
     2141    align-items: center;
     2142    gap: 4px;
     2143    font-size: var(--aidf-text-sm);
     2144    color: var(--aidf-text-secondary);
     2145    text-decoration: none;
     2146    transition: color 0.15s ease;
     2147}
     2148
     2149.aidf-verify-modal__dashboard-link:hover {
     2150    color: var(--aidf-orange);
     2151}
     2152
     2153.aidf-verify-modal__dashboard-link .dashicons {
     2154    width: 14px;
     2155    height: 14px;
     2156    font-size: 14px;
     2157}
     2158
     2159.aidf-verify-modal__footer .aidf-btn .dashicons {
     2160    width: 16px;
     2161    height: 16px;
     2162    font-size: 16px;
     2163    margin-right: 2px;
     2164}
     2165
     2166/* Animations */
     2167@keyframes aidfModalFadeIn {
     2168    from { opacity: 0; }
     2169    to { opacity: 1; }
     2170}
     2171
     2172@keyframes aidfModalSlideUp {
     2173    from { opacity: 0; transform: translateY(12px) scale(0.98); }
     2174    to { opacity: 1; transform: translateY(0) scale(1); }
     2175}
     2176
     2177/* --------------------------------------------------------
     2178   23b. CTA Verify Button Variant
     2179   -------------------------------------------------------- */
     2180.aidf-directory-cta__actions {
     2181    display: flex;
     2182    gap: var(--aidf-sp-2);
     2183    flex-shrink: 0;
     2184}
     2185
     2186.aidf-directory-cta__button--verify {
     2187    background: transparent;
     2188    border: 2px solid rgba(255, 255, 255, 0.5);
     2189    color: var(--aidf-text-inverse);
     2190    cursor: pointer;
     2191}
     2192
     2193.aidf-directory-cta__button--verify:hover {
     2194    background: rgba(255, 255, 255, 0.15);
     2195    border-color: rgba(255, 255, 255, 0.8);
     2196    color: var(--aidf-text-inverse) !important;
     2197    transform: translateY(-1px);
     2198}
     2199
     2200.aidf-directory-cta__button--verify .dashicons {
     2201    width: 16px;
     2202    height: 16px;
     2203    font-size: 16px;
     2204}
     2205
     2206/* --------------------------------------------------------
     2207   23c. Inline Verification Panel (Status Tab)
     2208   -------------------------------------------------------- */
     2209.aidf-verify-panel {
     2210    margin-top: var(--aidf-sp-5);
     2211}
     2212
     2213.aidf-verify-panel .aidf-panel-title .dashicons {
     2214    margin-right: 6px;
     2215    color: var(--aidf-orange);
     2216}
     2217
     2218.aidf-verify-inline {
     2219    display: flex;
     2220    align-items: center;
     2221    justify-content: space-between;
     2222    gap: var(--aidf-sp-4);
     2223}
     2224
     2225.aidf-verify-inline__status {
     2226    display: flex;
     2227    align-items: center;
     2228    gap: var(--aidf-sp-2);
     2229    font-size: var(--aidf-text-sm);
     2230}
     2231
     2232.aidf-verify-inline__status--active {
     2233    color: #15803d;
     2234}
     2235
     2236.aidf-verify-inline__status--active .dashicons {
     2237    color: var(--aidf-green);
     2238}
     2239
     2240.aidf-verify-inline__status a {
     2241    color: var(--aidf-orange);
     2242}
     2243
     2244.aidf-verify-inline__description {
     2245    margin: 0;
     2246    font-size: var(--aidf-text-sm);
     2247    color: var(--aidf-text-secondary);
     2248}
     2249
     2250/* --------------------------------------------------------
     2251   24. Responsive
     2252   -------------------------------------------------------- */
     2253@media (max-width: 960px) {
     2254    .aidf-header {
     2255        padding: var(--aidf-sp-4) var(--aidf-sp-5);
     2256    }
     2257
     2258    .aidf-tabs {
     2259        padding: 0 var(--aidf-sp-4);
     2260        overflow-x: auto;
     2261    }
     2262
     2263    .aidf-content {
     2264        padding: var(--aidf-sp-5);
     2265    }
     2266
     2267    .aidf-file-grid {
     2268        grid-template-columns: 1fr;
     2269    }
     2270}
     2271
    18672272@media (max-width: 782px) {
     2273    .aidf-wrap {
     2274        margin-left: -10px;
     2275    }
     2276
     2277    .aidf-tabs {
     2278        top: 46px; /* Mobile admin bar height */
     2279    }
     2280
     2281    .aidf-header-right {
     2282        display: none;
     2283    }
     2284
     2285    .aidf-header-version {
     2286        display: none;
     2287    }
     2288
    18682289    .aidf-directory-cta__content {
    18692290        flex-direction: column;
     
    18722293    }
    18732294
     2295    .aidf-directory-cta__actions {
     2296        flex-direction: column;
     2297        width: 100%;
     2298    }
     2299
    18742300    .aidf-directory-cta__button {
    18752301        width: 100%;
    18762302        justify-content: center;
    18772303    }
    1878 }
    1879 
    1880 /* --------------------------------------------------------
    1881    23. Responsive (formerly 22)
    1882    -------------------------------------------------------- */
    1883 @media (max-width: 960px) {
    1884     .aidf-header {
    1885         padding: var(--aidf-sp-4) var(--aidf-sp-5);
     2304
     2305    .aidf-verify-modal__card {
     2306        max-height: 90vh;
     2307        overflow-y: auto;
    18862308    }
    18872309
    1888     .aidf-tabs {
    1889         padding: 0 var(--aidf-sp-4);
    1890         overflow-x: auto;
     2310    .aidf-verify-modal__footer {
     2311        flex-direction: column;
     2312        gap: var(--aidf-sp-3);
    18912313    }
    18922314
    1893     .aidf-content {
    1894         padding: var(--aidf-sp-5);
     2315    .aidf-verify-modal__footer-right {
     2316        width: 100%;
    18952317    }
    18962318
    1897     .aidf-file-grid {
    1898         grid-template-columns: 1fr;
     2319    .aidf-verify-modal__footer-right .aidf-btn {
     2320        flex: 1;
    18992321    }
    1900 }
    1901 
    1902 @media (max-width: 782px) {
    1903     .aidf-wrap {
    1904         margin-left: -10px;
     2322
     2323    .aidf-verify-inline {
     2324        flex-direction: column;
     2325        align-items: flex-start;
    19052326    }
    1906 
    1907     .aidf-tabs {
    1908         top: 46px; /* Mobile admin bar height */
    1909     }
    1910 
    1911     .aidf-header-right {
    1912         display: none;
    1913     }
    1914 
    1915     .aidf-header-version {
    1916         display: none;
    1917     }
    1918 }
    1919 
    1920 /* --------------------------------------------------------
    1921    23. WordPress Admin Overrides
     2327}
     2328
     2329/* --------------------------------------------------------
     2330   25. WordPress Admin Overrides
    19222331   -------------------------------------------------------- */
    19232332
     
    19482357
    19492358/* --------------------------------------------------------
    1950    24. Welcome Notice (global WP admin)
     2359   26. Welcome Notice (global WP admin)
    19512360   -------------------------------------------------------- */
    19522361.aidf-welcome-notice {
  • ai-discovery-files/tags/1.1.0/admin/js/admin.js

    r3472044 r3472434  
    461461        });
    462462
     463        /* -------------------------------------------------------
     464           Domain Verification Modal
     465           ------------------------------------------------------- */
     466        var verifyModal = $('#aidf-verify-modal');
     467
     468        function openVerifyModal() {
     469            verifyModal.show();
     470            $('body').css('overflow', 'hidden');
     471            verifyModal.find('#aidf-verify-code').focus();
     472        }
     473
     474        function closeVerifyModal() {
     475            verifyModal.hide();
     476            $('body').css('overflow', '');
     477        }
     478
     479        // Open from CTA button or Status tab panel button.
     480        $(document).on('click', '#aidf-open-verify-cta, #aidf-open-verify-panel', function (e) {
     481            e.preventDefault();
     482            openVerifyModal();
     483        });
     484
     485        // Close: X button, backdrop, Cancel button.
     486        verifyModal.on('click', '[data-aidf-close-modal]', function () {
     487            closeVerifyModal();
     488        });
     489
     490        // Close: Escape key.
     491        $(document).on('keydown', function (e) {
     492            if (e.key === 'Escape' && verifyModal.is(':visible')) {
     493                closeVerifyModal();
     494            }
     495        });
     496
     497        // Save verification code via AJAX.
     498        $('#aidf-verify-save').on('click', function () {
     499            var $btn = $(this);
     500            var code = $('#aidf-verify-code').val().trim();
     501            var $status = $('#aidf-verify-status');
     502
     503            $btn.prop('disabled', true).find('.dashicons').removeClass('dashicons-yes').addClass('dashicons-update');
     504
     505            $.post(aidfAdmin.ajaxUrl, {
     506                action: 'aidf_save_verify_code',
     507                nonce: aidfAdmin.nonce,
     508                verify_code: code
     509            }, function (response) {
     510                $btn.prop('disabled', false).find('.dashicons').removeClass('dashicons-update').addClass('dashicons-yes');
     511
     512                if (!response.success) {
     513                    $status
     514                        .removeClass('aidf-verify-modal__status--active')
     515                        .html('<span class="dashicons dashicons-warning"></span> <span>' + escAttr(response.data) + '</span>');
     516                    return;
     517                }
     518
     519                var data = response.data;
     520
     521                if (data.active) {
     522                    $status
     523                        .addClass('aidf-verify-modal__status--active')
     524                        .html(
     525                            '<span class="dashicons dashicons-yes-alt"></span> ' +
     526                            '<span>' + escAttr(aidfAdmin.i18n.verifyFileActive) + ' ' +
     527                            '<a href="' + escAttr(data.url) + '" target="_blank" rel="noopener">' + escAttr(data.url) + '</a></span>'
     528                        );
     529                } else {
     530                    $status
     531                        .removeClass('aidf-verify-modal__status--active')
     532                        .html('<span class="dashicons dashicons-info-outline"></span> <span>' + escAttr(aidfAdmin.i18n.verifyFileInactive) + '</span>');
     533                }
     534
     535                // Flash the save button green briefly.
     536                $btn.addClass('aidf-btn--success');
     537                setTimeout(function () {
     538                    $btn.removeClass('aidf-btn--success');
     539                }, 1500);
     540            }).fail(function () {
     541                $btn.prop('disabled', false).find('.dashicons').removeClass('dashicons-update').addClass('dashicons-yes');
     542            });
     543        });
     544
    463545    }); // end DOM ready
    464546
  • ai-discovery-files/tags/1.1.0/admin/views/partials/directory-cta.php

    r3472044 r3472434  
    4646        </div>
    4747
    48         <div class="aidf-directory-cta__action">
     48        <div class="aidf-directory-cta__actions">
    4949            <a href="https://www.ai-visibility.org.uk/submit/"
    5050               class="aidf-directory-cta__button"
     
    5454                <span class="dashicons dashicons-external"></span>
    5555            </a>
     56            <button type="button" class="aidf-directory-cta__button aidf-directory-cta__button--verify" id="aidf-open-verify-cta">
     57                <span class="dashicons dashicons-shield-alt"></span>
     58                <?php esc_html_e( 'Verify Domain', 'ai-discovery-files' ); ?>
     59            </button>
    5660        </div>
    5761    </div>
  • ai-discovery-files/tags/1.1.0/admin/views/settings-page.php

    r3472044 r3472434  
    9595                    ?>
    9696                    <input type="hidden" name="aidf_settings[spec_attribution]" value="<?php echo esc_attr( $settings['spec_attribution'] ? '1' : '0' ); ?>" class="aidf-hidden-attribution">
     97                    <input type="hidden" name="aidf_settings[verify_code]" value="<?php echo esc_attr( isset( $settings['verify_code'] ) ? $settings['verify_code'] : '' ); ?>">
    9798                <?php endif; ?>
    9899
     
    114115        <?php endif; ?>
    115116    </div>
     117
     118    <?php include AIDF_PLUGIN_DIR . 'admin/views/partials/verify-modal.php'; ?>
    116119</div>
  • ai-discovery-files/tags/1.1.0/admin/views/tab-status.php

    r3472044 r3472434  
    8181
    8282    <input type="hidden" name="aidf_settings[spec_attribution]" value="<?php echo esc_attr( $settings['spec_attribution'] ? '1' : '0' ); ?>">
     83    <input type="hidden" name="aidf_settings[verify_code]" value="<?php echo esc_attr( isset( $settings['verify_code'] ) ? $settings['verify_code'] : '' ); ?>">
    8384
    8485    <?php foreach ( $tiers as $tier_slug => $tier ) : ?>
     
    175176    </div>
    176177</div>
     178
     179<!-- Domain Verification -->
     180<?php
     181$verify_code   = isset( $settings['verify_code'] ) ? $settings['verify_code'] : '';
     182$verify_active = ! empty( $verify_code );
     183?>
     184<div class="aidf-panel aidf-verify-panel">
     185    <div class="aidf-panel-header">
     186        <div>
     187            <h2 class="aidf-panel-title">
     188                <span class="dashicons dashicons-shield-alt"></span>
     189                <?php esc_html_e( 'Directory Verification', 'ai-discovery-files' ); ?>
     190                <?php if ( $verify_active ) : ?>
     191                    <span class="aidf-badge aidf-badge--essential"><?php esc_html_e( 'active', 'ai-discovery-files' ); ?></span>
     192                <?php endif; ?>
     193            </h2>
     194            <p class="aidf-panel-subtitle"><?php esc_html_e( 'Prove domain ownership to manage your listing in the AI Visibility Directory.', 'ai-discovery-files' ); ?></p>
     195        </div>
     196    </div>
     197    <div class="aidf-panel-body">
     198        <div class="aidf-verify-inline">
     199            <?php if ( $verify_active ) : ?>
     200                <div class="aidf-verify-inline__status aidf-verify-inline__status--active">
     201                    <span class="dashicons dashicons-yes-alt"></span>
     202                    <span>
     203                        <?php esc_html_e( 'Verification file is live at', 'ai-discovery-files' ); ?>
     204                        <a href="<?php echo esc_url( home_url( '/ai-visibility-verify.txt' ) ); ?>" target="_blank" rel="noopener">
     205                            /ai-visibility-verify.txt &#8599;
     206                        </a>
     207                    </span>
     208                </div>
     209            <?php else : ?>
     210                <p class="aidf-verify-inline__description">
     211                    <?php esc_html_e( 'After submitting your site to the directory, enter your verification code here so the plugin can serve the ownership file automatically.', 'ai-discovery-files' ); ?>
     212                </p>
     213            <?php endif; ?>
     214            <button type="button" class="aidf-btn aidf-btn--secondary" id="aidf-open-verify-panel">
     215                <span class="dashicons dashicons-shield-alt"></span>
     216                <?php
     217                echo $verify_active
     218                    ? esc_html__( 'Update Verification Code', 'ai-discovery-files' )
     219                    : esc_html__( 'Enter Verification Code', 'ai-discovery-files' );
     220                ?>
     221            </button>
     222        </div>
     223    </div>
     224</div>
  • ai-discovery-files/tags/1.1.0/ai-discovery-files.php

    r3472087 r3472434  
    44 * Plugin URI:        https://www.ai-visibility.org.uk/wordpress-plugin/ai-discovery-files/
    55 * Description:       Improve your AI Visibility by generating AI Discovery Files so AI systems like ChatGPT, Claude, and Gemini can correctly discover, interpret, and cite your website.
    6  * Version:           1.0.0
     6 * Version:           1.1.0
    77 * Requires at least: 6.2
    88 * Requires PHP:      8.0
     
    2424 * Plugin constants.
    2525 */
    26 define( 'AIDF_VERSION', '1.0.0' );
     26define( 'AIDF_VERSION', '1.1.0' );
    2727define( 'AIDF_PLUGIN_FILE', __FILE__ );
    2828define( 'AIDF_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
  • ai-discovery-files/tags/1.1.0/includes/class-plugin.php

    r3472044 r3472434  
    123123            'active_files'       => array( 'llms-txt', 'ai-txt' ),
    124124            'spec_attribution'   => false,
     125
     126            // Directory verification.
     127            'verify_code'        => '',
    125128        );
    126129
  • ai-discovery-files/tags/1.1.0/includes/class-server.php

    r3472044 r3472434  
    5757            );
    5858        }
     59
     60        // Domain verification file for AI Visibility Directory.
     61        add_rewrite_rule(
     62            '^ai-visibility-verify\.txt$',
     63            'index.php?aidf_file=verify',
     64            'top'
     65        );
    5966    }
    6067
     
    8087        if ( empty( $file_slug ) ) {
    8188            return;
     89        }
     90
     91        // Handle verification file separately (not an AI Discovery File).
     92        if ( 'verify' === $file_slug ) {
     93            $settings    = AIDF_Plugin::get_settings();
     94            $verify_code = isset( $settings['verify_code'] ) ? $settings['verify_code'] : '';
     95
     96            if ( empty( $verify_code ) ) {
     97                status_header( 404 );
     98                nocache_headers();
     99                echo esc_html__( '404 — Verification file not configured.', 'ai-discovery-files' );
     100                exit;
     101            }
     102
     103            status_header( 200 );
     104            header( 'Content-Type: text/plain; charset=utf-8' );
     105            header( 'Cache-Control: no-store' );
     106            header( 'X-Robots-Tag: noindex' );
     107            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Plain text output, validated on save.
     108            echo $verify_code;
     109            exit;
    82110        }
    83111
  • ai-discovery-files/tags/1.1.0/readme.txt

    r3472165 r3472434  
    33Tags: ai, llms.txt, ai visibility, seo, chatgpt
    44Requires at least: 6.2
    5 Tested up to: 6.9.1
     5Tested up to: 6.9
    66Requires PHP: 8.0
    7 Stable tag: 1.0.0
     7Stable tag: 1.1.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    120120== Changelog ==
    121121
     122= 1.1.0 =
     123* Add domain verification for the AI Visibility Directory
     124* New verification modal with step-by-step instructions
     125* Serve ai-visibility-verify.txt with user-supplied code
     126* Verify Domain button in directory CTA banner
     127* Verification panel on Status tab
     128* AJAX save for verification code (no page reload)
     129
    122130= 1.0.0 =
    123131* Initial release
     
    133141== Upgrade Notice ==
    134142
     143= 1.1.0 =
     144New: Domain verification for the AI Visibility Directory. Serve your verification file directly from WordPress — no FTP needed.
     145
    135146= 1.0.0 =
    136147Initial release. Install and configure your AI Discovery Files.
  • ai-discovery-files/trunk/admin/class-admin.php

    r3472044 r3472434  
    3636        add_action( 'wp_ajax_aidf_validate_files', array( __CLASS__, 'ajax_validate_files' ) );
    3737        add_action( 'wp_ajax_aidf_dismiss_welcome', array( __CLASS__, 'ajax_dismiss_welcome' ) );
     38        add_action( 'wp_ajax_aidf_save_verify_code', array( __CLASS__, 'ajax_save_verify_code' ) );
    3839        add_action( 'admin_notices', array( __CLASS__, 'maybe_show_welcome_notice' ) );
    3940        add_action( 'admin_notices', array( __CLASS__, 'maybe_show_conflict_notice' ) );
     
    114115                    'unknownError'       => __( 'Unknown error', 'ai-discovery-files' ),
    115116                    'crossFileConsistency' => __( 'Cross-File Consistency', 'ai-discovery-files' ),
     117                    'verifyFileActive'   => __( 'Verification file is live at', 'ai-discovery-files' ),
     118                    'verifyFileInactive' => __( 'No verification code set. Enter your code above to activate.', 'ai-discovery-files' ),
    116119                ),
    117120            )
     
    226229
    227230        wp_send_json_success( $results );
     231    }
     232
     233    /**
     234     * AJAX handler: save domain verification code.
     235     *
     236     * @return void
     237     */
     238    public static function ajax_save_verify_code() {
     239        check_ajax_referer( 'aidf_admin_nonce', 'nonce' );
     240
     241        if ( ! current_user_can( 'manage_options' ) ) {
     242            wp_send_json_error( __( 'Permission denied.', 'ai-discovery-files' ) );
     243        }
     244
     245        $code = isset( $_POST['verify_code'] ) ? sanitize_text_field( wp_unslash( $_POST['verify_code'] ) ) : '';
     246
     247        // Validate: must match "ai-visibility-verify=" followed by hex, or empty to clear.
     248        if ( '' !== $code && ! preg_match( '/^ai-visibility-verify=[a-f0-9]+$/i', $code ) ) {
     249            wp_send_json_error( __( 'Invalid verification code. The code should be in the format: ai-visibility-verify=abc123', 'ai-discovery-files' ) );
     250        }
     251
     252        $settings               = get_option( 'aidf_settings', array() );
     253        $settings['verify_code'] = $code;
     254        update_option( 'aidf_settings', $settings );
     255
     256        // Flush rewrite rules to ensure the verify file route is active.
     257        AIDF_Server::register_rewrite_rules();
     258        flush_rewrite_rules();
     259
     260        $data = array(
     261            'message' => empty( $code )
     262                ? __( 'Verification code removed.', 'ai-discovery-files' )
     263                : __( 'Verification code saved.', 'ai-discovery-files' ),
     264            'active'  => ! empty( $code ),
     265            'url'     => home_url( '/ai-visibility-verify.txt' ),
     266        );
     267
     268        wp_send_json_success( $data );
    228269    }
    229270
  • ai-discovery-files/trunk/admin/class-settings.php

    r3472044 r3472434  
    270270        }
    271271
     272        // Directory verification code — "ai-visibility-verify=" followed by hex.
     273        if ( isset( $input['verify_code'] ) ) {
     274            $code = sanitize_text_field( $input['verify_code'] );
     275            $clean['verify_code'] = ( '' === $code || preg_match( '/^ai-visibility-verify=[a-f0-9]+$/i', $code ) ) ? $code : $fallback['verify_code'];
     276        } else {
     277            $clean['verify_code'] = isset( $fallback['verify_code'] ) ? $fallback['verify_code'] : '';
     278        }
     279
    272280        // Flush rewrite rules since active files may have changed.
    273281        AIDF_Server::register_rewrite_rules();
  • ai-discovery-files/trunk/admin/css/admin.css

    r3472044 r3472434  
    18651865}
    18661866
     1867/* --------------------------------------------------------
     1868   23. Domain Verification Modal
     1869   -------------------------------------------------------- */
     1870
     1871/* Backdrop */
     1872.aidf-verify-modal {
     1873    position: fixed;
     1874    inset: 0;
     1875    z-index: 100100;
     1876    display: flex;
     1877    align-items: center;
     1878    justify-content: center;
     1879    padding: var(--aidf-sp-5);
     1880    animation: aidfModalFadeIn 0.2s ease-out;
     1881}
     1882
     1883.aidf-verify-modal__backdrop {
     1884    position: absolute;
     1885    inset: 0;
     1886    background: rgba(0, 0, 0, 0.6);
     1887    backdrop-filter: blur(4px);
     1888    -webkit-backdrop-filter: blur(4px);
     1889}
     1890
     1891/* Card */
     1892.aidf-verify-modal__card {
     1893    position: relative;
     1894    width: 100%;
     1895    max-width: 580px;
     1896    background: var(--aidf-white);
     1897    border-radius: var(--aidf-radius-lg);
     1898    box-shadow: 0 24px 80px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(0, 0, 0, 0.05);
     1899    overflow: hidden;
     1900    animation: aidfModalSlideUp 0.25s ease-out;
     1901}
     1902
     1903/* Header */
     1904.aidf-verify-modal__header {
     1905    display: flex;
     1906    align-items: center;
     1907    justify-content: space-between;
     1908    padding: var(--aidf-sp-5) var(--aidf-sp-6);
     1909    background: linear-gradient(135deg, var(--aidf-dark) 0%, var(--aidf-dark-mid) 100%);
     1910    color: var(--aidf-text-inverse);
     1911}
     1912
     1913.aidf-verify-modal__header-content {
     1914    display: flex;
     1915    align-items: center;
     1916    gap: var(--aidf-sp-3);
     1917}
     1918
     1919.aidf-verify-modal__header-icon {
     1920    display: flex;
     1921    align-items: center;
     1922    justify-content: center;
     1923    width: 44px;
     1924    height: 44px;
     1925    background: linear-gradient(135deg, var(--aidf-orange) 0%, #ff9d42 100%);
     1926    border-radius: 10px;
     1927    flex-shrink: 0;
     1928}
     1929
     1930.aidf-verify-modal__header-icon .dashicons {
     1931    width: 22px;
     1932    height: 22px;
     1933    font-size: 22px;
     1934    color: var(--aidf-dark);
     1935}
     1936
     1937.aidf-verify-modal__title {
     1938    font-size: 18px;
     1939    font-weight: 700;
     1940    color: var(--aidf-text-inverse);
     1941    letter-spacing: -0.01em;
     1942    line-height: 1.3;
     1943}
     1944
     1945.aidf-verify-modal__subtitle {
     1946    margin: 2px 0 0;
     1947    font-size: 12px;
     1948    color: rgba(255, 255, 255, 0.6);
     1949    font-weight: 500;
     1950    text-transform: uppercase;
     1951    letter-spacing: 0.04em;
     1952}
     1953
     1954.aidf-verify-modal__close {
     1955    display: flex;
     1956    align-items: center;
     1957    justify-content: center;
     1958    width: 32px;
     1959    height: 32px;
     1960    background: rgba(255, 255, 255, 0.1);
     1961    border: none;
     1962    border-radius: 6px;
     1963    cursor: pointer;
     1964    color: rgba(255, 255, 255, 0.7);
     1965    transition: all 0.15s ease;
     1966}
     1967
     1968.aidf-verify-modal__close:hover {
     1969    background: rgba(255, 255, 255, 0.2);
     1970    color: var(--aidf-text-inverse);
     1971}
     1972
     1973.aidf-verify-modal__close .dashicons {
     1974    width: 20px;
     1975    height: 20px;
     1976    font-size: 20px;
     1977}
     1978
     1979/* Body */
     1980.aidf-verify-modal__body {
     1981    padding: var(--aidf-sp-6);
     1982}
     1983
     1984.aidf-verify-modal__intro {
     1985    margin-bottom: var(--aidf-sp-5);
     1986}
     1987
     1988.aidf-verify-modal__intro p {
     1989    margin: 0 0 var(--aidf-sp-2) 0;
     1990    font-size: var(--aidf-text-sm);
     1991    color: var(--aidf-text-secondary);
     1992    line-height: 1.6;
     1993}
     1994
     1995.aidf-verify-modal__intro p:last-child {
     1996    margin-bottom: 0;
     1997}
     1998
     1999/* Steps */
     2000.aidf-verify-modal__steps {
     2001    display: flex;
     2002    flex-direction: column;
     2003    gap: var(--aidf-sp-3);
     2004    margin-bottom: var(--aidf-sp-5);
     2005    padding: var(--aidf-sp-4);
     2006    background: var(--aidf-bg);
     2007    border-radius: var(--aidf-radius);
     2008    border: 1px solid var(--aidf-border-light);
     2009}
     2010
     2011.aidf-verify-modal__step {
     2012    display: flex;
     2013    align-items: center;
     2014    gap: var(--aidf-sp-3);
     2015}
     2016
     2017.aidf-verify-modal__step-number {
     2018    display: flex;
     2019    align-items: center;
     2020    justify-content: center;
     2021    width: 24px;
     2022    height: 24px;
     2023    background: var(--aidf-orange);
     2024    color: var(--aidf-text-on-orange);
     2025    border-radius: 50%;
     2026    font-size: 12px;
     2027    font-weight: 700;
     2028    flex-shrink: 0;
     2029    line-height: 1;
     2030}
     2031
     2032.aidf-verify-modal__step-text {
     2033    font-size: var(--aidf-text-sm);
     2034    color: var(--aidf-text);
     2035    line-height: 1.4;
     2036}
     2037
     2038.aidf-verify-modal__step-text a {
     2039    color: var(--aidf-orange);
     2040    font-weight: 600;
     2041    text-decoration: none;
     2042}
     2043
     2044.aidf-verify-modal__step-text a:hover {
     2045    color: var(--aidf-orange-hover);
     2046    text-decoration: underline;
     2047}
     2048
     2049/* Code input */
     2050.aidf-verify-modal__field {
     2051    margin-bottom: var(--aidf-sp-4);
     2052}
     2053
     2054.aidf-verify-modal__label {
     2055    display: block;
     2056    margin-bottom: var(--aidf-sp-2);
     2057    font-size: var(--aidf-text-sm);
     2058    font-weight: 600;
     2059    color: var(--aidf-text);
     2060}
     2061
     2062.aidf-verify-modal__input {
     2063    width: 100%;
     2064    padding: 10px 14px;
     2065    font-size: 14px;
     2066    font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
     2067    letter-spacing: 0.02em;
     2068    color: var(--aidf-text);
     2069    background: var(--aidf-white);
     2070    border: 1px solid var(--aidf-border);
     2071    border-radius: var(--aidf-radius);
     2072    outline: none;
     2073    transition: border-color 0.15s ease, box-shadow 0.15s ease;
     2074    box-sizing: border-box;
     2075}
     2076
     2077.aidf-verify-modal__input:focus {
     2078    border-color: var(--aidf-orange);
     2079    box-shadow: 0 0 0 3px var(--aidf-orange-glow);
     2080}
     2081
     2082.aidf-verify-modal__input::placeholder {
     2083    color: var(--aidf-text-tertiary);
     2084    font-family: var(--aidf-font);
     2085    letter-spacing: normal;
     2086}
     2087
     2088/* Status */
     2089.aidf-verify-modal__status {
     2090    display: flex;
     2091    align-items: center;
     2092    gap: var(--aidf-sp-2);
     2093    padding: var(--aidf-sp-3) var(--aidf-sp-4);
     2094    border-radius: var(--aidf-radius);
     2095    font-size: var(--aidf-text-sm);
     2096    color: var(--aidf-text-secondary);
     2097    background: var(--aidf-bg);
     2098    border: 1px solid var(--aidf-border-light);
     2099}
     2100
     2101.aidf-verify-modal__status .dashicons {
     2102    width: 18px;
     2103    height: 18px;
     2104    font-size: 18px;
     2105    flex-shrink: 0;
     2106}
     2107
     2108.aidf-verify-modal__status--active {
     2109    background: var(--aidf-green-bg);
     2110    border-color: var(--aidf-green-border);
     2111    color: #15803d;
     2112}
     2113
     2114.aidf-verify-modal__status--active .dashicons {
     2115    color: var(--aidf-green);
     2116}
     2117
     2118.aidf-verify-modal__status a {
     2119    color: var(--aidf-orange);
     2120    font-weight: 500;
     2121    word-break: break-all;
     2122}
     2123
     2124/* Footer */
     2125.aidf-verify-modal__footer {
     2126    display: flex;
     2127    align-items: center;
     2128    justify-content: space-between;
     2129    padding: var(--aidf-sp-4) var(--aidf-sp-6);
     2130    background: var(--aidf-bg);
     2131    border-top: 1px solid var(--aidf-border-light);
     2132}
     2133
     2134.aidf-verify-modal__footer-right {
     2135    display: flex;
     2136    gap: var(--aidf-sp-2);
     2137}
     2138
     2139.aidf-verify-modal__dashboard-link {
     2140    display: inline-flex;
     2141    align-items: center;
     2142    gap: 4px;
     2143    font-size: var(--aidf-text-sm);
     2144    color: var(--aidf-text-secondary);
     2145    text-decoration: none;
     2146    transition: color 0.15s ease;
     2147}
     2148
     2149.aidf-verify-modal__dashboard-link:hover {
     2150    color: var(--aidf-orange);
     2151}
     2152
     2153.aidf-verify-modal__dashboard-link .dashicons {
     2154    width: 14px;
     2155    height: 14px;
     2156    font-size: 14px;
     2157}
     2158
     2159.aidf-verify-modal__footer .aidf-btn .dashicons {
     2160    width: 16px;
     2161    height: 16px;
     2162    font-size: 16px;
     2163    margin-right: 2px;
     2164}
     2165
     2166/* Animations */
     2167@keyframes aidfModalFadeIn {
     2168    from { opacity: 0; }
     2169    to { opacity: 1; }
     2170}
     2171
     2172@keyframes aidfModalSlideUp {
     2173    from { opacity: 0; transform: translateY(12px) scale(0.98); }
     2174    to { opacity: 1; transform: translateY(0) scale(1); }
     2175}
     2176
     2177/* --------------------------------------------------------
     2178   23b. CTA Verify Button Variant
     2179   -------------------------------------------------------- */
     2180.aidf-directory-cta__actions {
     2181    display: flex;
     2182    gap: var(--aidf-sp-2);
     2183    flex-shrink: 0;
     2184}
     2185
     2186.aidf-directory-cta__button--verify {
     2187    background: transparent;
     2188    border: 2px solid rgba(255, 255, 255, 0.5);
     2189    color: var(--aidf-text-inverse);
     2190    cursor: pointer;
     2191}
     2192
     2193.aidf-directory-cta__button--verify:hover {
     2194    background: rgba(255, 255, 255, 0.15);
     2195    border-color: rgba(255, 255, 255, 0.8);
     2196    color: var(--aidf-text-inverse) !important;
     2197    transform: translateY(-1px);
     2198}
     2199
     2200.aidf-directory-cta__button--verify .dashicons {
     2201    width: 16px;
     2202    height: 16px;
     2203    font-size: 16px;
     2204}
     2205
     2206/* --------------------------------------------------------
     2207   23c. Inline Verification Panel (Status Tab)
     2208   -------------------------------------------------------- */
     2209.aidf-verify-panel {
     2210    margin-top: var(--aidf-sp-5);
     2211}
     2212
     2213.aidf-verify-panel .aidf-panel-title .dashicons {
     2214    margin-right: 6px;
     2215    color: var(--aidf-orange);
     2216}
     2217
     2218.aidf-verify-inline {
     2219    display: flex;
     2220    align-items: center;
     2221    justify-content: space-between;
     2222    gap: var(--aidf-sp-4);
     2223}
     2224
     2225.aidf-verify-inline__status {
     2226    display: flex;
     2227    align-items: center;
     2228    gap: var(--aidf-sp-2);
     2229    font-size: var(--aidf-text-sm);
     2230}
     2231
     2232.aidf-verify-inline__status--active {
     2233    color: #15803d;
     2234}
     2235
     2236.aidf-verify-inline__status--active .dashicons {
     2237    color: var(--aidf-green);
     2238}
     2239
     2240.aidf-verify-inline__status a {
     2241    color: var(--aidf-orange);
     2242}
     2243
     2244.aidf-verify-inline__description {
     2245    margin: 0;
     2246    font-size: var(--aidf-text-sm);
     2247    color: var(--aidf-text-secondary);
     2248}
     2249
     2250/* --------------------------------------------------------
     2251   24. Responsive
     2252   -------------------------------------------------------- */
     2253@media (max-width: 960px) {
     2254    .aidf-header {
     2255        padding: var(--aidf-sp-4) var(--aidf-sp-5);
     2256    }
     2257
     2258    .aidf-tabs {
     2259        padding: 0 var(--aidf-sp-4);
     2260        overflow-x: auto;
     2261    }
     2262
     2263    .aidf-content {
     2264        padding: var(--aidf-sp-5);
     2265    }
     2266
     2267    .aidf-file-grid {
     2268        grid-template-columns: 1fr;
     2269    }
     2270}
     2271
    18672272@media (max-width: 782px) {
     2273    .aidf-wrap {
     2274        margin-left: -10px;
     2275    }
     2276
     2277    .aidf-tabs {
     2278        top: 46px; /* Mobile admin bar height */
     2279    }
     2280
     2281    .aidf-header-right {
     2282        display: none;
     2283    }
     2284
     2285    .aidf-header-version {
     2286        display: none;
     2287    }
     2288
    18682289    .aidf-directory-cta__content {
    18692290        flex-direction: column;
     
    18722293    }
    18732294
     2295    .aidf-directory-cta__actions {
     2296        flex-direction: column;
     2297        width: 100%;
     2298    }
     2299
    18742300    .aidf-directory-cta__button {
    18752301        width: 100%;
    18762302        justify-content: center;
    18772303    }
    1878 }
    1879 
    1880 /* --------------------------------------------------------
    1881    23. Responsive (formerly 22)
    1882    -------------------------------------------------------- */
    1883 @media (max-width: 960px) {
    1884     .aidf-header {
    1885         padding: var(--aidf-sp-4) var(--aidf-sp-5);
     2304
     2305    .aidf-verify-modal__card {
     2306        max-height: 90vh;
     2307        overflow-y: auto;
    18862308    }
    18872309
    1888     .aidf-tabs {
    1889         padding: 0 var(--aidf-sp-4);
    1890         overflow-x: auto;
     2310    .aidf-verify-modal__footer {
     2311        flex-direction: column;
     2312        gap: var(--aidf-sp-3);
    18912313    }
    18922314
    1893     .aidf-content {
    1894         padding: var(--aidf-sp-5);
     2315    .aidf-verify-modal__footer-right {
     2316        width: 100%;
    18952317    }
    18962318
    1897     .aidf-file-grid {
    1898         grid-template-columns: 1fr;
     2319    .aidf-verify-modal__footer-right .aidf-btn {
     2320        flex: 1;
    18992321    }
    1900 }
    1901 
    1902 @media (max-width: 782px) {
    1903     .aidf-wrap {
    1904         margin-left: -10px;
     2322
     2323    .aidf-verify-inline {
     2324        flex-direction: column;
     2325        align-items: flex-start;
    19052326    }
    1906 
    1907     .aidf-tabs {
    1908         top: 46px; /* Mobile admin bar height */
    1909     }
    1910 
    1911     .aidf-header-right {
    1912         display: none;
    1913     }
    1914 
    1915     .aidf-header-version {
    1916         display: none;
    1917     }
    1918 }
    1919 
    1920 /* --------------------------------------------------------
    1921    23. WordPress Admin Overrides
     2327}
     2328
     2329/* --------------------------------------------------------
     2330   25. WordPress Admin Overrides
    19222331   -------------------------------------------------------- */
    19232332
     
    19482357
    19492358/* --------------------------------------------------------
    1950    24. Welcome Notice (global WP admin)
     2359   26. Welcome Notice (global WP admin)
    19512360   -------------------------------------------------------- */
    19522361.aidf-welcome-notice {
  • ai-discovery-files/trunk/admin/js/admin.js

    r3472044 r3472434  
    461461        });
    462462
     463        /* -------------------------------------------------------
     464           Domain Verification Modal
     465           ------------------------------------------------------- */
     466        var verifyModal = $('#aidf-verify-modal');
     467
     468        function openVerifyModal() {
     469            verifyModal.show();
     470            $('body').css('overflow', 'hidden');
     471            verifyModal.find('#aidf-verify-code').focus();
     472        }
     473
     474        function closeVerifyModal() {
     475            verifyModal.hide();
     476            $('body').css('overflow', '');
     477        }
     478
     479        // Open from CTA button or Status tab panel button.
     480        $(document).on('click', '#aidf-open-verify-cta, #aidf-open-verify-panel', function (e) {
     481            e.preventDefault();
     482            openVerifyModal();
     483        });
     484
     485        // Close: X button, backdrop, Cancel button.
     486        verifyModal.on('click', '[data-aidf-close-modal]', function () {
     487            closeVerifyModal();
     488        });
     489
     490        // Close: Escape key.
     491        $(document).on('keydown', function (e) {
     492            if (e.key === 'Escape' && verifyModal.is(':visible')) {
     493                closeVerifyModal();
     494            }
     495        });
     496
     497        // Save verification code via AJAX.
     498        $('#aidf-verify-save').on('click', function () {
     499            var $btn = $(this);
     500            var code = $('#aidf-verify-code').val().trim();
     501            var $status = $('#aidf-verify-status');
     502
     503            $btn.prop('disabled', true).find('.dashicons').removeClass('dashicons-yes').addClass('dashicons-update');
     504
     505            $.post(aidfAdmin.ajaxUrl, {
     506                action: 'aidf_save_verify_code',
     507                nonce: aidfAdmin.nonce,
     508                verify_code: code
     509            }, function (response) {
     510                $btn.prop('disabled', false).find('.dashicons').removeClass('dashicons-update').addClass('dashicons-yes');
     511
     512                if (!response.success) {
     513                    $status
     514                        .removeClass('aidf-verify-modal__status--active')
     515                        .html('<span class="dashicons dashicons-warning"></span> <span>' + escAttr(response.data) + '</span>');
     516                    return;
     517                }
     518
     519                var data = response.data;
     520
     521                if (data.active) {
     522                    $status
     523                        .addClass('aidf-verify-modal__status--active')
     524                        .html(
     525                            '<span class="dashicons dashicons-yes-alt"></span> ' +
     526                            '<span>' + escAttr(aidfAdmin.i18n.verifyFileActive) + ' ' +
     527                            '<a href="' + escAttr(data.url) + '" target="_blank" rel="noopener">' + escAttr(data.url) + '</a></span>'
     528                        );
     529                } else {
     530                    $status
     531                        .removeClass('aidf-verify-modal__status--active')
     532                        .html('<span class="dashicons dashicons-info-outline"></span> <span>' + escAttr(aidfAdmin.i18n.verifyFileInactive) + '</span>');
     533                }
     534
     535                // Flash the save button green briefly.
     536                $btn.addClass('aidf-btn--success');
     537                setTimeout(function () {
     538                    $btn.removeClass('aidf-btn--success');
     539                }, 1500);
     540            }).fail(function () {
     541                $btn.prop('disabled', false).find('.dashicons').removeClass('dashicons-update').addClass('dashicons-yes');
     542            });
     543        });
     544
    463545    }); // end DOM ready
    464546
  • ai-discovery-files/trunk/admin/views/partials/directory-cta.php

    r3472044 r3472434  
    4646        </div>
    4747
    48         <div class="aidf-directory-cta__action">
     48        <div class="aidf-directory-cta__actions">
    4949            <a href="https://www.ai-visibility.org.uk/submit/"
    5050               class="aidf-directory-cta__button"
     
    5454                <span class="dashicons dashicons-external"></span>
    5555            </a>
     56            <button type="button" class="aidf-directory-cta__button aidf-directory-cta__button--verify" id="aidf-open-verify-cta">
     57                <span class="dashicons dashicons-shield-alt"></span>
     58                <?php esc_html_e( 'Verify Domain', 'ai-discovery-files' ); ?>
     59            </button>
    5660        </div>
    5761    </div>
  • ai-discovery-files/trunk/admin/views/settings-page.php

    r3472044 r3472434  
    9595                    ?>
    9696                    <input type="hidden" name="aidf_settings[spec_attribution]" value="<?php echo esc_attr( $settings['spec_attribution'] ? '1' : '0' ); ?>" class="aidf-hidden-attribution">
     97                    <input type="hidden" name="aidf_settings[verify_code]" value="<?php echo esc_attr( isset( $settings['verify_code'] ) ? $settings['verify_code'] : '' ); ?>">
    9798                <?php endif; ?>
    9899
     
    114115        <?php endif; ?>
    115116    </div>
     117
     118    <?php include AIDF_PLUGIN_DIR . 'admin/views/partials/verify-modal.php'; ?>
    116119</div>
  • ai-discovery-files/trunk/admin/views/tab-status.php

    r3472044 r3472434  
    8181
    8282    <input type="hidden" name="aidf_settings[spec_attribution]" value="<?php echo esc_attr( $settings['spec_attribution'] ? '1' : '0' ); ?>">
     83    <input type="hidden" name="aidf_settings[verify_code]" value="<?php echo esc_attr( isset( $settings['verify_code'] ) ? $settings['verify_code'] : '' ); ?>">
    8384
    8485    <?php foreach ( $tiers as $tier_slug => $tier ) : ?>
     
    175176    </div>
    176177</div>
     178
     179<!-- Domain Verification -->
     180<?php
     181$verify_code   = isset( $settings['verify_code'] ) ? $settings['verify_code'] : '';
     182$verify_active = ! empty( $verify_code );
     183?>
     184<div class="aidf-panel aidf-verify-panel">
     185    <div class="aidf-panel-header">
     186        <div>
     187            <h2 class="aidf-panel-title">
     188                <span class="dashicons dashicons-shield-alt"></span>
     189                <?php esc_html_e( 'Directory Verification', 'ai-discovery-files' ); ?>
     190                <?php if ( $verify_active ) : ?>
     191                    <span class="aidf-badge aidf-badge--essential"><?php esc_html_e( 'active', 'ai-discovery-files' ); ?></span>
     192                <?php endif; ?>
     193            </h2>
     194            <p class="aidf-panel-subtitle"><?php esc_html_e( 'Prove domain ownership to manage your listing in the AI Visibility Directory.', 'ai-discovery-files' ); ?></p>
     195        </div>
     196    </div>
     197    <div class="aidf-panel-body">
     198        <div class="aidf-verify-inline">
     199            <?php if ( $verify_active ) : ?>
     200                <div class="aidf-verify-inline__status aidf-verify-inline__status--active">
     201                    <span class="dashicons dashicons-yes-alt"></span>
     202                    <span>
     203                        <?php esc_html_e( 'Verification file is live at', 'ai-discovery-files' ); ?>
     204                        <a href="<?php echo esc_url( home_url( '/ai-visibility-verify.txt' ) ); ?>" target="_blank" rel="noopener">
     205                            /ai-visibility-verify.txt &#8599;
     206                        </a>
     207                    </span>
     208                </div>
     209            <?php else : ?>
     210                <p class="aidf-verify-inline__description">
     211                    <?php esc_html_e( 'After submitting your site to the directory, enter your verification code here so the plugin can serve the ownership file automatically.', 'ai-discovery-files' ); ?>
     212                </p>
     213            <?php endif; ?>
     214            <button type="button" class="aidf-btn aidf-btn--secondary" id="aidf-open-verify-panel">
     215                <span class="dashicons dashicons-shield-alt"></span>
     216                <?php
     217                echo $verify_active
     218                    ? esc_html__( 'Update Verification Code', 'ai-discovery-files' )
     219                    : esc_html__( 'Enter Verification Code', 'ai-discovery-files' );
     220                ?>
     221            </button>
     222        </div>
     223    </div>
     224</div>
  • ai-discovery-files/trunk/ai-discovery-files.php

    r3472087 r3472434  
    44 * Plugin URI:        https://www.ai-visibility.org.uk/wordpress-plugin/ai-discovery-files/
    55 * Description:       Improve your AI Visibility by generating AI Discovery Files so AI systems like ChatGPT, Claude, and Gemini can correctly discover, interpret, and cite your website.
    6  * Version:           1.0.0
     6 * Version:           1.1.0
    77 * Requires at least: 6.2
    88 * Requires PHP:      8.0
     
    2424 * Plugin constants.
    2525 */
    26 define( 'AIDF_VERSION', '1.0.0' );
     26define( 'AIDF_VERSION', '1.1.0' );
    2727define( 'AIDF_PLUGIN_FILE', __FILE__ );
    2828define( 'AIDF_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
  • ai-discovery-files/trunk/includes/class-plugin.php

    r3472044 r3472434  
    123123            'active_files'       => array( 'llms-txt', 'ai-txt' ),
    124124            'spec_attribution'   => false,
     125
     126            // Directory verification.
     127            'verify_code'        => '',
    125128        );
    126129
  • ai-discovery-files/trunk/includes/class-server.php

    r3472044 r3472434  
    5757            );
    5858        }
     59
     60        // Domain verification file for AI Visibility Directory.
     61        add_rewrite_rule(
     62            '^ai-visibility-verify\.txt$',
     63            'index.php?aidf_file=verify',
     64            'top'
     65        );
    5966    }
    6067
     
    8087        if ( empty( $file_slug ) ) {
    8188            return;
     89        }
     90
     91        // Handle verification file separately (not an AI Discovery File).
     92        if ( 'verify' === $file_slug ) {
     93            $settings    = AIDF_Plugin::get_settings();
     94            $verify_code = isset( $settings['verify_code'] ) ? $settings['verify_code'] : '';
     95
     96            if ( empty( $verify_code ) ) {
     97                status_header( 404 );
     98                nocache_headers();
     99                echo esc_html__( '404 — Verification file not configured.', 'ai-discovery-files' );
     100                exit;
     101            }
     102
     103            status_header( 200 );
     104            header( 'Content-Type: text/plain; charset=utf-8' );
     105            header( 'Cache-Control: no-store' );
     106            header( 'X-Robots-Tag: noindex' );
     107            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Plain text output, validated on save.
     108            echo $verify_code;
     109            exit;
    82110        }
    83111
  • ai-discovery-files/trunk/readme.txt

    r3472165 r3472434  
    33Tags: ai, llms.txt, ai visibility, seo, chatgpt
    44Requires at least: 6.2
    5 Tested up to: 6.9.1
     5Tested up to: 6.9
    66Requires PHP: 8.0
    7 Stable tag: 1.0.0
     7Stable tag: 1.1.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    120120== Changelog ==
    121121
     122= 1.1.0 =
     123* Add domain verification for the AI Visibility Directory
     124* New verification modal with step-by-step instructions
     125* Serve ai-visibility-verify.txt with user-supplied code
     126* Verify Domain button in directory CTA banner
     127* Verification panel on Status tab
     128* AJAX save for verification code (no page reload)
     129
    122130= 1.0.0 =
    123131* Initial release
     
    133141== Upgrade Notice ==
    134142
     143= 1.1.0 =
     144New: Domain verification for the AI Visibility Directory. Serve your verification file directly from WordPress — no FTP needed.
     145
    135146= 1.0.0 =
    136147Initial release. Install and configure your AI Discovery Files.
Note: See TracChangeset for help on using the changeset viewer.