| @@ -29,8 +29,9 @@ | ||
| 29 | 29 | { |
| 30 | 30 | // This does the initial redirect to Launch. |
| 31 | 31 | \add_action('admin_init', [$this, 'redirectOnce'], 10); |
| 32 | 32 | \add_action('admin_init', [$this, 'maybeForceFlush'], 5); |
| 33 | + \add_action('admin_init', [$this, 'handleLaunchRedirect'], 1); | |
| 33 | 34 | |
| 34 | 35 | // Add a dropdown above Dashboard in the admin toolbar. |
| 35 | 36 | \add_action('admin_bar_menu', function ($wpAdminBar) { |
| 36 | 37 | if (!PartnerData::$id || is_admin()) { |
| @@ -175,10 +176,49 @@ | ||
| 175 | 176 | return 'admin.php?page=extendify-assist'; |
| 176 | 177 | } |
| 177 | 178 | |
| 178 | 179 | /** |
| 180 | + * Routes /wp-admin/?launch-redirect based on Launch and Agent state. | |
| 181 | + * | |
| 182 | + * @return void | |
| 183 | + */ | |
| 184 | + public function handleLaunchRedirect() | |
| 185 | + { | |
| 186 | + // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 187 | + if (!isset($_GET['launch-redirect']) || \wp_doing_ajax() || !PartnerData::$id) { | |
| 188 | + return; | |
| 189 | + } | |
| 190 | + | |
| 191 | + // If launch hasn't yet completed, they go to launch/auto-launch | |
| 192 | + if (!Config::$launchCompleted) { | |
| 193 | + $useAgentOnboarding = PartnerData::setting('useAgentOnboarding') || | |
| 194 | + Config::preview('agent-onboarding') || | |
| 195 | + constant('EXTENDIFY_DEVMODE'); | |
| 196 | + $page = $useAgentOnboarding ? 'extendify-auto-launch' : 'extendify-launch'; | |
| 197 | + $query_params = $this->presentLaunchParams(['page' => $page]); | |
| 198 | + \wp_safe_redirect(\add_query_arg($query_params, \admin_url('admin.php'))); | |
| 199 | + exit; | |
| 200 | + } | |
| 201 | + | |
| 202 | + // If they have the Agent onboarding enabled, redirect to home. | |
| 203 | + if (PartnerData::setting('showAIAgents')) { | |
| 204 | + \wp_safe_redirect(\add_query_arg( | |
| 205 | + ['extendify-open-agent' => '1'], | |
| 206 | + \home_url('/') | |
| 207 | + )); | |
| 208 | + exit; | |
| 209 | + } | |
| 210 | + | |
| 211 | + // Otherwise, they go to Assist. | |
| 212 | + \wp_safe_redirect(\admin_url() . $this->getRoute()); | |
| 213 | + exit; | |
| 214 | + } | |
| 215 | + | |
| 216 | + /** | |
| 179 | 217 | * Redirect once to Launch, only once (at least once) when |
| 180 | 218 | * the email matches the entry in WP Admin > Settings > General. |
| 219 | + * Requests carrying a build-id keep redirecting until onboarding | |
| 220 | + * completes. | |
| 181 | 221 | * |
| 182 | 222 | * @return void |
| 183 | 223 | */ |
| 184 | 224 | public function redirectOnce() |
| @@ -190,12 +230,21 @@ | ||
| 190 | 230 | if (defined('EXTENDIFY_IS_THEME_EXTENDABLE') && !EXTENDIFY_IS_THEME_EXTENDABLE) { |
| 191 | 231 | return; |
| 192 | 232 | } |
| 193 | 233 | |
| 194 | - if (\get_option('extendify_launch_loaded', false) || !Config::$showLaunch) { | |
| 234 | + // Partner preview links must work on every admin visit, not just the first. | |
| 235 | + // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash | |
| 236 | + $buildIdRedirect = !empty(sanitize_text_field(wp_unslash($_GET['build-id'] ?? ''))) | |
| 237 | + && !Config::$launchCompleted; | |
| 238 | + | |
| 239 | + if (!$buildIdRedirect && \get_option('extendify_launch_loaded', false)) { | |
| 195 | 240 | return; |
| 196 | 241 | } |
| 197 | 242 | |
| 243 | + if (!Config::$showLaunch) { | |
| 244 | + return; | |
| 245 | + } | |
| 246 | + | |
| 198 | 247 | // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 199 | 248 | if (isset($_GET['page']) && $_GET['page'] === 'extendify-auto-launch') { |
| 200 | 249 | return; |
| 201 | 250 | } |
| @@ -206,19 +255,27 @@ | ||
| 206 | 255 | $agentOnboarding = PartnerData::setting('useAgentOnboarding') || |
| 207 | 256 | Config::preview('agent-onboarding') || |
| 208 | 257 | constant('EXTENDIFY_DEVMODE'); |
| 209 | 258 | if ($agentOnboarding) { |
| 210 | - // If they landed on launch but have the Agent onboarding enabled, redirect to auto-launch. | |
| 211 | - $redirect_url = \add_query_arg( | |
| 212 | - ['page' => 'extendify-auto-launch'], | |
| 213 | - \admin_url('admin.php') | |
| 214 | - ); | |
| 215 | - \wp_safe_redirect($redirect_url); | |
| 259 | + // If they landed on launch but have the Agent onboarding enabled, redirect to | |
| 260 | + // auto-launch — carrying their deep-link params, which a bare page-only redirect drops. | |
| 261 | + $query_params = $this->presentLaunchParams(['page' => 'extendify-auto-launch']); | |
| 262 | + \wp_safe_redirect(\add_query_arg($query_params, \admin_url('admin.php'))); | |
| 216 | 263 | exit; |
| 217 | 264 | } |
| 218 | 265 | return; |
| 219 | 266 | } |
| 220 | 267 | |
| 268 | + if ($buildIdRedirect) { | |
| 269 | + \update_option('permalink_structure', '/%postname%/'); | |
| 270 | + \update_option('extendify_needs_rewrite_flush', true); | |
| 271 | + | |
| 272 | + // Only AutoLaunch consumes build-id. | |
| 273 | + $query_params = $this->presentLaunchParams(['page' => 'extendify-auto-launch']); | |
| 274 | + \wp_safe_redirect(\add_query_arg($query_params, \admin_url('admin.php'))); | |
| 275 | + exit; | |
| 276 | + } | |
| 277 | + | |
| 221 | 278 | $user = \wp_get_current_user(); |
| 222 | 279 | if ( |
| 223 | 280 | $user |
| 224 | 281 | // Check the main admin email, and they have an admin role. |
| @@ -237,52 +294,62 @@ | ||
| 237 | 294 | // Update permalink structure to postname when auto-redirecting to Launch |
| 238 | 295 | \update_option('permalink_structure', '/%postname%/'); |
| 239 | 296 | \update_option('extendify_needs_rewrite_flush', true); |
| 240 | 297 | |
| 241 | - $allowed_launch_params = [ | |
| 242 | - 'objective', | |
| 243 | - 'title', | |
| 244 | - 'description', | |
| 245 | - 'structure', | |
| 246 | - 'tone', | |
| 247 | - 'skip', | |
| 248 | - // new for autolaunch (some duplicated) | |
| 249 | - 'type', | |
| 250 | - 'title', | |
| 251 | - 'description', | |
| 252 | - 'objective', | |
| 253 | - 'category', | |
| 254 | - 'structure', | |
| 255 | - 'tone', | |
| 256 | - 'products', | |
| 257 | - 'appointments', | |
| 258 | - 'events', | |
| 259 | - 'donations', | |
| 260 | - 'multilingual', | |
| 261 | - 'contact', | |
| 262 | - 'address', | |
| 263 | - 'blog', | |
| 264 | - 'landing-page', | |
| 265 | - 'cta-link', | |
| 266 | - 'build-id', | |
| 267 | - 'go', | |
| 268 | - ]; | |
| 269 | 298 | // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash |
| 270 | 299 | $autoLaunch = isset($_GET['auto-launch']) && filter_var($_GET['auto-launch'], FILTER_VALIDATE_BOOLEAN); |
| 271 | - $query_params = ['page' => $autoLaunch ? 'extendify-auto-launch' : 'extendify-launch']; | |
| 300 | + $query_params = $this->presentLaunchParams([ | |
| 301 | + 'page' => $autoLaunch ? 'extendify-auto-launch' : 'extendify-launch', | |
| 302 | + ]); | |
| 272 | 303 | |
| 273 | - foreach ($allowed_launch_params as $param) { | |
| 274 | - // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 275 | - $value = sanitize_text_field(wp_unslash($_GET[$param] ?? '')); | |
| 276 | - if (!empty($value)) { | |
| 277 | - $query_params[$param] = $value; | |
| 278 | - } | |
| 279 | - } | |
| 280 | - | |
| 281 | 304 | $redirect_url = \add_query_arg($query_params, \admin_url('admin.php')); |
| 282 | 305 | \wp_safe_redirect($redirect_url); |
| 283 | 306 | exit; |
| 284 | 307 | } |
| 308 | + } | |
| 309 | + | |
| 310 | + /** | |
| 311 | + * Merges the allowlisted launch deep-link params present in the request onto | |
| 312 | + * $base, so a redirect into Launch/AutoLaunch carries the partner's params | |
| 313 | + * through instead of dropping them. | |
| 314 | + * | |
| 315 | + * @param array $base Seed query args (typically the destination `page`). | |
| 316 | + * @return array | |
| 317 | + */ | |
| 318 | + private function presentLaunchParams(array $base) | |
| 319 | + { | |
| 320 | + $allowed = [ | |
| 321 | + 'objective', | |
| 322 | + 'title', | |
| 323 | + 'description', | |
| 324 | + 'structure', | |
| 325 | + 'tone', | |
| 326 | + 'skip', | |
| 327 | + 'type', | |
| 328 | + 'category', | |
| 329 | + 'products', | |
| 330 | + 'appointments', | |
| 331 | + 'events', | |
| 332 | + 'donations', | |
| 333 | + 'multilingual', | |
| 334 | + 'contact', | |
| 335 | + 'address', | |
| 336 | + 'blog', | |
| 337 | + 'landing-page', | |
| 338 | + 'cta-link', | |
| 339 | + 'build-id', | |
| 340 | + 'go', | |
| 341 | + ]; | |
| 342 | + | |
| 343 | + foreach ($allowed as $param) { | |
| 344 | + // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 345 | + $value = sanitize_text_field(wp_unslash($_GET[$param] ?? '')); | |
| 346 | + if (!empty($value)) { | |
| 347 | + $base[$param] = $value; | |
| 348 | + } | |
| 349 | + } | |
| 350 | + | |
| 351 | + return $base; | |
| 285 | 352 | } |
| 286 | 353 | |
| 287 | 354 | /** |
| 288 | 355 | * Flushes WordPress rewrite rules if previously scheduled by `redirectOnce()`. |