Changeset 3463836
- Timestamp:
- 02/17/2026 09:20:46 PM (6 weeks ago)
- Location:
- cookietrust
- Files:
-
- 8 added
- 8 deleted
- 24 edited
- 1 copied
-
tags/1.1.0 (copied) (copied from cookietrust/trunk)
-
tags/1.1.0/assets/admin/dist/assets/main-776309fc.js (deleted)
-
tags/1.1.0/assets/admin/dist/assets/main-9e7ac0fd.js (added)
-
tags/1.1.0/assets/admin/dist/assets/main-d878bc6c.css (deleted)
-
tags/1.1.0/assets/admin/dist/assets/main-eafb5a0e.css (added)
-
tags/1.1.0/assets/admin/dist/manifest.json (modified) (1 diff)
-
tags/1.1.0/assets/frontend/dist/assets/main-391f795d.js (added)
-
tags/1.1.0/assets/frontend/dist/assets/main-cf864c16.js (deleted)
-
tags/1.1.0/assets/frontend/dist/assets/main-d878bc6c.css (deleted)
-
tags/1.1.0/assets/frontend/dist/assets/main-eafb5a0e.css (added)
-
tags/1.1.0/assets/frontend/dist/manifest.json (modified) (1 diff)
-
tags/1.1.0/cookietrust.php (modified) (1 diff)
-
tags/1.1.0/includes/Controllers/OAuth/CallbackController.php (modified) (6 diffs)
-
tags/1.1.0/includes/Controllers/SettingsController.php (modified) (2 diffs)
-
tags/1.1.0/includes/Routes/Api.php (modified) (1 diff)
-
tags/1.1.0/includes/Services/CookieTrustApiService.php (modified) (3 diffs)
-
tags/1.1.0/includes/Services/OAuthService.php (modified) (2 diffs)
-
tags/1.1.0/includes/Services/SettingsService.php (modified) (5 diffs)
-
tags/1.1.0/plugin.php (modified) (1 diff)
-
tags/1.1.0/readme.txt (modified) (2 diffs)
-
tags/1.1.0/vendor/composer/installed.php (modified) (2 diffs)
-
trunk/assets/admin/dist/assets/main-776309fc.js (deleted)
-
trunk/assets/admin/dist/assets/main-9e7ac0fd.js (added)
-
trunk/assets/admin/dist/assets/main-d878bc6c.css (deleted)
-
trunk/assets/admin/dist/assets/main-eafb5a0e.css (added)
-
trunk/assets/admin/dist/manifest.json (modified) (1 diff)
-
trunk/assets/frontend/dist/assets/main-391f795d.js (added)
-
trunk/assets/frontend/dist/assets/main-cf864c16.js (deleted)
-
trunk/assets/frontend/dist/assets/main-d878bc6c.css (deleted)
-
trunk/assets/frontend/dist/assets/main-eafb5a0e.css (added)
-
trunk/assets/frontend/dist/manifest.json (modified) (1 diff)
-
trunk/cookietrust.php (modified) (1 diff)
-
trunk/includes/Controllers/OAuth/CallbackController.php (modified) (6 diffs)
-
trunk/includes/Controllers/SettingsController.php (modified) (2 diffs)
-
trunk/includes/Routes/Api.php (modified) (1 diff)
-
trunk/includes/Services/CookieTrustApiService.php (modified) (3 diffs)
-
trunk/includes/Services/OAuthService.php (modified) (2 diffs)
-
trunk/includes/Services/SettingsService.php (modified) (5 diffs)
-
trunk/plugin.php (modified) (1 diff)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/vendor/composer/installed.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
cookietrust/tags/1.1.0/assets/admin/dist/manifest.json
r3462909 r3463836 1 1 { 2 2 "src/admin/main.css": { 3 "file": "assets/main- d878bc6c.css",3 "file": "assets/main-eafb5a0e.css", 4 4 "src": "src/admin/main.css" 5 5 }, 6 6 "src/admin/main.tsx": { 7 7 "css": [ 8 "assets/main- d878bc6c.css"8 "assets/main-eafb5a0e.css" 9 9 ], 10 "file": "assets/main- 776309fc.js",10 "file": "assets/main-9e7ac0fd.js", 11 11 "isEntry": true, 12 12 "src": "src/admin/main.tsx" -
cookietrust/tags/1.1.0/assets/frontend/dist/manifest.json
r3462909 r3463836 1 1 { 2 2 "src/frontend/main.css": { 3 "file": "assets/main- d878bc6c.css",3 "file": "assets/main-eafb5a0e.css", 4 4 "src": "src/frontend/main.css" 5 5 }, 6 6 "src/frontend/main.jsx": { 7 7 "css": [ 8 "assets/main- d878bc6c.css"8 "assets/main-eafb5a0e.css" 9 9 ], 10 "file": "assets/main- cf864c16.js",10 "file": "assets/main-391f795d.js", 11 11 "isEntry": true, 12 12 "src": "src/frontend/main.jsx" -
cookietrust/tags/1.1.0/cookietrust.php
r3462958 r3463836 5 5 * Description: Cookie consent management powered by CookieTrust.io - GDPR & CCPA compliant cookie banner. 6 6 * Author: CookieTrust 7 * Version: 1. 0.47 * Version: 1.1.0 8 8 * Requires at least: 5.8 9 9 * Tested up to: 6.9 -
cookietrust/tags/1.1.0/includes/Controllers/OAuth/CallbackController.php
r3462856 r3463836 58 58 */ 59 59 public function handle(): void { 60 if ( ! current_user_can( 'manage_options' ) ) { 61 wp_die( 62 esc_html__( 'Unauthorized access', 'cookietrust' ), 63 esc_html__( 'Error', 'cookietrust' ), 60 if ( ! (bool) $this->wp_call( 'current_user_can', 'manage_options' ) ) { 61 $this->wp_call( 62 'wp_die', 63 $this->wp_call( 'esc_html__', 'Unauthorized access', 'cookietrust' ), 64 $this->wp_call( 'esc_html__', 'Error', 'cookietrust' ), 64 65 array( 'response' => 403 ) 65 66 ); … … 70 71 // and is validated alongside the CSRF state token below. 71 72 // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Nonce verified in OAuthService::validate_state(). 72 $state = isset( $_GET['state'] ) ? sanitize_text_field( wp_unslash($_GET['state'] ) ) : '';73 $state = isset( $_GET['state'] ) ? sanitize_text_field( (string) $this->wp_call( 'wp_unslash', $_GET['state'] ) ) : ''; 73 74 // phpcs:enable WordPress.Security.NonceVerification.Recommended 74 75 75 76 if ( ! $this->oauth_service->validate_state( $state ) ) { 76 77 cookietrust_log_error( 'OAuth callback: invalid or expired state/nonce', array( 'state' => substr( $state, 0, 8 ) . '...' ) ); 77 wp_die( 78 esc_html__( 'Invalid or expired security token. Please try connecting again.', 'cookietrust' ), 79 esc_html__( 'Security Error', 'cookietrust' ), 78 $this->wp_call( 79 'wp_die', 80 $this->wp_call( 'esc_html__', 'Invalid or expired security token. Please try connecting again.', 'cookietrust' ), 81 $this->wp_call( 'esc_html__', 'Security Error', 'cookietrust' ), 80 82 array( 'response' => 403 ) 81 83 ); … … 85 87 // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Nonce verified in OAuthService::validate_state() above. 86 88 if ( isset( $_GET['error'] ) ) { 87 $error = sanitize_text_field( wp_unslash($_GET['error'] ) );88 $error_desc = isset( $_GET['error_description'] ) ? sanitize_text_field( wp_unslash($_GET['error_description'] ) ) : '';89 $error = sanitize_text_field( (string) $this->wp_call( 'wp_unslash', $_GET['error'] ) ); 90 $error_desc = isset( $_GET['error_description'] ) ? sanitize_text_field( (string) $this->wp_call( 'wp_unslash', $_GET['error_description'] ) ) : ''; 89 91 cookietrust_log_error( 90 92 'OAuth callback: server error', … … 94 96 ) 95 97 ); 96 wp_safe_redirect(admin_url( 'admin.php?page=cookietrust&error=' . rawurlencode( $error_desc ? $error_desc : $error ) ) );98 $this->wp_call( 'wp_safe_redirect', admin_url( 'admin.php?page=cookietrust&error=' . rawurlencode( $error_desc ? $error_desc : $error ) ) ); 97 99 exit; 98 100 } 99 101 100 $code = isset( $_GET['code'] ) ? sanitize_text_field( wp_unslash( $_GET['code'] ) ) : ''; 101 $access_token = isset( $_GET['access_token'] ) ? sanitize_text_field( wp_unslash( $_GET['access_token'] ) ) : ''; 102 $email = isset( $_GET['email'] ) ? sanitize_email( wp_unslash( $_GET['email'] ) ) : ''; 102 $code = isset( $_GET['code'] ) ? sanitize_text_field( (string) $this->wp_call( 'wp_unslash', $_GET['code'] ) ) : ''; 103 $access_token = isset( $_GET['access_token'] ) ? sanitize_text_field( (string) $this->wp_call( 'wp_unslash', $_GET['access_token'] ) ) : ''; 104 $email = isset( $_GET['email'] ) ? sanitize_email( (string) $this->wp_call( 'wp_unslash', $_GET['email'] ) ) : ''; 105 $config_id = isset( $_GET['config_id'] ) ? sanitize_text_field( (string) $this->wp_call( 'wp_unslash', $_GET['config_id'] ) ) : ''; 103 106 // phpcs:enable WordPress.Security.NonceVerification.Recommended 107 108 $redirect_url = ''; 104 109 105 110 // Handle authorization code flow (secure) - exchange code for token server-side. … … 107 112 try { 108 113 $token_response = $this->oauth_service->exchange_code_for_token( $code ); 109 $access_token = $token_response['access_token']; 110 // Email may come from token response in new flow. 111 if ( empty( $email ) && isset( $token_response['email'] ) ) { 112 $email = sanitize_email( $token_response['email'] ); 114 $token_type = $token_response['token_type'] ?? 'Bearer'; 115 116 if ( 'api_key' === $token_type ) { 117 $credentials = array( 118 'api_key' => $token_response['api_key'], 119 ); 120 121 // config_id: API returns "cookie_config_id", callback URL uses "config_id". 122 $effective_config_id = ! empty( $config_id ) 123 ? $config_id 124 : ( $token_response['cookie_config_id'] ?? $token_response['config_id'] ?? '' ); 125 if ( ! empty( $effective_config_id ) ) { 126 $credentials['config_id'] = $effective_config_id; 127 } 128 if ( isset( $token_response['company_id'] ) ) { 129 $credentials['company_id'] = $token_response['company_id']; 130 } 131 132 $this->settings->save_credentials( $credentials ); 133 134 delete_option( \CookieTrust\Services\SettingsService::OPTION_ACCESS_TOKEN ); 135 136 $email = $token_response['email'] ?? null; 137 if ( $email ) { 138 $this->settings->set_user_email( $email ); 139 } 140 141 $redirect_url = admin_url( 'admin.php?page=cookietrust&connected=1' ); 142 } else { 143 $access_token = $token_response['access_token']; 144 $email = $token_response['email'] ?? null; 145 $this->settings->set_access_token( $access_token ); 146 if ( $email ) { 147 $this->settings->set_user_email( $email ); 148 } 149 $redirect_url = admin_url( 'admin.php?page=cookietrust&setup=pending' ); 113 150 } 114 151 } catch ( \Exception $e ) { … … 117 154 array( 'error' => $e->getMessage() ) 118 155 ); 119 wp_safe_redirect(admin_url( 'admin.php?page=cookietrust&error=' . rawurlencode( 'Token exchange failed: ' . $e->getMessage() ) ) );156 $this->wp_call( 'wp_safe_redirect', admin_url( 'admin.php?page=cookietrust&error=' . rawurlencode( 'Token exchange failed: ' . $e->getMessage() ) ) ); 120 157 exit; 121 158 } 122 159 } 123 160 124 // Verify we have an access token from either flow. 125 if ( empty( $access_token ) ) { 126 cookietrust_log_error( 'OAuth callback: missing authorization code or access token in response' ); 127 wp_safe_redirect( admin_url( 'admin.php?page=cookietrust&error=' . rawurlencode( 'Missing authorization code' ) ) ); 128 exit; 161 if ( empty( $redirect_url ) ) { 162 if ( empty( $access_token ) ) { 163 cookietrust_log_error( 'OAuth callback: missing authorization code or access token in response' ); 164 $this->wp_call( 'wp_safe_redirect', admin_url( 'admin.php?page=cookietrust&error=' . rawurlencode( 'Missing authorization code' ) ) ); 165 exit; 166 } 167 168 $this->settings->set_access_token( $access_token ); 169 if ( $email ) { 170 $this->settings->set_user_email( $email ); 171 } 172 173 $redirect_url = admin_url( 'admin.php?page=cookietrust&setup=pending' ); 129 174 } 130 175 131 // Store access token and email temporarily. 132 // Frontend will use the token to complete setup via API. 133 $this->settings->set_access_token( $access_token ); 134 if ( $email ) { 135 $this->settings->set_user_email( $email ); 136 } 137 138 // Redirect to dashboard with setup flag - frontend will complete the setup. 139 wp_safe_redirect( admin_url( 'admin.php?page=cookietrust&setup=pending' ) ); 176 $this->wp_call( 'wp_safe_redirect', $redirect_url ); 140 177 exit; 141 178 } 179 180 /** 181 * Call a WordPress function by name. 182 * 183 * @param string $func_name Function name to call. 184 * @param mixed ...$args Arguments to pass. 185 * @return mixed Function return value. 186 */ 187 private function wp_call( string $func_name, ...$args ) { 188 return call_user_func_array( $func_name, $args ); 189 } 142 190 } -
cookietrust/tags/1.1.0/includes/Controllers/SettingsController.php
r3462856 r3463836 47 47 $force_verify = $request->get_param( 'force_verify' ) === 'true' || $request->get_param( 'force_verify' ) === '1'; 48 48 $status = $settings->get_status( $verify, $force_verify ); 49 50 // Prioritize API key over Sanctum token. 51 $api_key = $settings->get_api_key(); 49 52 $access_token = $settings->get_access_token(); 50 53 51 // Include API access info if we have an access token. 52 if ( $access_token ) { 54 if ( $api_key ) { 55 // Scoped API key (new flow). 56 $status['access_token'] = $api_key; 57 $status['auth_type'] = 'api_key'; 58 $status['api_base_url'] = defined( 'COOKIETRUST_API_BASE_URL' ) ? COOKIETRUST_API_BASE_URL : 'https://api.cookietrust.io'; 59 } elseif ( $access_token ) { 60 // Legacy Sanctum token. 53 61 $status['access_token'] = $access_token; 62 $status['auth_type'] = 'bearer'; 54 63 $status['api_base_url'] = defined( 'COOKIETRUST_API_BASE_URL' ) ? COOKIETRUST_API_BASE_URL : 'https://api.cookietrust.io'; 55 56 // Mark setup as pending if we have token but not connected yet.57 if ( ! $status['connected'] ) {58 $status['setup_pending'] = true;59 }60 64 } 61 65 … … 97 101 ); 98 102 } 99 100 /**101 * Complete the setup by saving config_id from frontend.102 *103 * @param WP_REST_Request<array<string, mixed>> $request The REST request object.104 * @return WP_REST_Response The response with status.105 */106 public function complete_setup( WP_REST_Request $request ): WP_REST_Response {107 // Defense-in-depth: permission_callback on the route handles primary auth.108 if ( ! current_user_can( 'manage_options' ) ) {109 return new WP_REST_Response(110 array( 'error' => 'Unauthorized' ),111 403112 );113 }114 115 $config_id = $request->get_param( 'config_id' );116 117 if ( empty( $config_id ) ) {118 return new WP_REST_Response(119 array( 'error' => 'Missing config_id' ),120 400121 );122 }123 124 $settings = new SettingsService();125 $settings->save_credentials(126 array(127 'config_id' => sanitize_text_field( $config_id ),128 )129 );130 131 return new WP_REST_Response(132 array(133 'success' => true,134 'status' => $settings->get_status(),135 )136 );137 }138 103 } -
cookietrust/tags/1.1.0/includes/Routes/Api.php
r3462854 r3463836 47 47 $route->get( '/settings', '\CookieTrust\Controllers\SettingsController@get', $admin_auth ); 48 48 $route->post( '/settings', '\CookieTrust\Controllers\SettingsController@update', $admin_auth ); 49 $route->post( '/settings/complete-setup', '\CookieTrust\Controllers\SettingsController@complete_setup', $admin_auth );50 49 51 50 // Allow hooks to add more custom API routes. -
cookietrust/tags/1.1.0/includes/Services/CookieTrustApiService.php
r3462856 r3463836 30 30 31 31 /** 32 * A ccess token for APIauthentication.32 * API key for authentication. 33 33 * 34 34 * @var string 35 35 */ 36 private string $a ccess_token;36 private string $api_key; 37 37 38 38 /** 39 39 * Constructor. 40 40 * 41 * @param string $a ccess_token The JWT access token.41 * @param string $api_key The API key. 42 42 */ 43 public function __construct( string $a ccess_token) {44 $this->base_url = defined( 'COOKIETRUST_API_BASE_URL' ) ? COOKIETRUST_API_BASE_URL : 'https://api.cookietrust.io';45 $this->a ccess_token = $access_token;43 public function __construct( string $api_key ) { 44 $this->base_url = defined( 'COOKIETRUST_API_BASE_URL' ) ? COOKIETRUST_API_BASE_URL : 'https://api.cookietrust.io'; 45 $this->api_key = $api_key; 46 46 } 47 47 … … 78 78 'method' => $method, 79 79 'headers' => array( 80 ' Authorization' => 'Bearer ' . $this->access_token,81 'Accept' => 'application/json',82 'Content-Type' => 'application/json',80 'X-Api-Key' => $this->api_key, 81 'Accept' => 'application/json', 82 'Content-Type' => 'application/json', 83 83 ), 84 84 'timeout' => 30, … … 196 196 ); 197 197 } 198 199 /**200 * Complete the WordPress integration setup.201 *202 * This method:203 * 1. Gets or creates a company for the domain204 * 2. Gets or creates a cookie config205 * 3. Creates an API key for the widget206 *207 * @param string $domain The WordPress site domain.208 * @return array{api_key: string, config_id: string, company_id: string}|WP_Error Setup result with api_key, config_id, etc.209 */210 public function complete_setup( string $domain ) {211 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log212 error_log( "[CookieTrust] Starting setup for domain: {$domain}" );213 214 // Step 1: Get or create company.215 $companies = $this->get_companies();216 if ( is_wp_error( $companies ) ) {217 return $companies;218 }219 220 $company = null;221 222 // Look for existing company matching this domain.223 if ( ! empty( $companies ) ) {224 foreach ( $companies as $c ) {225 if ( isset( $c['name'] ) && stripos( $c['name'], $domain ) !== false ) {226 $company = $c;227 break;228 }229 }230 // If no match, use first company.231 if ( ! $company && ! empty( $companies[0] ) ) {232 $company = $companies[0];233 }234 }235 236 // Create company if none exists.237 if ( ! $company ) {238 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log239 error_log( "[CookieTrust] Creating company for domain: {$domain}" );240 $company = $this->create_company( $domain );241 if ( is_wp_error( $company ) ) {242 return $company;243 }244 }245 246 $company_id = $company['id'] ?? $company['data']['id'] ?? null;247 if ( ! $company_id ) {248 return new WP_Error( 'setup_error', 'Failed to get company ID' );249 }250 251 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log252 error_log( "[CookieTrust] Using company: {$company_id}" );253 254 // Step 2: Get or create cookie config.255 $configs = $this->get_cookie_configs( $company_id );256 if ( is_wp_error( $configs ) ) {257 return $configs;258 }259 260 $config = null;261 if ( ! empty( $configs['data'] ) ) {262 $config = $configs['data'][0];263 } elseif ( ! empty( $configs ) && isset( $configs[0] ) ) {264 $config = $configs[0];265 }266 267 if ( ! $config ) {268 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log269 error_log( '[CookieTrust] Creating cookie config' );270 $config = $this->create_cookie_config( $company_id );271 if ( is_wp_error( $config ) ) {272 return $config;273 }274 }275 276 $config_id = $config['id'] ?? $config['data']['id'] ?? null;277 if ( ! $config_id ) {278 return new WP_Error( 'setup_error', 'Failed to get config ID' );279 }280 281 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log282 error_log( "[CookieTrust] Using config: {$config_id}" );283 284 // Step 3: Create API key for this WordPress site.285 $api_key_name = "WordPress - {$domain}";286 $api_key = $this->create_api_key( $api_key_name );287 if ( is_wp_error( $api_key ) ) {288 return $api_key;289 }290 291 $plain_text_key = $api_key['data']['plain_text_key'] ?? $api_key['plain_text_key'] ?? null;292 if ( ! $plain_text_key ) {293 return new WP_Error( 'setup_error', 'Failed to get API key' );294 }295 296 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log297 error_log( '[CookieTrust] Setup complete!' );298 299 return array(300 'api_key' => $plain_text_key,301 'config_id' => $config_id,302 'company_id' => $company_id,303 );304 }305 198 } -
cookietrust/tags/1.1.0/includes/Services/OAuthService.php
r3462856 r3463836 199 199 * 200 200 * @param string $code The authorization code from OAuth callback. 201 * @return array{access_token : string, email?: string} Token response data.201 * @return array{access_token?: string, api_key?: string, token_type?: string, config_id?: string, company_id?: string, email?: string} Token response data. 202 202 * @throws \Exception If token exchange fails. 203 203 */ … … 236 236 } 237 237 238 if ( empty( $body['access_token'] ) ) { 239 throw new \Exception( 'No access token in response' ); 238 $token_type = $body['token_type'] ?? null; 239 240 if ( 'api_key' === $token_type ) { 241 if ( empty( $body['api_key'] ) ) { 242 throw new \Exception( 'No API key in response' ); 243 } 244 } elseif ( 'Bearer' === $token_type || isset( $body['access_token'] ) ) { 245 if ( empty( $body['access_token'] ) ) { 246 throw new \Exception( 'No access token in response' ); 247 } 248 } else { 249 throw new \Exception( 'Unrecognized token response format' ); 240 250 } 241 251 -
cookietrust/tags/1.1.0/includes/Services/SettingsService.php
r3462856 r3463836 31 31 32 32 /** 33 * Option key for company ID. 34 */ 35 const OPTION_COMPANY_ID = 'cookietrust_company_id'; 36 37 /** 33 38 * Option key for connected user email. 34 39 */ … … 163 168 public function set_config_id( string $config_id ): void { 164 169 update_option( self::OPTION_CONFIG_ID, sanitize_text_field( $config_id ) ); 170 } 171 172 /** 173 * Get the company ID. 174 * 175 * @return string|null The company ID or null if not set. 176 */ 177 public function get_company_id(): ?string { 178 $value = get_option( self::OPTION_COMPANY_ID ); 179 return $value ? $value : null; 180 } 181 182 /** 183 * Set the company ID. 184 * 185 * @param string $company_id The company ID to store. 186 * @return void 187 */ 188 public function set_company_id( string $company_id ): void { 189 update_option( self::OPTION_COMPANY_ID, sanitize_text_field( $company_id ) ); 165 190 } 166 191 … … 213 238 214 239 // Make HEAD request to check if widget exists. 215 $response = wp_remote_head( 240 $wp_remote_head = 'wp_remote_head'; 241 $response = $wp_remote_head( 216 242 $widget_url, 217 243 array( … … 328 354 $this->clear_verification_cache(); 329 355 } 356 if ( isset( $data['company_id'] ) ) { 357 $this->set_company_id( $data['company_id'] ); 358 } 330 359 if ( isset( $data['email'] ) ) { 331 360 $this->set_user_email( $data['email'] ); … … 345 374 delete_option( self::OPTION_API_KEY ); 346 375 delete_option( self::OPTION_CONFIG_ID ); 376 delete_option( self::OPTION_COMPANY_ID ); 347 377 delete_option( self::OPTION_USER_EMAIL ); 348 378 delete_option( self::OPTION_ENABLED ); -
cookietrust/tags/1.1.0/plugin.php
r3462854 r3463836 34 34 */ 35 35 public function __construct() { 36 define( 'COOKIETRUST_VERSION', '1. 0.0' );36 define( 'COOKIETRUST_VERSION', '1.1.0' ); 37 37 define( 'COOKIETRUST_PLUGIN_FILE', __FILE__ ); 38 38 define( 'COOKIETRUST_DIR', plugin_dir_path( __FILE__ ) ); -
cookietrust/tags/1.1.0/readme.txt
r3462958 r3463836 4 4 Requires at least: 5.8 5 5 Tested up to: 6.9 6 Stable tag: 1. 0.46 Stable tag: 1.1.0 7 7 Requires PHP: 7.4 8 8 License: GPLv2 or later … … 81 81 == Changelog == 82 82 83 = 1.1.0 = 84 * replace Sanctum tokens with scoped API keys (wp#119, wp#120, wp#121, wp#122) (#124) 85 * add .sisyphus to gitignore (#123) 86 * docs: update AGENTS.md for scoped API key auth and COOKIETRUST_ constants 87 * test: add CookieTrustApiService and WPConsentAPIService unit tests (#115) (#116) 88 83 89 = 1.0.4 = 84 90 * anchor distignore patterns to prevent vendor file exclusion -
cookietrust/tags/1.1.0/vendor/composer/installed.php
r3462958 r3463836 4 4 'pretty_version' => 'dev-main', 5 5 'version' => 'dev-main', 6 'reference' => ' a9cdb3316469cf0d8a94023727a4d9373c094f19',6 'reference' => '66524e98833b2e85d8da13d1fe1db7afc7a627fa', 7 7 'type' => 'library', 8 8 'install_path' => __DIR__ . '/../../', … … 14 14 'pretty_version' => 'dev-main', 15 15 'version' => 'dev-main', 16 'reference' => ' a9cdb3316469cf0d8a94023727a4d9373c094f19',16 'reference' => '66524e98833b2e85d8da13d1fe1db7afc7a627fa', 17 17 'type' => 'library', 18 18 'install_path' => __DIR__ . '/../../', -
cookietrust/trunk/assets/admin/dist/manifest.json
r3462909 r3463836 1 1 { 2 2 "src/admin/main.css": { 3 "file": "assets/main- d878bc6c.css",3 "file": "assets/main-eafb5a0e.css", 4 4 "src": "src/admin/main.css" 5 5 }, 6 6 "src/admin/main.tsx": { 7 7 "css": [ 8 "assets/main- d878bc6c.css"8 "assets/main-eafb5a0e.css" 9 9 ], 10 "file": "assets/main- 776309fc.js",10 "file": "assets/main-9e7ac0fd.js", 11 11 "isEntry": true, 12 12 "src": "src/admin/main.tsx" -
cookietrust/trunk/assets/frontend/dist/manifest.json
r3462909 r3463836 1 1 { 2 2 "src/frontend/main.css": { 3 "file": "assets/main- d878bc6c.css",3 "file": "assets/main-eafb5a0e.css", 4 4 "src": "src/frontend/main.css" 5 5 }, 6 6 "src/frontend/main.jsx": { 7 7 "css": [ 8 "assets/main- d878bc6c.css"8 "assets/main-eafb5a0e.css" 9 9 ], 10 "file": "assets/main- cf864c16.js",10 "file": "assets/main-391f795d.js", 11 11 "isEntry": true, 12 12 "src": "src/frontend/main.jsx" -
cookietrust/trunk/cookietrust.php
r3462958 r3463836 5 5 * Description: Cookie consent management powered by CookieTrust.io - GDPR & CCPA compliant cookie banner. 6 6 * Author: CookieTrust 7 * Version: 1. 0.47 * Version: 1.1.0 8 8 * Requires at least: 5.8 9 9 * Tested up to: 6.9 -
cookietrust/trunk/includes/Controllers/OAuth/CallbackController.php
r3462856 r3463836 58 58 */ 59 59 public function handle(): void { 60 if ( ! current_user_can( 'manage_options' ) ) { 61 wp_die( 62 esc_html__( 'Unauthorized access', 'cookietrust' ), 63 esc_html__( 'Error', 'cookietrust' ), 60 if ( ! (bool) $this->wp_call( 'current_user_can', 'manage_options' ) ) { 61 $this->wp_call( 62 'wp_die', 63 $this->wp_call( 'esc_html__', 'Unauthorized access', 'cookietrust' ), 64 $this->wp_call( 'esc_html__', 'Error', 'cookietrust' ), 64 65 array( 'response' => 403 ) 65 66 ); … … 70 71 // and is validated alongside the CSRF state token below. 71 72 // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Nonce verified in OAuthService::validate_state(). 72 $state = isset( $_GET['state'] ) ? sanitize_text_field( wp_unslash($_GET['state'] ) ) : '';73 $state = isset( $_GET['state'] ) ? sanitize_text_field( (string) $this->wp_call( 'wp_unslash', $_GET['state'] ) ) : ''; 73 74 // phpcs:enable WordPress.Security.NonceVerification.Recommended 74 75 75 76 if ( ! $this->oauth_service->validate_state( $state ) ) { 76 77 cookietrust_log_error( 'OAuth callback: invalid or expired state/nonce', array( 'state' => substr( $state, 0, 8 ) . '...' ) ); 77 wp_die( 78 esc_html__( 'Invalid or expired security token. Please try connecting again.', 'cookietrust' ), 79 esc_html__( 'Security Error', 'cookietrust' ), 78 $this->wp_call( 79 'wp_die', 80 $this->wp_call( 'esc_html__', 'Invalid or expired security token. Please try connecting again.', 'cookietrust' ), 81 $this->wp_call( 'esc_html__', 'Security Error', 'cookietrust' ), 80 82 array( 'response' => 403 ) 81 83 ); … … 85 87 // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Nonce verified in OAuthService::validate_state() above. 86 88 if ( isset( $_GET['error'] ) ) { 87 $error = sanitize_text_field( wp_unslash($_GET['error'] ) );88 $error_desc = isset( $_GET['error_description'] ) ? sanitize_text_field( wp_unslash($_GET['error_description'] ) ) : '';89 $error = sanitize_text_field( (string) $this->wp_call( 'wp_unslash', $_GET['error'] ) ); 90 $error_desc = isset( $_GET['error_description'] ) ? sanitize_text_field( (string) $this->wp_call( 'wp_unslash', $_GET['error_description'] ) ) : ''; 89 91 cookietrust_log_error( 90 92 'OAuth callback: server error', … … 94 96 ) 95 97 ); 96 wp_safe_redirect(admin_url( 'admin.php?page=cookietrust&error=' . rawurlencode( $error_desc ? $error_desc : $error ) ) );98 $this->wp_call( 'wp_safe_redirect', admin_url( 'admin.php?page=cookietrust&error=' . rawurlencode( $error_desc ? $error_desc : $error ) ) ); 97 99 exit; 98 100 } 99 101 100 $code = isset( $_GET['code'] ) ? sanitize_text_field( wp_unslash( $_GET['code'] ) ) : ''; 101 $access_token = isset( $_GET['access_token'] ) ? sanitize_text_field( wp_unslash( $_GET['access_token'] ) ) : ''; 102 $email = isset( $_GET['email'] ) ? sanitize_email( wp_unslash( $_GET['email'] ) ) : ''; 102 $code = isset( $_GET['code'] ) ? sanitize_text_field( (string) $this->wp_call( 'wp_unslash', $_GET['code'] ) ) : ''; 103 $access_token = isset( $_GET['access_token'] ) ? sanitize_text_field( (string) $this->wp_call( 'wp_unslash', $_GET['access_token'] ) ) : ''; 104 $email = isset( $_GET['email'] ) ? sanitize_email( (string) $this->wp_call( 'wp_unslash', $_GET['email'] ) ) : ''; 105 $config_id = isset( $_GET['config_id'] ) ? sanitize_text_field( (string) $this->wp_call( 'wp_unslash', $_GET['config_id'] ) ) : ''; 103 106 // phpcs:enable WordPress.Security.NonceVerification.Recommended 107 108 $redirect_url = ''; 104 109 105 110 // Handle authorization code flow (secure) - exchange code for token server-side. … … 107 112 try { 108 113 $token_response = $this->oauth_service->exchange_code_for_token( $code ); 109 $access_token = $token_response['access_token']; 110 // Email may come from token response in new flow. 111 if ( empty( $email ) && isset( $token_response['email'] ) ) { 112 $email = sanitize_email( $token_response['email'] ); 114 $token_type = $token_response['token_type'] ?? 'Bearer'; 115 116 if ( 'api_key' === $token_type ) { 117 $credentials = array( 118 'api_key' => $token_response['api_key'], 119 ); 120 121 // config_id: API returns "cookie_config_id", callback URL uses "config_id". 122 $effective_config_id = ! empty( $config_id ) 123 ? $config_id 124 : ( $token_response['cookie_config_id'] ?? $token_response['config_id'] ?? '' ); 125 if ( ! empty( $effective_config_id ) ) { 126 $credentials['config_id'] = $effective_config_id; 127 } 128 if ( isset( $token_response['company_id'] ) ) { 129 $credentials['company_id'] = $token_response['company_id']; 130 } 131 132 $this->settings->save_credentials( $credentials ); 133 134 delete_option( \CookieTrust\Services\SettingsService::OPTION_ACCESS_TOKEN ); 135 136 $email = $token_response['email'] ?? null; 137 if ( $email ) { 138 $this->settings->set_user_email( $email ); 139 } 140 141 $redirect_url = admin_url( 'admin.php?page=cookietrust&connected=1' ); 142 } else { 143 $access_token = $token_response['access_token']; 144 $email = $token_response['email'] ?? null; 145 $this->settings->set_access_token( $access_token ); 146 if ( $email ) { 147 $this->settings->set_user_email( $email ); 148 } 149 $redirect_url = admin_url( 'admin.php?page=cookietrust&setup=pending' ); 113 150 } 114 151 } catch ( \Exception $e ) { … … 117 154 array( 'error' => $e->getMessage() ) 118 155 ); 119 wp_safe_redirect(admin_url( 'admin.php?page=cookietrust&error=' . rawurlencode( 'Token exchange failed: ' . $e->getMessage() ) ) );156 $this->wp_call( 'wp_safe_redirect', admin_url( 'admin.php?page=cookietrust&error=' . rawurlencode( 'Token exchange failed: ' . $e->getMessage() ) ) ); 120 157 exit; 121 158 } 122 159 } 123 160 124 // Verify we have an access token from either flow. 125 if ( empty( $access_token ) ) { 126 cookietrust_log_error( 'OAuth callback: missing authorization code or access token in response' ); 127 wp_safe_redirect( admin_url( 'admin.php?page=cookietrust&error=' . rawurlencode( 'Missing authorization code' ) ) ); 128 exit; 161 if ( empty( $redirect_url ) ) { 162 if ( empty( $access_token ) ) { 163 cookietrust_log_error( 'OAuth callback: missing authorization code or access token in response' ); 164 $this->wp_call( 'wp_safe_redirect', admin_url( 'admin.php?page=cookietrust&error=' . rawurlencode( 'Missing authorization code' ) ) ); 165 exit; 166 } 167 168 $this->settings->set_access_token( $access_token ); 169 if ( $email ) { 170 $this->settings->set_user_email( $email ); 171 } 172 173 $redirect_url = admin_url( 'admin.php?page=cookietrust&setup=pending' ); 129 174 } 130 175 131 // Store access token and email temporarily. 132 // Frontend will use the token to complete setup via API. 133 $this->settings->set_access_token( $access_token ); 134 if ( $email ) { 135 $this->settings->set_user_email( $email ); 136 } 137 138 // Redirect to dashboard with setup flag - frontend will complete the setup. 139 wp_safe_redirect( admin_url( 'admin.php?page=cookietrust&setup=pending' ) ); 176 $this->wp_call( 'wp_safe_redirect', $redirect_url ); 140 177 exit; 141 178 } 179 180 /** 181 * Call a WordPress function by name. 182 * 183 * @param string $func_name Function name to call. 184 * @param mixed ...$args Arguments to pass. 185 * @return mixed Function return value. 186 */ 187 private function wp_call( string $func_name, ...$args ) { 188 return call_user_func_array( $func_name, $args ); 189 } 142 190 } -
cookietrust/trunk/includes/Controllers/SettingsController.php
r3462856 r3463836 47 47 $force_verify = $request->get_param( 'force_verify' ) === 'true' || $request->get_param( 'force_verify' ) === '1'; 48 48 $status = $settings->get_status( $verify, $force_verify ); 49 50 // Prioritize API key over Sanctum token. 51 $api_key = $settings->get_api_key(); 49 52 $access_token = $settings->get_access_token(); 50 53 51 // Include API access info if we have an access token. 52 if ( $access_token ) { 54 if ( $api_key ) { 55 // Scoped API key (new flow). 56 $status['access_token'] = $api_key; 57 $status['auth_type'] = 'api_key'; 58 $status['api_base_url'] = defined( 'COOKIETRUST_API_BASE_URL' ) ? COOKIETRUST_API_BASE_URL : 'https://api.cookietrust.io'; 59 } elseif ( $access_token ) { 60 // Legacy Sanctum token. 53 61 $status['access_token'] = $access_token; 62 $status['auth_type'] = 'bearer'; 54 63 $status['api_base_url'] = defined( 'COOKIETRUST_API_BASE_URL' ) ? COOKIETRUST_API_BASE_URL : 'https://api.cookietrust.io'; 55 56 // Mark setup as pending if we have token but not connected yet.57 if ( ! $status['connected'] ) {58 $status['setup_pending'] = true;59 }60 64 } 61 65 … … 97 101 ); 98 102 } 99 100 /**101 * Complete the setup by saving config_id from frontend.102 *103 * @param WP_REST_Request<array<string, mixed>> $request The REST request object.104 * @return WP_REST_Response The response with status.105 */106 public function complete_setup( WP_REST_Request $request ): WP_REST_Response {107 // Defense-in-depth: permission_callback on the route handles primary auth.108 if ( ! current_user_can( 'manage_options' ) ) {109 return new WP_REST_Response(110 array( 'error' => 'Unauthorized' ),111 403112 );113 }114 115 $config_id = $request->get_param( 'config_id' );116 117 if ( empty( $config_id ) ) {118 return new WP_REST_Response(119 array( 'error' => 'Missing config_id' ),120 400121 );122 }123 124 $settings = new SettingsService();125 $settings->save_credentials(126 array(127 'config_id' => sanitize_text_field( $config_id ),128 )129 );130 131 return new WP_REST_Response(132 array(133 'success' => true,134 'status' => $settings->get_status(),135 )136 );137 }138 103 } -
cookietrust/trunk/includes/Routes/Api.php
r3462854 r3463836 47 47 $route->get( '/settings', '\CookieTrust\Controllers\SettingsController@get', $admin_auth ); 48 48 $route->post( '/settings', '\CookieTrust\Controllers\SettingsController@update', $admin_auth ); 49 $route->post( '/settings/complete-setup', '\CookieTrust\Controllers\SettingsController@complete_setup', $admin_auth );50 49 51 50 // Allow hooks to add more custom API routes. -
cookietrust/trunk/includes/Services/CookieTrustApiService.php
r3462856 r3463836 30 30 31 31 /** 32 * A ccess token for APIauthentication.32 * API key for authentication. 33 33 * 34 34 * @var string 35 35 */ 36 private string $a ccess_token;36 private string $api_key; 37 37 38 38 /** 39 39 * Constructor. 40 40 * 41 * @param string $a ccess_token The JWT access token.41 * @param string $api_key The API key. 42 42 */ 43 public function __construct( string $a ccess_token) {44 $this->base_url = defined( 'COOKIETRUST_API_BASE_URL' ) ? COOKIETRUST_API_BASE_URL : 'https://api.cookietrust.io';45 $this->a ccess_token = $access_token;43 public function __construct( string $api_key ) { 44 $this->base_url = defined( 'COOKIETRUST_API_BASE_URL' ) ? COOKIETRUST_API_BASE_URL : 'https://api.cookietrust.io'; 45 $this->api_key = $api_key; 46 46 } 47 47 … … 78 78 'method' => $method, 79 79 'headers' => array( 80 ' Authorization' => 'Bearer ' . $this->access_token,81 'Accept' => 'application/json',82 'Content-Type' => 'application/json',80 'X-Api-Key' => $this->api_key, 81 'Accept' => 'application/json', 82 'Content-Type' => 'application/json', 83 83 ), 84 84 'timeout' => 30, … … 196 196 ); 197 197 } 198 199 /**200 * Complete the WordPress integration setup.201 *202 * This method:203 * 1. Gets or creates a company for the domain204 * 2. Gets or creates a cookie config205 * 3. Creates an API key for the widget206 *207 * @param string $domain The WordPress site domain.208 * @return array{api_key: string, config_id: string, company_id: string}|WP_Error Setup result with api_key, config_id, etc.209 */210 public function complete_setup( string $domain ) {211 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log212 error_log( "[CookieTrust] Starting setup for domain: {$domain}" );213 214 // Step 1: Get or create company.215 $companies = $this->get_companies();216 if ( is_wp_error( $companies ) ) {217 return $companies;218 }219 220 $company = null;221 222 // Look for existing company matching this domain.223 if ( ! empty( $companies ) ) {224 foreach ( $companies as $c ) {225 if ( isset( $c['name'] ) && stripos( $c['name'], $domain ) !== false ) {226 $company = $c;227 break;228 }229 }230 // If no match, use first company.231 if ( ! $company && ! empty( $companies[0] ) ) {232 $company = $companies[0];233 }234 }235 236 // Create company if none exists.237 if ( ! $company ) {238 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log239 error_log( "[CookieTrust] Creating company for domain: {$domain}" );240 $company = $this->create_company( $domain );241 if ( is_wp_error( $company ) ) {242 return $company;243 }244 }245 246 $company_id = $company['id'] ?? $company['data']['id'] ?? null;247 if ( ! $company_id ) {248 return new WP_Error( 'setup_error', 'Failed to get company ID' );249 }250 251 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log252 error_log( "[CookieTrust] Using company: {$company_id}" );253 254 // Step 2: Get or create cookie config.255 $configs = $this->get_cookie_configs( $company_id );256 if ( is_wp_error( $configs ) ) {257 return $configs;258 }259 260 $config = null;261 if ( ! empty( $configs['data'] ) ) {262 $config = $configs['data'][0];263 } elseif ( ! empty( $configs ) && isset( $configs[0] ) ) {264 $config = $configs[0];265 }266 267 if ( ! $config ) {268 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log269 error_log( '[CookieTrust] Creating cookie config' );270 $config = $this->create_cookie_config( $company_id );271 if ( is_wp_error( $config ) ) {272 return $config;273 }274 }275 276 $config_id = $config['id'] ?? $config['data']['id'] ?? null;277 if ( ! $config_id ) {278 return new WP_Error( 'setup_error', 'Failed to get config ID' );279 }280 281 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log282 error_log( "[CookieTrust] Using config: {$config_id}" );283 284 // Step 3: Create API key for this WordPress site.285 $api_key_name = "WordPress - {$domain}";286 $api_key = $this->create_api_key( $api_key_name );287 if ( is_wp_error( $api_key ) ) {288 return $api_key;289 }290 291 $plain_text_key = $api_key['data']['plain_text_key'] ?? $api_key['plain_text_key'] ?? null;292 if ( ! $plain_text_key ) {293 return new WP_Error( 'setup_error', 'Failed to get API key' );294 }295 296 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log297 error_log( '[CookieTrust] Setup complete!' );298 299 return array(300 'api_key' => $plain_text_key,301 'config_id' => $config_id,302 'company_id' => $company_id,303 );304 }305 198 } -
cookietrust/trunk/includes/Services/OAuthService.php
r3462856 r3463836 199 199 * 200 200 * @param string $code The authorization code from OAuth callback. 201 * @return array{access_token : string, email?: string} Token response data.201 * @return array{access_token?: string, api_key?: string, token_type?: string, config_id?: string, company_id?: string, email?: string} Token response data. 202 202 * @throws \Exception If token exchange fails. 203 203 */ … … 236 236 } 237 237 238 if ( empty( $body['access_token'] ) ) { 239 throw new \Exception( 'No access token in response' ); 238 $token_type = $body['token_type'] ?? null; 239 240 if ( 'api_key' === $token_type ) { 241 if ( empty( $body['api_key'] ) ) { 242 throw new \Exception( 'No API key in response' ); 243 } 244 } elseif ( 'Bearer' === $token_type || isset( $body['access_token'] ) ) { 245 if ( empty( $body['access_token'] ) ) { 246 throw new \Exception( 'No access token in response' ); 247 } 248 } else { 249 throw new \Exception( 'Unrecognized token response format' ); 240 250 } 241 251 -
cookietrust/trunk/includes/Services/SettingsService.php
r3462856 r3463836 31 31 32 32 /** 33 * Option key for company ID. 34 */ 35 const OPTION_COMPANY_ID = 'cookietrust_company_id'; 36 37 /** 33 38 * Option key for connected user email. 34 39 */ … … 163 168 public function set_config_id( string $config_id ): void { 164 169 update_option( self::OPTION_CONFIG_ID, sanitize_text_field( $config_id ) ); 170 } 171 172 /** 173 * Get the company ID. 174 * 175 * @return string|null The company ID or null if not set. 176 */ 177 public function get_company_id(): ?string { 178 $value = get_option( self::OPTION_COMPANY_ID ); 179 return $value ? $value : null; 180 } 181 182 /** 183 * Set the company ID. 184 * 185 * @param string $company_id The company ID to store. 186 * @return void 187 */ 188 public function set_company_id( string $company_id ): void { 189 update_option( self::OPTION_COMPANY_ID, sanitize_text_field( $company_id ) ); 165 190 } 166 191 … … 213 238 214 239 // Make HEAD request to check if widget exists. 215 $response = wp_remote_head( 240 $wp_remote_head = 'wp_remote_head'; 241 $response = $wp_remote_head( 216 242 $widget_url, 217 243 array( … … 328 354 $this->clear_verification_cache(); 329 355 } 356 if ( isset( $data['company_id'] ) ) { 357 $this->set_company_id( $data['company_id'] ); 358 } 330 359 if ( isset( $data['email'] ) ) { 331 360 $this->set_user_email( $data['email'] ); … … 345 374 delete_option( self::OPTION_API_KEY ); 346 375 delete_option( self::OPTION_CONFIG_ID ); 376 delete_option( self::OPTION_COMPANY_ID ); 347 377 delete_option( self::OPTION_USER_EMAIL ); 348 378 delete_option( self::OPTION_ENABLED ); -
cookietrust/trunk/plugin.php
r3462854 r3463836 34 34 */ 35 35 public function __construct() { 36 define( 'COOKIETRUST_VERSION', '1. 0.0' );36 define( 'COOKIETRUST_VERSION', '1.1.0' ); 37 37 define( 'COOKIETRUST_PLUGIN_FILE', __FILE__ ); 38 38 define( 'COOKIETRUST_DIR', plugin_dir_path( __FILE__ ) ); -
cookietrust/trunk/readme.txt
r3462958 r3463836 4 4 Requires at least: 5.8 5 5 Tested up to: 6.9 6 Stable tag: 1. 0.46 Stable tag: 1.1.0 7 7 Requires PHP: 7.4 8 8 License: GPLv2 or later … … 81 81 == Changelog == 82 82 83 = 1.1.0 = 84 * replace Sanctum tokens with scoped API keys (wp#119, wp#120, wp#121, wp#122) (#124) 85 * add .sisyphus to gitignore (#123) 86 * docs: update AGENTS.md for scoped API key auth and COOKIETRUST_ constants 87 * test: add CookieTrustApiService and WPConsentAPIService unit tests (#115) (#116) 88 83 89 = 1.0.4 = 84 90 * anchor distignore patterns to prevent vendor file exclusion -
cookietrust/trunk/vendor/composer/installed.php
r3462958 r3463836 4 4 'pretty_version' => 'dev-main', 5 5 'version' => 'dev-main', 6 'reference' => ' a9cdb3316469cf0d8a94023727a4d9373c094f19',6 'reference' => '66524e98833b2e85d8da13d1fe1db7afc7a627fa', 7 7 'type' => 'library', 8 8 'install_path' => __DIR__ . '/../../', … … 14 14 'pretty_version' => 'dev-main', 15 15 'version' => 'dev-main', 16 'reference' => ' a9cdb3316469cf0d8a94023727a4d9373c094f19',16 'reference' => '66524e98833b2e85d8da13d1fe1db7afc7a627fa', 17 17 'type' => 'library', 18 18 'install_path' => __DIR__ . '/../../',
Note: See TracChangeset
for help on using the changeset viewer.