<?php
|
namespace JVBase\integrations;
|
|
use JVBase\meta\Form;
|
|
if (!defined('ABSPATH')) {
|
exit;
|
}
|
|
trait Admin {
|
use _Base;
|
protected bool $canTest = false;
|
//An array of AdminAction objects
|
protected array $actions = [];
|
protected array $instructions = [];
|
|
protected array $fields = [];
|
protected function initializeActions():void
|
{
|
$actions = [
|
[
|
'name' => 'Save Credentials',
|
'label' => 'Save Changes',
|
'icon' => 'floppy-disk',
|
'action' => [$this, 'saveCredentials']
|
]
|
];
|
if ($this->isSetUp()) {
|
$actions[] = [
|
'name' => 'Clear Credentials',
|
'label' => 'Clear Credentials',
|
'icon' => 'broom',
|
'action' => [$this, 'deleteCredentials']
|
];
|
}
|
if ($this->canTest) {
|
$actions[] = [
|
'name' => 'Test Connection',
|
'label' => 'Test Connection',
|
'icon' => 'plugs-connected',
|
'action' => [$this, 'testConnectionAction']
|
];
|
}
|
foreach ($actions as $a) {
|
$this->addAction($a['name'], $a['action'], $a['slug']??null, $a['icon']??null, $a['label']??null);
|
}
|
|
$this->defineActions();
|
}
|
|
abstract protected function defineActions():void;
|
|
public function addAction(string $name, callable $callback, ?string $slug = null, ?string $icon = null, ?string $label = null):void
|
{
|
$action = new AdminActions($name, $callback);
|
if (!is_null($slug)) {
|
$action->setSlug($slug);
|
}
|
if (!is_null($icon)) {
|
$action->setIcon($icon);
|
}
|
if (!is_null($label)) {
|
$action->setLabel($label);
|
}
|
$this->actions[$action->getSlug()] = $action;
|
}
|
/**
|
* @param string $action
|
* @param mixed|null $data
|
* @return array [
|
* 'success' => {bool},
|
* 'message' => {string} optional message,
|
* 'data' => {mixed} optional data to return
|
* ]
|
*/
|
public function processAction(string $action, mixed $data = null):array
|
{
|
if (!array_key_exists($action, $this->actions)) {
|
$this->logError('processAction', 'Attempted action not setup: '.print_r(['action' => $action, 'service' => $this->service_name], true));
|
return $this->response(false, 'Attempted action not set up');
|
}
|
$theAction = $this->actions[$action];
|
if (!$theAction->checkPermission()) {
|
$this->logError('processAction', 'User has no capabilities to perform this action: '.print_r(['action' => $action, 'service' => $this->service_name], true));
|
return $this->response(false, 'Insufficient permissions');
|
}
|
|
$result = $theAction->handleCallback($data);
|
if (is_wp_error($result)) {
|
$this->logError('processAction', $result->get_error_message());
|
return $this->response(false, $result->get_error_message());
|
}
|
|
return $result;
|
}
|
|
|
public function renderConnection(bool $return = false):string
|
{
|
if ($this->userID && !$this->userCanConnect($this->userID)) {
|
return '';
|
}
|
|
$credentials = $this->loadCredentials();
|
$isConnected = !empty($credentials);
|
|
$meta = '';
|
if ($isConnected) {
|
$updated = Auth::getInstance()->getLastUpdated($this->service_name, $this->userID);
|
$meta = [];
|
if ($updated) {
|
$meta[] = sprintf(
|
'<small>Last updated: %s ago</small>',
|
human_time_diff(strtotime($updated))
|
);
|
}
|
$tested = Auth::getInstance()->getLastTested($this->service_name, $this->userID);
|
if ($tested && $tested !== '0000-00-00 00:00:00') {
|
$meta[] = sprintf(
|
'<small>Last tested: %s ago</small>',
|
human_time_diff(strtotime($tested))
|
);
|
}
|
if (!empty($meta)) {
|
$meta = sprintf(
|
'<div class="meta">%s</div>',
|
implode('<br>', $meta)
|
);
|
}
|
}
|
|
$form = sprintf(
|
'<form id="%s" class="integration %s" data-service="%s" data-save="integrations">
|
<div class="header row x-btw">
|
<h3>%s</h3>
|
<div class="setup">
|
<span class="indicator">%s</span>
|
<span class="text">%sSet Up</span>
|
</div>
|
</div>
|
%s',
|
$this->service_name,
|
$isConnected ? 'connected' : 'disconnected',
|
esc_attr($this->service_name),
|
esc_html($this->title),
|
$isConnected ? '●' : '○',
|
$isConnected ? '' : 'Not ',
|
$meta,
|
);
|
|
if (!$this->isSetUp() && !empty($this->instructions)) {
|
$form .= sprintf(
|
'<details%s>
|
<summary>Instructions</summary>
|
<ol>%s</ol>
|
</details>',
|
$isConnected ? '' : ' open',
|
implode('',array_map(function($el) {
|
if (is_array($el)) {
|
$bits = implode('', array_map(function ($b) {
|
return sprintf('<li>%s</li>', $b);
|
}, $el));
|
return sprintf('<ol>%s</ol>', $bits);
|
}
|
return sprintf('<li>%s</li>', $el);
|
}, $this->instructions))
|
);
|
}
|
|
$setupFields = array_filter($this->fields, function($field) {
|
return $field->isSetupField();
|
});
|
|
if ((method_exists($this, 'userCanConnect') && $this->userCanConnect(get_current_user_id())) || current_user_can('manage_options')) {
|
$form .= sprintf(
|
'<details class="initial-setup"%s>
|
<summary>Setup</summary>
|
%s
|
%s
|
</details>',
|
$isConnected ? '' : ' open',
|
$this->outputFields($setupFields),
|
method_exists($this, 'renderWebhookUrl') ? $this->renderWebhookUrl() : ''
|
);
|
}
|
|
if (method_exists($this, 'renderOAuthConnectionForm')) {
|
$form .= $this->renderOAuthConnectionForm();
|
}
|
|
$advancedFields = array_filter($this->fields, function($field) {
|
return $field->isAdvancedField();
|
});
|
|
if (!empty($advancedFields) && $this->isSetUp()) {
|
$form .= sprintf(
|
'<details class="advanced">
|
<summary>Advanced Options</summary>
|
%s
|
</details>',
|
$this->outputFields($advancedFields),
|
);
|
}
|
|
$optionalFields = array_filter($this->fields, function($field) {
|
return $field->isAdditionalField();
|
});
|
|
if ($this->isSetUp() && (!empty($optionalFields) || $this->hasExtraOptions())) {
|
$form .= '<div class="integration-content">';
|
if (!empty($optionalFields)) {
|
$form .= sprintf(
|
'<details class="advanced"%s>
|
<summary>Optional Settings</summary>%s</details>',
|
$this->isSetUp() ? ' open' : '',
|
$this->outputFields($optionalFields)
|
);
|
}
|
if ($this->hasExtraOptions()) {
|
$form .= sprintf(
|
'<a href="%s" class="btn">More Settings</a>',
|
$this->determineOptionPage()
|
);
|
}
|
$form .= '</div>';
|
}
|
|
$form .= $this->outputActionButtons();
|
$form .= '</form>';
|
|
if (!$return) {
|
echo $form;
|
}
|
|
return $form;
|
}
|
protected function outputActionButtons():string
|
{
|
return sprintf('<div class="actions row x-btw wrap">%s</div>',implode('', array_map(function($action) {
|
$label = '';
|
if (!empty ($action->getIcon())) {
|
$label .= $this->outputIcon($action->getIcon());
|
}
|
$label .= $action->getLabel();
|
return sprintf(
|
'<button type="button" data-action="%s"%s%s>%s</button>',
|
$action->getSlug(),
|
$action->getName(),
|
empty($action->getConfirm()) ? '' : ' data-confirm="'.$action->getConfirm().'"',
|
$label
|
);
|
}, array_filter($this->actions, function ($action) {
|
return $action->checkPermission();
|
}))));
|
}
|
|
protected function outputFields(array $fields):string
|
{
|
$credentials = $this->loadCredentials();
|
$fields = array_filter($fields, function ($field) {
|
return $field->checkPermission();
|
});
|
return implode('', array_map(function($f) use ($credentials) {
|
$value = array_key_exists($f->name, $credentials) ? $credentials[$f->name] : '';
|
$config = $f->getConfig();
|
if (!empty($value)) {
|
$config['readonly'] = true;
|
}
|
if (!empty($value) && $f->isHideStored()) {
|
return sprintf(
|
'<span class="label">%s</span><code>%s</code>',
|
$config['label'],
|
preg_replace('/.(?=.{4})/', '*', $value)
|
);
|
}
|
$config['base'] = $this->service_name.'_';
|
return Form::render($f->name, $value, $f->getConfig());
|
}, $fields));
|
}
|
|
public function addField(string $name, string $label, string $type, callable|string|bool $permission):Field
|
{
|
$field = new Field($name, $label, $type, $permission);
|
$this->fields[$name] = $field;
|
return $field;
|
}
|
public function setFieldRequired(string $name):bool
|
{
|
if (!array_key_exists($name, $this->fields)) {
|
return false;
|
}
|
$this->fields[$name]->setRequired();
|
return true;
|
}
|
public function updateField(string $name, Field $field):void
|
{
|
$this->fields[$name] = $field;
|
}
|
|
public function testConnectionAction(?array $data = null):array
|
{
|
$success = $this->testConnection();
|
$message = $success ? 'Successfully tested connection' : 'Could not test connection';
|
return $this->response($success, $message);
|
}
|
|
}
|