PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.4.0
Fluent Support – Helpdesk & Customer Support Ticket System v2.4.0
2.4.0 2.3.2 2.3.1 2.3.0 2.2.1 2.2.0 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.4.0 1.4.1 1.4.2 1.4.5 1.4.6 1.4.7 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 All 68 releases
fluent-support / app / Hooks / CLI / FluentCli.php

FluentCli.php in Fluent Support – Helpdesk & Customer Support Ticket System 2.4.0, at app/Hooks/CLI/FluentCli.php

352 lines 12.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentSupport\App\Hooks\CLI;
4
5 use FluentSupport\App\Modules\StatModule;
6 use FluentSupport\App\Services\Tickets\Importer\MigratorService;
7 use FluentSupport\App\Models\Meta;
8
9 class FluentCli
10 {
11 private $domain;
12 private $accessToken;
13
14 public function stats($args, $assoc_args)
15 {
16 $overallStats = StatModule::getOverAllStats();
17 $format = \WP_CLI\Utils\get_flag_value($assoc_args, 'format', 'table');
18
19 \WP_CLI\Utils\format_items(
20 $format,
21 $overallStats,
22 ['title', 'count']
23 );
24 }
25
26 public function activate_license($args, $assoc_args)
27 {
28 if (empty($assoc_args['key'])) {
29 \WP_CLI::line('use --key=LICENSE_KEY to activate the license');
30 return;
31 }
32
33 $licenseKey = trim(sanitize_text_field($assoc_args['key'] ?? ''));
34
35 if (!class_exists('\FluentSupportPro\App\Services\PluginManager\LicenseManager')) {
36 \WP_CLI::line('FluentSupport Pro is required');
37 return;
38 }
39
40 \WP_CLI::line('Validating License, Please wait');
41
42 $licenseManager = new \FluentSupportPro\App\Services\PluginManager\LicenseManager();
43 $response = $licenseManager->activateLicense($licenseKey);
44
45 if (is_wp_error($response)) {
46 \WP_CLI::error($response->get_error_message());
47 return;
48 }
49
50 \WP_CLI::line('Your license key has been successfully updated');
51 \WP_CLI::line('Your License Status: ' . $response['status']);
52 \WP_CLI::line('Expire Date: ' . $response['expires']);
53 return;
54 }
55
56 public function license_status()
57 {
58
59 if (!class_exists('\FluentSupportPro\App\Services\PluginManager\LicenseManager')) {
60 \WP_CLI::line('FluentSupport Pro is required');
61 return;
62 }
63
64 \WP_CLI::line('Fetching License details, Please wait');
65
66 $licenseManager = new \FluentSupportPro\App\Services\PluginManager\LicenseManager();
67 $licenseManager->verifyRemoteLicense(true);
68 $response = $licenseManager->getLicenseDetails();
69
70 \WP_CLI::line('Your License Status: ' . $response['status']);
71 \WP_CLI::line('Expires: ' . $response['expires']);
72 return;
73 }
74
75 public function freshdesk_ticket_import($args, $assoc_args)
76 {
77 try {
78 if (!defined('FLUENTSUPPORTPRO_PLUGIN_VERSION')) {
79 \WP_CLI::error('Fluent Support Pro is required for this command.');
80 return;
81 }
82
83 if (!$this->validateAndExtractParams($assoc_args)) {
84 return;
85 }
86
87 $startPage = $this->checkPreviousMigration();
88
89 $this->processMigration($startPage);
90
91 } finally {
92 $this->domain = null;
93 $this->accessToken = null;
94 }
95 }
96
97 /**
98 * Validates and extracts parameters from CLI arguments.
99 *
100 * @param array $assoc_args Associative array of options.
101 * @return bool True if validation succeeded, false otherwise
102 */
103 private function validateAndExtractParams($assoc_args)
104 {
105 $this->domain = \WP_CLI\Utils\get_flag_value($assoc_args, 'domain');
106 if (empty($this->domain)) {
107 $this->domain = \cli\prompt('Enter your support domain (e.g. https://yourcompany.freshdesk.com)');
108 }
109
110 $this->accessToken = \WP_CLI\Utils\get_flag_value($assoc_args, 'access_token');
111 if (empty($this->accessToken)) {
112 $this->accessToken = \cli\prompt('Enter your access token');
113 }
114
115 if (empty($this->accessToken) || empty($this->domain)) {
116 \WP_CLI::error('Both domain and access token are required to proceed.');
117 return false;
118 }
119
120 return true;
121 }
122
123 /**
124 * Check for previous migration data and determine the starting page.
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.
128 * @return int Page number to start from
129 */
130 private function checkPreviousMigration($metaKey = '_fs_freshdesk_migration_info', $identifier = null)
131 {
132 if ($identifier === null) {
133 $identifier = $this->domain;
134 }
135
136 $metadata = Meta::where('object_type', $metaKey)->first();
137
138 if (empty($metadata)) {
139 return 1;
140 }
141
142 $previouslyImported = maybe_unserialize($metadata->value ?? []);
143 $previousDomain = $metadata->key ?? '';
144
145 if ($previousDomain === $identifier &&
146 !empty($previouslyImported['has_more']) &&
147 $previouslyImported['has_more'] === true &&
148 !empty($previouslyImported['next_page'])) {
149
150 \WP_CLI::line("A previous incomplete migration was found for this domain:");
151 \WP_CLI::line(" - Last imported page: " . ($previouslyImported['imported_page'] ?? 'N/A'));
152 \WP_CLI::line(" - Tickets imported so far: " . ($previouslyImported['completed'] ?? 0));
153
154 $choice = \cli\prompt('Do you want to resume from the last incomplete page? (yes/no)', 'yes');
155
156 if (strtolower($choice) === 'yes') {
157 $startPage = (int) $previouslyImported['next_page'];
158 \WP_CLI::line("Resuming from page $startPage...");
159 return $startPage;
160 }
161
162 \WP_CLI::line("Starting over from page 1...");
163 }
164
165 return 1;
166 }
167
168 /**
169 * Process the migration by fetching and importing tickets page by page.
170 *
171 * Supports both page-number pagination (Freshdesk) and cursor-based pagination
172 * (Zendesk) by carrying cursor/next_page_url forward from each response.
173 *
174 * @param int $startPage Page number to start from
175 * @param string $provider Handler name passed to MigratorService. Defaults to 'freshdesk'.
176 * @param array $query Credentials and pagination state. Defaults to Freshdesk class props.
177 */
178 private function processMigration($startPage = 1, $provider = 'freshdesk', $query = null)
179 {
180 try {
181 if ($query === null) {
182 $query = [
183 'access_token' => $this->accessToken,
184 'domain' => $this->domain,
185 ];
186 }
187
188 $page = $startPage;
189 $migrator = new MigratorService;
190 $totalImported = 0;
191 $totalSkipped = 0;
192
193 while (true) {
194 \WP_CLI::line("Processing page: $page");
195
196 $result = $migrator->handleImport($page, $provider, $query);
197
198 if (empty($result) || !isset($result['completed'])) {
199 \WP_CLI::warning("Invalid response received for page $page. Stopping migration.");
200 break;
201 }
202
203 // Carry cursor/next_page_url forward for cursor-based providers (e.g. Zendesk)
204 if (array_key_exists('cursor', $result)) {
205 $query['cursor'] = $result['cursor'];
206 }
207 if (array_key_exists('next_page_url', $result)) {
208 $query['next_page_url'] = $result['next_page_url'];
209 }
210
211 if (!empty($result['error'])) {
212 \WP_CLI::error("Migration error: " . ($result['message'] ?? 'Unknown error'));
213 break;
214 }
215
216 $imported = count($result['insert_ids'] ?? []);
217 $skipped = intval($result['skips'] ?? 0);
218
219 $totalImported += $imported;
220 $totalSkipped += $skipped;
221
222 \WP_CLI::success(sprintf(
223 "Page %d processed: %d ticket(s) imported, %d skipped",
224 $page,
225 $imported,
226 $skipped
227 ));
228
229 if (empty($result['has_more'])) {
230 break;
231 }
232
233 $page = intval($result['next_page'] ?? ($page + 1));
234 }
235
236 \WP_CLI::success(sprintf(
237 "Migration complete. Total: %d imported, %d skipped.",
238 $totalImported,
239 $totalSkipped
240 ));
241 } catch (\Exception $e) {
242 \WP_CLI::error("Migration failed: " . $e->getMessage());
243 }
244 }
245
246 // -------------------------------------------------------------------------
247 // Zendesk
248 // -------------------------------------------------------------------------
249
250 public function zendesk_ticket_import($args, $assoc_args)
251 {
252 try {
253 if (!defined('FLUENTSUPPORTPRO_PLUGIN_VERSION')) {
254 \WP_CLI::error('Fluent Support Pro is required for this command.');
255 return;
256 }
257
258 $this->domain = \WP_CLI\Utils\get_flag_value($assoc_args, 'domain');
259 if (empty($this->domain)) {
260 $this->domain = \cli\prompt('Enter your Zendesk domain (e.g. https://yourcompany.zendesk.com)');
261 }
262
263 $email = \WP_CLI\Utils\get_flag_value($assoc_args, 'email');
264 if (empty($email)) {
265 $email = \cli\prompt('Enter your Zendesk admin email');
266 }
267
268 $this->accessToken = \WP_CLI\Utils\get_flag_value($assoc_args, 'access_token');
269 if (empty($this->accessToken)) {
270 $this->accessToken = \cli\prompt('Enter your API token');
271 }
272
273 if (empty($this->domain) || empty($email) || empty($this->accessToken)) {
274 \WP_CLI::error('Domain, email, and access token are all required to proceed.');
275 return;
276 }
277
278 $includeArchived = \WP_CLI\Utils\get_flag_value($assoc_args, 'include_archived', false);
279
280 $startPage = $this->checkPreviousMigration('_fs_zendesk_migration_info', $this->domain);
281
282 $query = [
283 'access_token' => $this->accessToken,
284 'domain' => $this->domain,
285 'email' => $email,
286 'include_archived' => $includeArchived,
287 ];
288
289 // On resume, restore cursor state saved by the previous run
290 if ($startPage > 1) {
291 $savedProgress = Meta::where('object_type', '_fs_zendesk_migration_info')->first();
292 if ($savedProgress) {
293 $savedData = maybe_unserialize($savedProgress->value ?? []);
294 if (!empty($savedData['cursor'])) {
295 $query['cursor'] = $savedData['cursor'];
296 }
297 if (!empty($savedData['next_page_url'])) {
298 $query['next_page_url'] = $savedData['next_page_url'];
299 }
300 }
301 }
302
303 $this->processMigration($startPage, 'zendesk', $query);
304
305 } finally {
306 $this->domain = null;
307 $this->accessToken = null;
308 }
309 }
310
311 // -------------------------------------------------------------------------
312 // HelpScout
313 // -------------------------------------------------------------------------
314
315 public function helpscout_ticket_import($args, $assoc_args)
316 {
317 try {
318 if (!defined('FLUENTSUPPORTPRO_PLUGIN_VERSION')) {
319 \WP_CLI::error('Fluent Support Pro is required for this command.');
320 return;
321 }
322
323 $this->accessToken = \WP_CLI\Utils\get_flag_value($assoc_args, 'access_token');
324 if (empty($this->accessToken)) {
325 $this->accessToken = \cli\prompt('Enter your HelpScout access token');
326 }
327
328 $mailboxId = \WP_CLI\Utils\get_flag_value($assoc_args, 'mailbox_id');
329 if (empty($mailboxId)) {
330 $mailboxId = \cli\prompt('Enter your HelpScout mailbox ID');
331 }
332
333 if (empty($this->accessToken) || empty($mailboxId)) {
334 \WP_CLI::error('Both access token and mailbox ID are required to proceed.');
335 return;
336 }
337
338 $startPage = $this->checkPreviousMigration('_fs_helpscout_migration_info', $mailboxId);
339
340 $query = [
341 'access_token' => $this->accessToken,
342 'mailbox' => $mailboxId,
343 ];
344
345 $this->processMigration($startPage, 'helpscout', $query);
346
347 } finally {
348 $this->accessToken = null;
349 }
350 }
351 }
352