Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Help Scout to Mailbox API 2.0 #12

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion assets/dist/importer.js
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,8 @@ window.awesomeSupportImporter = window.awesomeSupportImporter || {
$mailboxSelect,
$helpScoutMessage,
$mailboxesLoadedMessage,
$errorMessage;
$errorMessage,
$tokenMessage;

/**
* Initializes the settings page.
Expand All @@ -496,7 +497,10 @@ window.awesomeSupportImporter = window.awesomeSupportImporter || {
$helpScoutMessage = $('#awesome-support-importing-getting-mailboxes-message');
$mailboxesLoadedMessage = $('.awesome-support-importer-mailboxes-loaded');
$errorMessage = $('.awesome-support-importer-message');
$tokenMessage = $('#awesome-support-access-token-message');
$('#awesome-support-get-helpscout-mailboxes').on('click', getMailboxes);
$('#awesome-support-helpscout-authorize').on('click', getAuthorize);
$('#awesome-support-helpscout-access-token').on('click', getAccessToken);
};

helpScout.render = function (selectedApi) {
Expand Down Expand Up @@ -576,5 +580,47 @@ window.awesomeSupportImporter = window.awesomeSupportImporter || {
});
};

var getAuthorize = function getAuthorize(event) {
event.preventDefault();
if (!$('#awesome-support-importer-app-id').val() || !$('#awesome-support-importer-app-secret').val()) {
return;
}

// Prepare the data to be sent to the server.
var data = importer.getOptionValues();
window.open("https://secure.helpscout.net/authentication/authorizeClientApplication?client_id=" + data['awesome-support-importer-app-id'] + "&state=" + data['awesome-support-importer-app-secret'], '_blank');
};

var getAccessToken = function getAccessToken(event) {
event.preventDefault();
if (!$('#awesome-support-importer-app-id').val()
|| !$('#awesome-support-importer-app-secret').val()
|| !$('#awesome-support-importer-app-code').val()) {
return;
}

// Prepare the data to be sent to the server.
var data = importer.getOptionValues();

var prepareData = {
code: data['awesome-support-importer-app-code'],
client_id: data['awesome-support-importer-app-id'],
client_secret: data['awesome-support-importer-app-secret'],
grant_type: 'authorization_code'
};

// Then do the actual request.
$.post('https://api.helpscout.net/v2/oauth2/token', prepareData, function (resJSON) {
console.log('got them!');
$tokenMessage.addClass('is-success').removeClass('is-error').empty().html('Access Token: ' + resJSON.access_token).show();
}).fail(function (jqXHR) {
$tokenMessage.removeClass('is-success').addClass('is-error').empty().html(jqXHR.responseJSON.error_description).show();
}).always(function () {
// Always reset the form's interface when the Ajax request is done.
$helpScoutMessage.hide();
importer.resetFormAjaxDone();
});
};

importer.helpScout = helpScout;
})(jQuery, window, document, window.awesomeSupportImporter);
6 changes: 4 additions & 2 deletions assets/styles/admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,15 @@
Help Scout
*/
[data-helpscout="1"],
#awesome-support-get-helpscout-mailboxes {
#awesome-support-get-helpscout-mailboxes,
#awesome-support-get-helpscout-access-token {
display: none;
}
#awesome-support-importer[data-selectedapi="help-scout"] [data-helpscout="1"] {
display: block;
}

#awesome-support-importer[data-selectedapi="help-scout"] #awesome-support-get-helpscout-mailboxes {
#awesome-support-importer[data-selectedapi="help-scout"] #awesome-support-get-helpscout-mailboxes,
#awesome-support-importer[data-selectedapi="help-scout"] #awesome-support-get-helpscout-access-token {
display: inline-block;
}
6 changes: 4 additions & 2 deletions assets/styles/admin.scss
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@
Help Scout
*/
[data-helpscout="1"],
#awesome-support-get-helpscout-mailboxes {
#awesome-support-get-helpscout-mailboxes,
#awesome-support-get-helpscout-access-token {
display: none;
}

Expand All @@ -70,7 +71,8 @@ Help Scout
display: block;
}

