| @@ -29,9 +29,9 @@ | ||
| 29 | 29 | \WP_CLI::line('use --key=LICENSE_KEY to activate the license'); |
| 30 | 30 | return; |
| 31 | 31 | } |
| 32 | 32 | |
| 33 | - $licenseKey = trim(sanitize_text_field($assoc_args['key'])); | |
| 33 | + $licenseKey = trim(sanitize_text_field($assoc_args['key'] ?? '')); | |
| 34 | 34 | |
| 35 | 35 | if (!class_exists('\FluentSupportPro\App\Services\PluginManager\LicenseManager')) { |
| 36 | 36 | \WP_CLI::line('FluentSupport Pro is required'); |
| 37 | 37 | return; |
| @@ -122,14 +122,20 @@ | ||
| 122 | 122 | |
| 123 | 123 | /** |
| 124 | 124 | * Check for previous migration data and determine the starting page. |
| 125 | 125 | * |
| 126 | + * @param string $metaKey The object_type key in fs_meta. Defaults to Freshdesk. | |
| 127 | + * @param string|null $identifier The domain or other unique identifier. Defaults to $this->domain. | |
| 126 | 128 | * @return int Page number to start from |
| 127 | 129 | */ |
| 128 | - private function checkPreviousMigration() | |
| 130 | + private function checkPreviousMigration($metaKey = '_fs_freshdesk_migration_info', $identifier = null) | |
| 129 | 131 | { |
| 130 | - $metadata = Meta::where('object_type', '_fs_freshdesk_migration_info')->first(); | |
| 132 | + if ($identifier === null) { | |
| 133 | + $identifier = $this->domain; | |
| 134 | + } | |
| 131 | 135 | |
| 136 | + $metadata = Meta::where('object_type', $metaKey)->first(); | |
| 137 | + | |
| 132 | 138 | if (empty($metadata)) { |
| 133 | 139 | return 1; |
| 134 | 140 | } |
| 135 | 141 | |
| @@ -135,9 +141,9 @@ | ||
| 135 | 141 | |
| 136 | 142 | $previouslyImported = maybe_unserialize($metadata->value ?? []); |
| 137 | 143 | $previousDomain = $metadata->key ?? ''; |
| 138 | 144 | |
| 139 | - if ($previousDomain === $this->domain && | |
| 145 | + if ($previousDomain === $identifier && | |
| 140 | 146 | !empty($previouslyImported['has_more']) && |
| 141 | 147 | $previouslyImported['has_more'] === true && |
| 142 | 148 | !empty($previouslyImported['next_page'])) { |
| 143 | 149 | |
| @@ -159,15 +165,24 @@ | ||
| 159 | 165 | return 1; |
| 160 | 166 | } |
| 161 | 167 | |
| 162 | 168 | /** |
| 163 | - * Process the migration by fetching and importing tickets page by page. | |
| 169 | + * Import tickets page by page until the provider reports no more. | |
| 164 | 170 | * |
| 165 | - * @param int $startPage Page number to start from | |
| 171 | + * @param int $startPage Page number to start from | |
| 172 | + * @param string $provider Handler name passed to MigratorService. Defaults to 'freshdesk'. | |
| 173 | + * @param array $query Credentials and pagination state. Defaults to Freshdesk class props. | |
| 166 | 174 | */ |
| 167 | - private function processMigration($startPage = 1) | |
| 175 | + private function processMigration($startPage = 1, $provider = 'freshdesk', $query = null) | |
| 168 | 176 | { |
| 169 | 177 | try { |
| 178 | + if ($query === null) { | |
| 179 | + $query = [ | |
| 180 | + 'access_token' => $this->accessToken, | |
| 181 | + 'domain' => $this->domain, | |
| 182 | + ]; | |
| 183 | + } | |
| 184 | + | |
| 170 | 185 | $page = $startPage; |
| 171 | 186 | $migrator = new MigratorService; |
| 172 | 187 | $totalImported = 0; |
| 173 | 188 | $totalSkipped = 0; |
| @@ -174,12 +189,9 @@ | ||
| 174 | 189 | |
| 175 | 190 | while (true) { |
| 176 | 191 | \WP_CLI::line("Processing page: $page"); |
| 177 | 192 | |
| 178 | - $result = $migrator->handleImport($page, 'freshdesk', [ | |
| 179 | - 'access_token' => $this->accessToken, | |
| 180 | - 'domain' => $this->domain | |
| 181 | - ]); | |
| 193 | + $result = $migrator->handleImport($page, $provider, $query); | |
| 182 | 194 | |
| 183 | 195 | if (empty($result) || !isset($result['completed'])) { |
| 184 | 196 | \WP_CLI::warning("Invalid response received for page $page. Stopping migration."); |
| 185 | 197 | break; |
| @@ -184,8 +196,21 @@ | ||
| 184 | 196 | \WP_CLI::warning("Invalid response received for page $page. Stopping migration."); |
| 185 | 197 | break; |
| 186 | 198 | } |
| 187 | 199 | |
| 200 | + // Cursor-based providers (Zendesk) need these carried into the next request. | |
| 201 | + if (array_key_exists('cursor', $result)) { | |
| 202 | + $query['cursor'] = $result['cursor']; | |
| 203 | + } | |
| 204 | + if (array_key_exists('next_page_url', $result)) { | |
| 205 | + $query['next_page_url'] = $result['next_page_url']; | |
| 206 | + } | |
| 207 | + | |
| 208 | + if (!empty($result['error'])) { | |
| 209 | + \WP_CLI::error("Migration error: " . ($result['message'] ?? 'Unknown error')); | |
| 210 | + break; | |
| 211 | + } | |
| 212 | + | |
| 188 | 213 | $imported = count($result['insert_ids'] ?? []); |
| 189 | 214 | $skipped = intval($result['skips'] ?? 0); |
| 190 | 215 | |
| 191 | 216 | $totalImported += $imported; |
| @@ -211,7 +236,113 @@ | ||
| 211 | 236 | $totalSkipped |
| 212 | 237 | )); |
| 213 | 238 | } catch (\Exception $e) { |
| 214 | 239 | \WP_CLI::error("Migration failed: " . $e->getMessage()); |
| 240 | + } | |
| 241 | + } | |
| 242 | + | |
| 243 | + // ------------------------------------------------------------------------- | |
| 244 | + // Zendesk | |
| 245 | + // ------------------------------------------------------------------------- | |
| 246 | + | |
| 247 | + public function zendesk_ticket_import($args, $assoc_args) | |
| 248 | + { | |
| 249 | + try { | |
| 250 | + if (!defined('FLUENTSUPPORTPRO_PLUGIN_VERSION')) { | |
| 251 | + \WP_CLI::error('Fluent Support Pro is required for this command.'); | |
| 252 | + return; | |
| 253 | + } | |
| 254 | + | |
| 255 | + $this->domain = \WP_CLI\Utils\get_flag_value($assoc_args, 'domain'); | |
| 256 | + if (empty($this->domain)) { | |
| 257 | + $this->domain = \cli\prompt('Enter your Zendesk domain (e.g. https://yourcompany.zendesk.com)'); | |
| 258 | + } | |
| 259 | + | |
| 260 | + $email = \WP_CLI\Utils\get_flag_value($assoc_args, 'email'); | |
| 261 | + if (empty($email)) { | |
| 262 | + $email = \cli\prompt('Enter your Zendesk admin email'); | |
| 263 | + } | |
| 264 | + | |
| 265 | + $this->accessToken = \WP_CLI\Utils\get_flag_value($assoc_args, 'access_token'); | |
| 266 | + if (empty($this->accessToken)) { | |
| 267 | + $this->accessToken = \cli\prompt('Enter your API token'); | |
| 268 | + } | |
| 269 | + | |
| 270 | + if (empty($this->domain) || empty($email) || empty($this->accessToken)) { | |
| 271 | + \WP_CLI::error('Domain, email, and access token are all required to proceed.'); | |
| 272 | + return; | |
| 273 | + } | |
| 274 | + | |
| 275 | + $includeArchived = \WP_CLI\Utils\get_flag_value($assoc_args, 'include_archived', false); | |
| 276 | + | |
| 277 | + $startPage = $this->checkPreviousMigration('_fs_zendesk_migration_info', $this->domain); | |
| 278 | + | |
| 279 | + $query = [ | |
| 280 | + 'access_token' => $this->accessToken, | |
| 281 | + 'domain' => $this->domain, | |
| 282 | + 'email' => $email, | |
| 283 | + 'include_archived' => $includeArchived, | |
| 284 | + ]; | |
| 285 | + | |
| 286 | + // On resume, restore cursor state saved by the previous run | |
| 287 | + if ($startPage > 1) { | |
| 288 | + $savedProgress = Meta::where('object_type', '_fs_zendesk_migration_info')->first(); | |
| 289 | + if ($savedProgress) { | |
| 290 | + $savedData = maybe_unserialize($savedProgress->value ?? []); | |
| 291 | + if (!empty($savedData['cursor'])) { | |
| 292 | + $query['cursor'] = $savedData['cursor']; | |
| 293 | + } | |
| 294 | + if (!empty($savedData['next_page_url'])) { | |
| 295 | + $query['next_page_url'] = $savedData['next_page_url']; | |
| 296 | + } | |
| 297 | + } | |
| 298 | + } | |
| 299 | + | |
| 300 | + $this->processMigration($startPage, 'zendesk', $query); | |
| 301 | + | |
| 302 | + } finally { | |
| 303 | + $this->domain = null; | |
| 304 | + $this->accessToken = null; | |
| 305 | + } | |
| 306 | + } | |
| 307 | + | |
| 308 | + // ------------------------------------------------------------------------- | |
| 309 | + // HelpScout | |
| 310 | + // ------------------------------------------------------------------------- | |
| 311 | + | |
| 312 | + public function helpscout_ticket_import($args, $assoc_args) | |
| 313 | + { | |
| 314 | + try { | |
| 315 | + if (!defined('FLUENTSUPPORTPRO_PLUGIN_VERSION')) { | |
| 316 | + \WP_CLI::error('Fluent Support Pro is required for this command.'); | |
| 317 | + return; | |
| 318 | + } | |
| 319 | + | |
| 320 | + $this->accessToken = \WP_CLI\Utils\get_flag_value($assoc_args, 'access_token'); | |
| 321 | + if (empty($this->accessToken)) { | |
| 322 | + $this->accessToken = \cli\prompt('Enter your HelpScout access token'); | |
| 323 | + } | |
| 324 | + | |
| 325 | + $mailboxId = \WP_CLI\Utils\get_flag_value($assoc_args, 'mailbox_id'); | |
| 326 | + if (empty($mailboxId)) { | |
| 327 | + $mailboxId = \cli\prompt('Enter your HelpScout mailbox ID'); | |
| 328 | + } | |
| 329 | + | |
| 330 | + if (empty($this->accessToken) || empty($mailboxId)) { | |
| 331 | + \WP_CLI::error('Both access token and mailbox ID are required to proceed.'); | |
| 332 | + return; | |
| 333 | + } | |
| 334 | + | |
| 335 | + $startPage = $this->checkPreviousMigration('_fs_helpscout_migration_info', $mailboxId); | |
| 336 | + | |
| 337 | + $query = [ | |
| 338 | + 'access_token' => $this->accessToken, | |
| 339 | + 'mailbox' => $mailboxId, | |
| 340 | + ]; | |
| 341 | + | |
| 342 | + $this->processMigration($startPage, 'helpscout', $query); | |
| 343 | + | |
| 344 | + } finally { | |
| 345 | + $this->accessToken = null; | |
| 215 | 346 | } |
| 216 | 347 | } |
| 217 | 348 | } |