'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); error_log('Action result: '.print_r($result, true)); 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( 'Last updated: %s ago', 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( 'Last tested: %s ago', human_time_diff(strtotime($tested)) ); } if (!empty($meta)) { $meta = sprintf( '
%s
', implode('
', $meta) ); } } $form = sprintf( '

%s

%s %sSet Up
%s', $this->service_name, $isConnected ? 'connected' : 'disconnected', esc_attr($this->service_name), esc_html($this->title), $isConnected ? '●' : '○', $isConnected ? '' : 'Not ', $meta, ); if (!empty($this->instructions)) { $form .= sprintf( ' Instructions
    %s
', $this->isSetUp() ? '' : ' open', implode('',array_map(function($el) { if (is_array($el)) { $bits = implode('', array_map(function ($b) { return sprintf('
  • %s
  • ', $b); }, $el)); return sprintf('
      %s
    ', $bits); } return sprintf('
  • %s
  • ', $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( '
    Setup %s %s
    ', $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( '
    Advanced Options %s
    ', $this->outputFields($advancedFields), ); } $optionalFields = array_filter($this->fields, function($field) { return $field->isAdditionalField(); }); if ($this->isSetUp() && (!empty($optionalFields) || $this->hasExtraOptions())) { $form .= '
    '; if (!empty($optionalFields)) { $form .= sprintf( '
    Optional Settings%s
    ', $this->isSetUp() ? ' open' : '', $this->outputFields($optionalFields) ); } if ($this->hasExtraOptions()) { $form .= sprintf( 'More Settings', $this->determineOptionPage() ); } $form .= '
    '; } $form .= $this->outputActionButtons(); $form .= ''; if (!$return) { echo $form; } return $form; } protected function outputActionButtons():string { return sprintf('
    %s
    ',implode('', array_map(function($action) { $label = ''; if (!empty ($action->getIcon())) { $label .= $this->outputIcon($action->getIcon()); } $label .= $action->getLabel(); return sprintf( '', $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( '%s%s', $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); } }