Changeset 3492952
- Timestamp:
- 03/27/2026 07:41:43 PM (2 days ago)
- Location:
- efficiencynext-data-connector/trunk
- Files:
-
- 2 edited
-
efficiencynext-data-connector.php (modified) (23 diffs)
-
readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
efficiencynext-data-connector/trunk/efficiencynext-data-connector.php
r3474007 r3492952 703 703 } 704 704 705 $data = $this->fetch_json( $uri );705 $data = $this->fetch_json( $uri, $source ); 706 706 if ( null === $data ) { 707 707 $data = array(); … … 838 838 * Fetch JSON from URL using WordPress HTTP API. 839 839 * 840 * @param string $url URL to fetch. 840 * @param string $url URL to fetch. 841 * @param array $source Optional source config for authentication. 842 * @param string $error Optional reference populated with a human-readable error on failure. 841 843 * @return array|null JSON data or null on failure. 842 844 */ 843 private function fetch_json( string $url ): ?array { 845 private function fetch_json( string $url, array $source = array(), string &$error = '' ): ?array { 846 $headers = array( 847 'Accept' => 'application/json', 848 'User-Agent' => 'EfficiencyNext-Data-Connector/' . self::VERSION . ' WordPress/' . get_bloginfo( 'version' ), 849 ); 850 851 $auth_type = $source['auth_type'] ?? 'none'; 852 853 if ( 'basic' === $auth_type ) { 854 $username = $source['auth_username'] ?? ''; 855 $password = $source['auth_password'] ?? ''; 856 if ( '' !== $username || '' !== $password ) { 857 // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode 858 $headers['Authorization'] = 'Basic ' . base64_encode( $username . ':' . $password ); 859 } 860 } elseif ( 'apikey' === $auth_type ) { 861 $key_name = $source['auth_key_name'] ?? ''; 862 $key_value = $source['auth_key_value'] ?? ''; 863 $key_placement = $source['auth_key_placement'] ?? 'header'; 864 865 // Last-resort guard: the sentinel should never reach the HTTP layer. 866 if ( '__UNCHANGED__' === $key_value ) { 867 $key_value = ''; 868 } 869 870 if ( '' !== $key_name && '' !== $key_value ) { 871 if ( 'header' === $key_placement ) { 872 $headers[ $key_name ] = $key_value; 873 } elseif ( 'query' === $key_placement ) { 874 $separator = str_contains( $url, '?' ) ? '&' : '?'; 875 $url .= $separator . rawurlencode( $key_name ) . '=' . rawurlencode( $key_value ); 876 } 877 } 878 } 879 844 880 $response = wp_remote_get( 845 881 $url, … … 847 883 'timeout' => 15, 848 884 'redirection' => 3, 849 'headers' => array( 850 'Accept' => 'application/json', 851 'User-Agent' => 'EfficiencyNext-Data-Connector/' . self::VERSION . ' WordPress/' . get_bloginfo( 'version' ), 852 ), 885 'headers' => $headers, 853 886 ) 854 887 ); 855 888 856 889 if ( is_wp_error( $response ) ) { 857 $this->log_error( 'Fetch error: ' . $response->get_error_message() ); 890 $error = $response->get_error_message(); 891 $this->log_error( 'Fetch error: ' . $error ); 858 892 return null; 859 893 } … … 861 895 $code = wp_remote_retrieve_response_code( $response ); 862 896 if ( $code < 200 || $code >= 300 ) { 897 $error = 'HTTP ' . $code; 863 898 $this->log_error( 'HTTP error: ' . $code ); 864 899 return null; … … 867 902 $body = wp_remote_retrieve_body( $response ); 868 903 if ( empty( $body ) ) { 904 $error = 'Empty response body'; 869 905 return null; 870 906 } … … 872 908 $data = json_decode( $body, true ); 873 909 if ( JSON_ERROR_NONE !== json_last_error() ) { 910 $error = 'JSON parse error: ' . json_last_error_msg(); 874 911 $this->log_error( 'JSON error: ' . json_last_error_msg() ); 875 912 return null; … … 968 1005 'requestFailed' => __( 'Request failed.', 'efficiencynext-data-connector' ), 969 1006 'insertTooltip' => __( 'Click to insert into template', 'efficiencynext-data-connector' ), 1007 'authNone' => __( 'No Authentication', 'efficiencynext-data-connector' ), 1008 'authBasic' => __( 'Basic Auth', 'efficiencynext-data-connector' ), 1009 'authApiKey' => __( 'API Key', 'efficiencynext-data-connector' ), 1010 'authPlacementHeader' => __( 'Header', 'efficiencynext-data-connector' ), 1011 'authPlacementQuery' => __( 'Query Parameter', 'efficiencynext-data-connector' ), 1012 'passwordStored' => __( '(stored — leave blank to keep unchanged)', 'efficiencynext-data-connector' ), 970 1013 ), 971 1014 ) … … 998 1041 $api_url = $this->sanitize_api_url( wp_unslash( $_POST['api_url'] ?? '' ) ); 999 1042 $template = $this->sanitize_template( wp_unslash( $_POST['template'] ?? '' ) ); 1000 $inline = ( sanitize_text_field( wp_unslash( $_POST['inline'] ?? '0' ) ) ) === '1'; 1043 $inline = ( sanitize_text_field( wp_unslash( $_POST['inline'] ?? '0' ) ) ) === '1'; 1044 1045 // Auth fields. 1046 $allowed_auth_types = array( 'none', 'basic', 'apikey' ); 1047 $auth_type = sanitize_text_field( wp_unslash( $_POST['auth_type'] ?? 'none' ) ); 1048 if ( ! in_array( $auth_type, $allowed_auth_types, true ) ) { 1049 $auth_type = 'none'; 1050 } 1051 $auth_username = sanitize_text_field( wp_unslash( $_POST['auth_username'] ?? '' ) ); 1052 $auth_key_name = sanitize_text_field( wp_unslash( $_POST['auth_key_name'] ?? '' ) ); 1053 $auth_key_placement = sanitize_text_field( wp_unslash( $_POST['auth_key_placement'] ?? 'header' ) ); 1054 if ( ! in_array( $auth_key_placement, array( 'header', 'query' ), true ) ) { 1055 $auth_key_placement = 'header'; 1056 } 1057 1058 // For password/API key value: if the submitted value is the mask sentinel, preserve the existing stored value. 1059 $mask_sentinel = '__UNCHANGED__'; 1060 $auth_password = wp_unslash( $_POST['auth_password'] ?? '' ); 1061 $auth_key_value = wp_unslash( $_POST['auth_key_value'] ?? '' ); 1062 1063 // If editing and the field comes back as the mask sentinel, keep whatever was stored before. 1064 if ( ! empty( $id ) ) { 1065 foreach ( $sources as $existing ) { 1066 if ( ( $existing['id'] ?? '' ) === $id ) { 1067 if ( $mask_sentinel === $auth_password ) { 1068 $auth_password = $existing['auth_password'] ?? ''; 1069 } 1070 if ( $mask_sentinel === $auth_key_value ) { 1071 $auth_key_value = $existing['auth_key_value'] ?? ''; 1072 } 1073 break; 1074 } 1075 } 1076 } 1077 1078 // If the sentinel was not resolved (e.g. brand-new source with an empty secret field), 1079 // treat it as an intentionally empty value — never persist the sentinel literal. 1080 if ( $mask_sentinel === $auth_password ) { 1081 $auth_password = ''; 1082 } 1083 if ( $mask_sentinel === $auth_key_value ) { 1084 $auth_key_value = ''; 1085 } 1086 1087 // Sanitize — these are stored as-is (plain text in wp_options, protected by WP access controls). 1088 $auth_password = sanitize_text_field( $auth_password ); 1089 $auth_key_value = sanitize_text_field( $auth_key_value ); 1001 1090 1002 1091 // Validate required fields. … … 1021 1110 1022 1111 $source_data = array( 1023 'id' => ! empty( $id ) ? $id : wp_generate_uuid4(), 1024 'name' => $name, 1025 'placeholder' => $placeholder, 1026 'api_url' => $api_url, 1027 'template' => $template, 1028 'inline' => $inline, 1112 'id' => ! empty( $id ) ? $id : wp_generate_uuid4(), 1113 'name' => $name, 1114 'placeholder' => $placeholder, 1115 'api_url' => $api_url, 1116 'template' => $template, 1117 'inline' => $inline, 1118 'auth_type' => $auth_type, 1119 'auth_username' => $auth_username, 1120 'auth_password' => $auth_password, 1121 'auth_key_name' => $auth_key_name, 1122 'auth_key_value' => $auth_key_value, 1123 'auth_key_placement' => $auth_key_placement, 1029 1124 ); 1030 1125 … … 1089 1184 } 1090 1185 1091 wp_send_json_success( array( 'sources' => $this->get_sources() ) ); 1186 // Return sources with secrets masked — the JS layer uses the __UNCHANGED__ sentinel 1187 // to preserve the stored value when the user saves without re-entering credentials. 1188 $sources = $this->get_sources(); 1189 $masked = array_map( function ( $s ) { 1190 if ( ! empty( $s['auth_password'] ) ) { 1191 $s['auth_password'] = '__UNCHANGED__'; 1192 } 1193 if ( ! empty( $s['auth_key_value'] ) ) { 1194 $s['auth_key_value'] = '__UNCHANGED__'; 1195 } 1196 return $s; 1197 }, $sources ); 1198 1199 wp_send_json_success( array( 'sources' => $masked ) ); 1092 1200 } 1093 1201 … … 1109 1217 } 1110 1218 1219 // Build a temporary source config for auth. 1220 $allowed_auth_types = array( 'none', 'basic', 'apikey' ); 1221 $auth_type = sanitize_text_field( wp_unslash( $_POST['auth_type'] ?? 'none' ) ); 1222 if ( ! in_array( $auth_type, $allowed_auth_types, true ) ) { 1223 $auth_type = 'none'; 1224 } 1225 $preview_source = array( 1226 'auth_type' => $auth_type, 1227 'auth_username' => sanitize_text_field( wp_unslash( $_POST['auth_username'] ?? '' ) ), 1228 'auth_password' => sanitize_text_field( wp_unslash( $_POST['auth_password'] ?? '' ) ), 1229 'auth_key_name' => sanitize_text_field( wp_unslash( $_POST['auth_key_name'] ?? '' ) ), 1230 'auth_key_value' => sanitize_text_field( wp_unslash( $_POST['auth_key_value'] ?? '' ) ), 1231 'auth_key_placement' => sanitize_text_field( wp_unslash( $_POST['auth_key_placement'] ?? 'header' ) ), 1232 ); 1233 1234 // If credentials come in as the mask sentinel, load them from the saved source. 1235 $source_id = sanitize_text_field( wp_unslash( $_POST['source_id'] ?? '' ) ); 1236 if ( ! empty( $source_id ) ) { 1237 $sources = $this->get_sources(); 1238 foreach ( $sources as $existing ) { 1239 if ( ( $existing['id'] ?? '' ) === $source_id ) { 1240 if ( '__UNCHANGED__' === $preview_source['auth_password'] ) { 1241 $preview_source['auth_password'] = $existing['auth_password'] ?? ''; 1242 } 1243 if ( '__UNCHANGED__' === $preview_source['auth_key_value'] ) { 1244 $preview_source['auth_key_value'] = $existing['auth_key_value'] ?? ''; 1245 } 1246 break; 1247 } 1248 } 1249 } 1250 1111 1251 // Remove mustache tags for preview. 1112 1252 $url = preg_replace( '/\{\{[\w.]+\}\}/', '', $url ); 1113 1253 $url = esc_url_raw( $url ); 1114 1254 1115 $data = $this->fetch_json( $url ); 1255 $fetch_error = ''; 1256 $data = $this->fetch_json( $url, $preview_source, $fetch_error ); 1116 1257 if ( null === $data ) { 1117 wp_send_json_error( array( 'message' => __( 'Could not fetch data from the API. Check the URL and try again.', 'efficiencynext-data-connector' ) ) ); 1258 $message = __( 'Could not fetch data from the API.', 'efficiencynext-data-connector' ); 1259 if ( ! empty( $fetch_error ) ) { 1260 $message .= ' ' . sprintf( 1261 /* translators: %s: error detail such as "HTTP 401" */ 1262 __( 'Server responded: %s', 'efficiencynext-data-connector' ), 1263 $fetch_error 1264 ); 1265 // Give targeted hints for common HTTP auth errors. 1266 if ( str_starts_with( $fetch_error, 'HTTP 401' ) || str_starts_with( $fetch_error, 'HTTP 403' ) ) { 1267 $message .= ' — ' . __( 'check your credentials.', 'efficiencynext-data-connector' ); 1268 } elseif ( str_starts_with( $fetch_error, 'HTTP 404' ) ) { 1269 $message .= ' — ' . __( 'check the API URL.', 'efficiencynext-data-connector' ); 1270 } 1271 } 1272 wp_send_json_error( array( 'message' => $message ) ); 1118 1273 } 1119 1274 … … 1506 1661 margin-top: 10px; 1507 1662 } 1663 .auth-field-row { 1664 flex-direction: column; 1665 } 1666 } 1667 1668 /* Auth section */ 1669 .auth-fields { 1670 margin-top: 10px; 1671 padding: 12px 14px; 1672 background: #f6f7f7; 1673 border: 1px solid #dcdcde; 1674 border-radius: 4px; 1675 } 1676 .auth-field-row { 1677 display: flex; 1678 gap: 12px; 1679 margin-bottom: 10px; 1680 } 1681 .auth-field-row:last-child { 1682 margin-bottom: 0; 1683 } 1684 .auth-field-col { 1685 flex: 1; 1686 min-width: 0; 1687 } 1688 .auth-field-col-full { 1689 flex: 1 1 100%; 1690 } 1691 .auth-field-col label { 1692 display: block; 1693 font-weight: 600; 1694 font-size: 12px; 1695 margin-bottom: 4px; 1696 color: #1d2327; 1697 } 1698 .auth-field-col input, 1699 .auth-field-col select { 1700 width: 100%; 1701 } 1702 .auth-stored-note { 1703 display: block; 1704 font-size: 11px; 1705 color: #646970; 1706 margin-top: 3px; 1707 font-style: italic; 1708 } 1709 .source-meta .auth-badge { 1710 display: inline-block; 1711 font-size: 10px; 1712 font-weight: 600; 1713 padding: 1px 6px; 1714 border-radius: 10px; 1715 text-transform: uppercase; 1716 letter-spacing: 0.03em; 1717 } 1718 .source-meta .auth-badge.auth-none { 1719 background: #f0f0f1; 1720 color: #646970; 1721 } 1722 .source-meta .auth-badge.auth-basic { 1723 background: #e7f0f5; 1724 color: #2271b1; 1725 } 1726 .source-meta .auth-badge.auth-apikey { 1727 background: #fef3e7; 1728 color: #9a6700; 1508 1729 } 1509 1730 <?php … … 1571 1792 }); 1572 1793 1794 // Auth type toggle. 1795 $('#db-source-auth-type').on('change', function() { 1796 DB.toggleAuthFields($(this).val()); 1797 DB.saveDraft(); 1798 }); 1799 1573 1800 // Auto-save draft on every field change. 1574 1801 $('#db-source-name, #db-source-placeholder, #db-source-url, #db-source-template').on('input change', function() { … … 1578 1805 DB.saveDraft(); 1579 1806 }); 1807 $('#db-auth-username, #db-auth-password, #db-auth-key-name, #db-auth-key-value').on('input', function() { 1808 DB.saveDraft(); 1809 }); 1810 $('#db-auth-key-placement').on('change', function() { 1811 DB.saveDraft(); 1812 }); 1580 1813 }, 1581 1814 … … 1587 1820 1588 1821 this.draft = { 1589 source_id: $('#db-source-id').val(), 1590 name: $('#db-source-name').val(), 1591 placeholder: $('#db-source-placeholder').val(), 1592 api_url: $('#db-source-url').val(), 1593 template: $('#db-source-template').val(), 1594 inline: $('#db-source-inline').is(':checked'), 1595 timestamp: Date.now() 1822 source_id: $('#db-source-id').val(), 1823 name: $('#db-source-name').val(), 1824 placeholder: $('#db-source-placeholder').val(), 1825 api_url: $('#db-source-url').val(), 1826 template: $('#db-source-template').val(), 1827 inline: $('#db-source-inline').is(':checked'), 1828 auth_type: $('#db-source-auth-type').val(), 1829 auth_username: $('#db-auth-username').val(), 1830 auth_key_name: $('#db-auth-key-name').val(), 1831 auth_key_placement: $('#db-auth-key-placement').val(), 1832 // Never persist secret values in sessionStorage. 1833 timestamp: Date.now() 1596 1834 }; 1597 1835 … … 1627 1865 $('#db-source-template').val(this.draft.template || ''); 1628 1866 $('#db-source-inline').prop('checked', !!this.draft.inline); 1867 var authType = this.draft.auth_type || 'none'; 1868 $('#db-source-auth-type').val(authType); 1869 $('#db-auth-username').val(this.draft.auth_username || ''); 1870 $('#db-auth-password').val(''); 1871 $('#db-auth-password-note').hide(); 1872 $('#db-auth-key-name').val(this.draft.auth_key_name || ''); 1873 $('#db-auth-key-placement').val(this.draft.auth_key_placement || 'header'); 1874 $('#db-auth-key-value').val(''); 1875 $('#db-auth-key-value-note').hide(); 1876 this.toggleAuthFields(authType); 1629 1877 }, 1630 1878 … … 1658 1906 this.sources.forEach(function(source) { 1659 1907 var shortcode = '[effcncynxtdc source="' + DB.escHtml(source.name) + '"]'; 1908 var authType = source.auth_type || 'none'; 1909 var authLabels = { none: 'No Auth', basic: 'Basic', apikey: 'API Key' }; 1910 var authLabel = authLabels[authType] || 'No Auth'; 1660 1911 var $item = $( 1661 1912 '<div class="effcncynxtdc-source-item" data-id="' + DB.escHtml(source.id) + '">' + … … 1666 1917 '<span>' + effcncynxtdcAdmin.i18n.labelShortcode + ' <code>' + DB.escHtml(shortcode) + '</code></span>' + 1667 1918 '<span>' + effcncynxtdcAdmin.i18n.labelInline + ' ' + (source.inline ? effcncynxtdcAdmin.i18n.yes : effcncynxtdcAdmin.i18n.no) + '</span>' + 1919 '<span><span class="auth-badge auth-' + DB.escHtml(authType) + '">' + DB.escHtml(authLabel) + '</span></span>' + 1668 1920 '</div>' + 1669 1921 '</div>' + … … 1694 1946 $('#db-source-template').val(source.template); 1695 1947 $('#db-source-inline').prop('checked', source.inline); 1948 // Auth fields. 1949 var authType = source.auth_type || 'none'; 1950 $('#db-source-auth-type').val(authType); 1951 $('#db-auth-username').val(source.auth_username || ''); 1952 $('#db-auth-password').val(''); 1953 $('#db-auth-password-note').toggle(source.auth_password === '__UNCHANGED__'); 1954 $('#db-auth-key-name').val(source.auth_key_name || ''); 1955 $('#db-auth-key-placement').val(source.auth_key_placement || 'header'); 1956 $('#db-auth-key-value').val(''); 1957 $('#db-auth-key-value-note').toggle(source.auth_key_value === '__UNCHANGED__'); 1958 this.toggleAuthFields(authType); 1696 1959 this.clearDraft(); 1697 1960 } else if (this.hasDraft() && !this.draft.source_id) { … … 1723 1986 $('#db-source-template').val(''); 1724 1987 $('#db-source-inline').prop('checked', false); 1988 $('#db-source-auth-type').val('none'); 1989 $('#db-auth-username').val(''); 1990 $('#db-auth-password').val(''); 1991 $('#db-auth-password-note').hide(); 1992 $('#db-auth-key-name').val(''); 1993 $('#db-auth-key-value').val(''); 1994 $('#db-auth-key-value-note').hide(); 1995 $('#db-auth-key-placement').val('header'); 1996 this.toggleAuthFields('none'); 1997 }, 1998 1999 toggleAuthFields: function(type) { 2000 $('.auth-fields').hide(); 2001 if (type === 'basic') { $('#db-auth-basic').show(); } 2002 if (type === 'apikey') { $('#db-auth-apikey').show(); } 1725 2003 }, 1726 2004 … … 1733 2011 saveSource: function() { 1734 2012 var data = { 1735 action: 'effcncynxtdc_save_source', 1736 nonce: effcncynxtdcAdmin.nonce, 1737 source_id: $('#db-source-id').val(), 1738 name: $('#db-source-name').val(), 1739 placeholder: $('#db-source-placeholder').val(), 1740 api_url: $('#db-source-url').val(), 1741 template: $('#db-source-template').val(), 1742 inline: $('#db-source-inline').is(':checked') ? '1' : '0' 2013 action: 'effcncynxtdc_save_source', 2014 nonce: effcncynxtdcAdmin.nonce, 2015 source_id: $('#db-source-id').val(), 2016 name: $('#db-source-name').val(), 2017 placeholder: $('#db-source-placeholder').val(), 2018 api_url: $('#db-source-url').val(), 2019 template: $('#db-source-template').val(), 2020 inline: $('#db-source-inline').is(':checked') ? '1' : '0', 2021 auth_type: $('#db-source-auth-type').val(), 2022 auth_username: $('#db-auth-username').val(), 2023 auth_password: $('#db-auth-password').val() || '__UNCHANGED__', 2024 auth_key_name: $('#db-auth-key-name').val(), 2025 auth_key_value: $('#db-auth-key-value').val() || '__UNCHANGED__', 2026 auth_key_placement: $('#db-auth-key-placement').val() 1743 2027 }; 1744 2028 … … 1798 2082 1799 2083 $.post(effcncynxtdcAdmin.ajaxUrl, { 1800 action: 'effcncynxtdc_preview_api', 1801 nonce: effcncynxtdcAdmin.nonce, 1802 api_url: url 2084 action: 'effcncynxtdc_preview_api', 2085 nonce: effcncynxtdcAdmin.nonce, 2086 api_url: url, 2087 source_id: $('#db-source-id').val(), 2088 auth_type: $('#db-source-auth-type').val(), 2089 auth_username: $('#db-auth-username').val(), 2090 auth_password: $('#db-auth-password').val() || '__UNCHANGED__', 2091 auth_key_name: $('#db-auth-key-name').val(), 2092 auth_key_value: $('#db-auth-key-value').val() || '__UNCHANGED__', 2093 auth_key_placement: $('#db-auth-key-placement').val() 1803 2094 }, function(resp) { 1804 2095 $btn.prop('disabled', false).text(effcncynxtdcAdmin.i18n.previewApi); … … 1962 2253 </div> 1963 2254 <p class="description"><?php esc_html_e( 'HTML template with Mustache-style tags. Only safe HTML tags are allowed (no script/iframe/form). Click "Preview API" above to see available fields.', 'efficiencynext-data-connector' ); ?></p> 2255 </div> 2256 2257 <div class="form-row"> 2258 <label for="db-source-auth-type"><?php esc_html_e( 'Authentication', 'efficiencynext-data-connector' ); ?></label> 2259 <select id="db-source-auth-type" class="regular-text"> 2260 <option value="none"><?php esc_html_e( 'No Authentication', 'efficiencynext-data-connector' ); ?></option> 2261 <option value="basic"><?php esc_html_e( 'Basic Auth', 'efficiencynext-data-connector' ); ?></option> 2262 <option value="apikey"><?php esc_html_e( 'API Key', 'efficiencynext-data-connector' ); ?></option> 2263 </select> 2264 2265 <!-- Basic Auth fields --> 2266 <div id="db-auth-basic" class="auth-fields auth-fields-basic" style="display:none;"> 2267 <div class="auth-field-row"> 2268 <div class="auth-field-col"> 2269 <label for="db-auth-username"><?php esc_html_e( 'Username', 'efficiencynext-data-connector' ); ?></label> 2270 <input type="text" id="db-auth-username" class="regular-text" autocomplete="off"> 2271 </div> 2272 <div class="auth-field-col"> 2273 <label for="db-auth-password"><?php esc_html_e( 'Password', 'efficiencynext-data-connector' ); ?></label> 2274 <input type="password" id="db-auth-password" class="regular-text" autocomplete="new-password"> 2275 <span class="auth-stored-note" id="db-auth-password-note" style="display:none;"><?php esc_html_e( '(stored — leave blank to keep unchanged)', 'efficiencynext-data-connector' ); ?></span> 2276 </div> 2277 </div> 2278 </div> 2279 2280 <!-- API Key fields --> 2281 <div id="db-auth-apikey" class="auth-fields auth-fields-apikey" style="display:none;"> 2282 <div class="auth-field-row"> 2283 <div class="auth-field-col"> 2284 <label for="db-auth-key-name"><?php esc_html_e( 'Key Name', 'efficiencynext-data-connector' ); ?></label> 2285 <input type="text" id="db-auth-key-name" class="regular-text" placeholder="X-API-Key" autocomplete="off"> 2286 <p class="description"><?php esc_html_e( 'Header name or query parameter name.', 'efficiencynext-data-connector' ); ?></p> 2287 </div> 2288 <div class="auth-field-col"> 2289 <label for="db-auth-key-placement"><?php esc_html_e( 'Send As', 'efficiencynext-data-connector' ); ?></label> 2290 <select id="db-auth-key-placement"> 2291 <option value="header"><?php esc_html_e( 'Header', 'efficiencynext-data-connector' ); ?></option> 2292 <option value="query"><?php esc_html_e( 'Query Parameter', 'efficiencynext-data-connector' ); ?></option> 2293 </select> 2294 </div> 2295 </div> 2296 <div class="auth-field-row"> 2297 <div class="auth-field-col auth-field-col-full"> 2298 <label for="db-auth-key-value"><?php esc_html_e( 'Key Value', 'efficiencynext-data-connector' ); ?></label> 2299 <input type="password" id="db-auth-key-value" class="regular-text" autocomplete="new-password"> 2300 <span class="auth-stored-note" id="db-auth-key-value-note" style="display:none;"><?php esc_html_e( '(stored — leave blank to keep unchanged)', 'efficiencynext-data-connector' ); ?></span> 2301 </div> 2302 </div> 2303 </div> 1964 2304 </div> 1965 2305 -
efficiencynext-data-connector/trunk/readme.txt
r3474007 r3492952 5 5 Tested up to: 6.9 6 6 Requires PHP: 8.0 7 Stable tag: 1.0. 07 Stable tag: 1.0.1 8 8 License: GPL-2.0-or-later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 108 108 * Full internationalization (i18n) with load_plugin_textdomain and translatable JavaScript strings 109 109 * Automatic migration from pre-release storage format 110 111 = 1.0.1 = 112 * Added support for Basic Authentication and API Key Authentication
Note: See TracChangeset
for help on using the changeset viewer.