#awesome-support-get-helpscout-mailboxes {
#awesome-support-get-helpscout-mailboxes,
#awesome-support-get-helpscout-access-token {
display: inline-block;
}
}
2 changes: 2 additions & 0 deletions config/plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
$optionsPrefix . 'api-token' => '',
$optionsPrefix . 'date-start' => '',
$optionsPrefix . 'date-end' => '',
$optionsPrefix . 'app-secret' => '',
$optionsPrefix . 'app-id' => '',
],
'screenName' => 'awesome_support_import_tickets',
'redirectUri' => 'wp-admin/edit.php?post_type=ticket&page=awesome_support_import_tickets',
Expand Down
34 changes: 15 additions & 19 deletions src/API/Provider/HelpScout/ApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ class ApiController extends ProviderController
* @since 0.1.0
*
* @return array
* @link https://developer.helpscout.com/help-desk-api/mailboxes/list/
* @link https://developer.helpscout.com/mailbox-api/endpoints/mailboxes/list/
*/
public function getMailboxes()
{
$selectOptions = [];
$endpoint = 'https://api.helpscout.net/v1/mailboxes.json';
$endpoint = 'https://api.helpscout.net//v2/mailboxes';
$json = $this->get($endpoint);
foreach ($this->fromJSON($json)->items as $mailbox) {
$_embedded = $this->fromJSON($json)->_embedded;
foreach ($_embedded->mailboxes as $mailbox) {
$selectOptions[$mailbox->id] = $mailbox->name;
}
return $selectOptions;
Expand Down Expand Up @@ -47,18 +48,19 @@ protected function request()
do {
$endpoint = $this->getEndpoint($pageNumber);
$packet = $this->fromJSON($this->get($endpoint));
$_embedded = $packet->_embedded;

// No conversations. We're done.
if (!$packet->count) {
if (!$packet->page->totalElements) {
break;
}

// Process each of the items and request each conversation object.
foreach ($packet->items as $item) {
foreach ($_embedded->conversations as $item) {
$this->requestConversation((int)$item->id);
}
// continue iterating until there are no more pages to fetch.
} while ($packet->page < $packet->pages);
} while ($packet->page->number < $packet->page->totalPages);
}

/**
Expand All @@ -69,11 +71,11 @@ protected function request()
* @param int|string $conversationId
*
* @return void
* @link https://developer.helpscout.com/help-desk-api/conversations/get/
* @link https://developer.helpscout.com/mailbox-api/endpoints/conversations/get/
*/
protected function requestConversation($conversationId)
{
$endpoint = "https://api.helpscout.net/v1/conversations/{$conversationId}.json";
$endpoint = "https://api.helpscout.net/v2/conversations/{$conversationId}?embed=threads";
$packet = $this->get($endpoint);
$this->dataMapper->mapJSON($packet);
}
Expand All @@ -86,30 +88,24 @@ protected function requestConversation($conversationId)
* @param int|null $nextPage If provided, appends the `page={$nextPage}` query var to endpoint
*
* @return string
* @link https://developer.helpscout.com/help-desk-api/conversations/list/
* @link https://developer.helpscout.com/mailbox-api/endpoints/conversations/list/
*/
protected function getEndpoint($nextPage = null)
{
$endpoint = sprintf(
'https://api.helpscout.net/v1/mailboxes/%s/conversations.json',
'https://api.helpscout.net/v2/conversations?mailbox=%s',
$this->config['mailboxId']
);

// If date limited, append the start time to the endpoint.
$startTime = $this->config['startDate'] ? $this->getStartTime() : '';
if ($startTime || $nextPage) {
$endpoint .= '?';
}
if ($startTime) {
$endpoint .= "modifiedSince={$startTime}";
$endpoint .= "&modifiedSince={$startTime}";
}

// If a page number is provided, append the `page={$nextPage}` to endpoint.
if ($nextPage) {
if ($startTime) {
$endpoint .= '&';
}
$endpoint .= "page={$nextPage}";
$endpoint .= "&page={$nextPage}";
}

return $endpoint;
Expand Down Expand Up @@ -145,7 +141,7 @@ protected function getStartTime()
protected function mergeOptionsWithAuth(array $options)
{
return array_merge($options, [
'auth' => [$this->config['token'], 'X', 'basic',],
'headers' => $this->config['headers'],
]);
}
}
35 changes: 20 additions & 15 deletions src/API/Provider/HelpScout/DataMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ class DataMapper extends AbstractDataMapper
*/
public function mapJSON($json, $key = '')
{
$conversation = $this->fromJSON($json)->item;
$conversation = $this->fromJSON($json);

if (!$this->withinDateRange($conversation->modifiedAt ?: $conversation->createdAt)) {
if (!$this->withinDateRange($conversation->userUpdatedAt ?: $conversation->createdAt)) {
return;
}
$ticketId = $conversation->number;

$this->mapUsers($conversation);
$this->mapTicket($ticketId, $conversation);

foreach ((array)$conversation->threads as $thread) {
foreach ((array)$conversation->_embedded->threads as $thread) {
$this->mapThreads($ticketId, $thread);
}
}
Expand All @@ -46,13 +46,13 @@ public function mapJSON($json, $key = '')
*/
protected function mapTicket($ticketId, $conversation)
{
$customerId = is_object($conversation->customer) ? $conversation->customer->id : 0;
$customerId = is_object($conversation->primaryCustomer) ? $conversation->primaryCustomer->id : 0;
$this->ticketRepository->create($ticketId, [
'agentID' => is_object($conversation->owner) ? $conversation->owner->id : 0,
'customerID' => is_object($conversation->customer) ? $conversation->customer->id : 0,
'agentID' => is_object($conversation->createdBy) ? $conversation->createdBy->id : 0,
'customerID' => is_object($conversation->primaryCustomer) ? $conversation->primaryCustomer->id : 0,
'subject' => $conversation->subject,
'createdAt' => $conversation->createdAt,
'updatedAt' => $conversation->modifiedAt,
'updatedAt' => $conversation->userUpdatedAt,
]);

$this->historyRepository->create(
Expand Down Expand Up @@ -104,6 +104,9 @@ protected function mapThreads($ticketId, $thread)
*/
protected function isOriginalTicket($thread)
{
if (!property_exists($thread, 'state')) {
return false;
}
return 'published' === $thread->state && 'customer' === $thread->type;
}

Expand All @@ -118,7 +121,9 @@ protected function isOriginalTicket($thread)
*/
protected function isAReply($thread)
{
return 'message' === $thread->type && $thread->actionSourceId;
return 'message' === $thread->type
&& property_exists($thread->action->associatedEntities, 'originalConversation')
&& !empty($thread->action->associatedEntities->originalConversation);
}

/**
Expand All @@ -137,8 +142,8 @@ protected function isAReply($thread)
public function mapAttachment($attachment)
{
return [
'url' => $attachment->url,
'filename' => $attachment->fileName,
'url' => $attachment->_links->web->href,
'filename' => $attachment->filename,
];
}

Expand All @@ -153,10 +158,10 @@ public function mapAttachment($attachment)
*/
protected function mapUsers($conversation)
{
foreach (['owner', 'customer'] as $property) {
foreach (['createdBy', 'primaryCustomer'] as $property) {
$this->mapUser(
$conversation->{$property},
'customer' === $property ? $property : 'agent'
'primaryCustomer' === $property ? $property : 'agent'
);
}
}
Expand All @@ -183,7 +188,7 @@ protected function mapUser($user, $role = '')
// Cast it to an array for strict mode, i.e. to add more properties.
$user = (array)$user;
$user['role'] = $role;
$user['name'] = "{$user['firstName']} {$user['lastName']}";
$user['name'] = "{$user['first']} {$user['last']}";

// Cast it back to a stdClass object and create the model.
$this->userRepository->createModel((object)$user);
Expand Down Expand Up @@ -216,7 +221,7 @@ protected function mapReply($ticketId, $replyId, \stdClass $thread)
]
);

$this->mapAttachments($thread->attachments, $ticketId, $replyId);
$this->mapAttachments($thread->_embedded->attachments, $ticketId, $replyId);
}

/**
Expand All @@ -232,7 +237,7 @@ protected function mapReply($ticketId, $replyId, \stdClass $thread)
protected function mapOriginalTicket($ticketId, \stdClass $thread)
{
$this->ticketRepository->set("$ticketId.description", $thread->body);
$this->mapAttachments($thread->attachments, $ticketId);
$this->mapAttachments($thread->_embedded->attachments, $ticketId);
}

/**
Expand Down
3 changes: 3 additions & 0 deletions src/API/Provider/HelpScout/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ protected function remapData(array $data)
'mailboxId' => $data[$optionsPrefix . 'api-mailbox'],
'startDate' => $data[$optionsPrefix . 'date-start'],
'endDate' => $data[$optionsPrefix . 'date-end'],
'headers' => [
'Authorization' => 'Bearer '.$data[$optionsPrefix . 'api-token']
]
];
}

Expand Down
3 changes: 2 additions & 1 deletion src/PluginAPI/AbstractAssetSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ public static function getHooks()

public function isCurrentAdminPage()
{
return 'ticket_page_awesome_support_import_tickets' === get_current_screen()->id;
return 'ticket_page_awesome_support_import_tickets' === get_current_screen()->id
|| 'ticket_page_awesome_support_import_tickets_token' === get_current_screen()->id;
}

abstract public function enqueue();
Expand Down
21 changes: 21 additions & 0 deletions src/Subscriber/MenuSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ public function addSubmenu()
'awesome_support_import_tickets',
[$this, 'render']
);
add_submenu_page(
null,
__('Awesome Support: Importer', 'awesome-support-importer'),
__('Get Access Token', 'awesome-support-importer'),
'manage_options',
'awesome_support_import_tickets_token',
[$this, 'render_access_token']
);
}

/**
Expand Down Expand Up @@ -111,6 +119,19 @@ public function render()
include_once $this->pluginPath . '/views/import-options.php';
}

/**
* Render the view. View data is compiled before calling the view.
*
* @since 0.1.0
*
* @return void
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
*/
public function render_access_token()
{
include_once $this->pluginPath . '/views/help-scout-access-token.php';
}

protected function hasUpdatedOption()
{
return $this->updatedOption;
Expand Down
Loading