Skip to content

DTAB-84: Add Settings Page #2

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

Merged
merged 1 commit into from
May 14, 2025
Merged
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
Empty file removed CRM/Thinkific/.gitkeep
Empty file.
112 changes: 112 additions & 0 deletions CRM/Thinkific/Form/Settings.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php

use CRM_Thinkific_ExtensionUtil as E;
use CRM_Thinkific_SettingsManager as SettingsManager;
use Civi\Thinkific\Service\ApiClient;
use GuzzleHttp\Exception\BadResponseException;

/**
* Form controller class
*
* @see https://docs.civicrm.org/dev/en/latest/framework/quickform/
*/
class CRM_Thinkific_Form_Settings extends CRM_Core_Form {

public function buildQuickForm(): void {
CRM_Utils_System::setTitle(E::ts('Thinkfic LMS Settings'));
$this->add('password', SettingsManager::API_KEY, E::ts('Thinkfic Api Key'), NULL, TRUE);
$this->add('text', SettingsManager::SUBDOMAIN, E::ts('Thinkfic Subdomain'), NULL, TRUE);

$this->addButtons([
[
'type' => 'submit',
'name' => E::ts('Save'),
'isDefault' => TRUE,
],
]);

// export form elements
$this->assign('elementNames', $this->getRenderableElementNames());
parent::buildQuickForm();
}

public function postProcess(): void {
$values = $this->exportValues();

try {
$client = new ApiClient();
$headers = [
'X-Auth-API-Key' => $values[SettingsManager::API_KEY],
'X-Auth-Subdomain' => $values[SettingsManager::SUBDOMAIN],
'Content-Type' => 'application/json',
];
$client->request('GET', 'courses', $headers);
/** @var array<string, mixed> $result */
$result = civicrm_api3('setting', 'create', [
SettingsManager::API_KEY => $values[SettingsManager::API_KEY],
SettingsManager::SUBDOMAIN => $values[SettingsManager::SUBDOMAIN],
]);
if ($result['is_error'] == 0) {
CRM_Core_Session::singleton()->setStatus(E::ts('Connection success.'), E::ts('Thinkfic LMS Settings'), 'success');
}
}
catch (Throwable $e) {
$msg = 'An issue has occurred connecting to the Thinkific platform. Please contact your administrator.';
if ($e instanceof BadResponseException) {
$msg .= ' Error code: ' . $e->getResponse()->getStatusCode();
}
CRM_Core_Session::singleton()->setStatus(
$msg,
E::ts('Thinkfic LMS Settings'),
'error'
);
}

parent::postProcess();
}

/**
* Set defaults for form.
*
* @return array<string, mixed>
*
* @see CRM_Core_Form::setDefaultValues()
*/
public function setDefaultValues(): array {
$defaults = [];
$domainId = CRM_Core_Config::domainID();

/** @var array<string, array<int, array<string, mixed>>> $currentValues */
$currentValues = civicrm_api3('setting', 'get', ['return' => [SettingsManager::API_KEY, SettingsManager::SUBDOMAIN]]);

if (isset($currentValues['values'][$domainId])) {
foreach ($currentValues['values'][$domainId] as $name => $value) {
$defaults[$name] = $value;
}
}
return $defaults;
}

/**
* Get the fields/elements defined in this form.
*
* @return list<string>
*/
public function getRenderableElementNames(): array {
// The _elements list includes some items which should not be
// auto-rendered in the loop -- such as "qfKey" and "buttons". These
// items don't have labels. We'll identify renderable by filtering on
// the 'label'.
$elementNames = [];
/** @phpstan-ignore property.notFound */
foreach ($this->_elements as $element) {
/** @var HTML_QuickForm_element $element */
$label = $element->getLabel();
if (!empty($label)) {
$elementNames[] = $element->getName();
}
}
return $elementNames;
}

}
23 changes: 23 additions & 0 deletions CRM/Thinkific/SettingsManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php
/*
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC. All rights reserved. |
| |
| This work is published under the GNU AGPLv3 license with some |
| permitted exceptions and without any warranty. For full license |
| and copyright information, see https://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/

/**
* Settings manager class to manage Thinkific settings.
*/
class CRM_Thinkific_SettingsManager {

/**
* Constants for setting name
*/
const API_KEY = 'thinkific_api_key';
const SUBDOMAIN = 'thinkific_subdomain';

}
Empty file removed Civi/.gitkeep
Empty file.
48 changes: 48 additions & 0 deletions Civi/Thinkific/Service/ApiClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace Civi\Thinkific\Service;

use GuzzleHttp\Client;
use Psr\Http\Message\ResponseInterface;
use CRM_Thinkific_SettingsManager as SettingsManager;

class ApiClient {
private const API_URL = 'https://api.thinkific.com/api/public/v1/';

/**
* @var array
* @phpstan-ignore missingType.iterableValue
*/
private array $requestHeaders = [];

public function __construct() {
$domainId = \CRM_Core_Config::domainID();

/** @var array<string, array<int, array<string, mixed>>> $settings */
$settings = civicrm_api3('setting', 'get', ['return' => [SettingsManager::API_KEY, SettingsManager::SUBDOMAIN]]);
$this->requestHeaders = [
'X-Auth-API-Key' => $settings['values'][$domainId][SettingsManager::API_KEY],
'X-Auth-Subdomain' => $settings['values'][$domainId][SettingsManager::SUBDOMAIN],
'Content-Type' => 'application/json',
];
}

/**
* @param string $method
* @param string $url
* @param array<string,mixed> $headers
*
* @return \Psr\Http\Message\ResponseInterface
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function request(string $method, string $url, array $headers = []): ResponseInterface {
$client = new Client([
'headers' => array_merge($this->requestHeaders, $headers),
]);

$completeUrl = self::API_URL . $url;

return $client->request($method, $completeUrl);
}

}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## io.compuco.lmsd2lintegration
## io.compuco.thinkific

This extension integrates Thinkific LMS with civicrm.

Expand Down
1 change: 1 addition & 0 deletions info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@
<mixin>[email protected]</mixin>
<mixin>[email protected]</mixin>
<mixin>[email protected]</mixin>
<mixin>[email protected]</mixin>
</mixins>
</extension>
36 changes: 0 additions & 36 deletions mixin/[email protected]

This file was deleted.

51 changes: 0 additions & 51 deletions mixin/[email protected]

This file was deleted.

1 change: 1 addition & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ parameters:
- ../../../CRM
- ../../../api
- ../../../packages
- ../../../vendor
40 changes: 40 additions & 0 deletions settings/Thinkific.setting.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php
/*
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC. All rights reserved. |
| |
| This work is published under the GNU AGPLv3 license with some |
| permitted exceptions and without any warranty. For full license |
| and copyright information, see https://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/

use CRM_Thinkific_SettingsManager as SettingsManager;

/*
* Settings metadata file
*/
return [
'thinkific_api_key' => [
'name' => SettingsManager::API_KEY,
'title' => 'Thinkific Api Key',
'type' => 'String',
'html_type' => 'password',
'default' => '',
'is_domain' => 1,
'is_contact' => 0,
'description' => 'Api key from Thinkific',
'html_attributes' => [],
],
'thinkific_subdomain' => [
'name' => SettingsManager::SUBDOMAIN,
'title' => 'Thinkific Subdomain',
'type' => 'String',
'html_type' => 'text',
'default' => '',
'is_domain' => 1,
'is_contact' => 0,
'description' => 'Subdomain for Thinkific',
'html_attributes' => [],
],
];
Empty file removed templates/.gitkeep
Empty file.
16 changes: 16 additions & 0 deletions templates/CRM/Thinkific/Form/Settings.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{* HEADER *}
<div class="crm-block crm-form-block">
<div class="crm-submit-buttons">
{include file="CRM/common/formButtons.tpl" location="top"}
</div>
{foreach from=$elementNames item=elementName}
<div class="crm-section">
<div class="label">{$form.$elementName.label}</div>
<div class="content">{$form.$elementName.html}</div>
<div class="clear"></div>
</div>
{/foreach}
<div class="crm-submit-buttons">
{include file="CRM/common/formButtons.tpl" location="bottom"}
</div>
</div>
17 changes: 17 additions & 0 deletions thinkific.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,20 @@ function thinkific_civicrm_install(): void {
function thinkific_civicrm_enable(): void {
_thinkific_civix_civicrm_enable();
}

/**
* Implements hook_civicrm_navigationMenu().
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_navigationMenu
*/
function thinkific_civicrm_navigationMenu(&$menu) {
_thinkific_civix_insert_navigation_menu($menu, 'Administer/CiviEvent', array(
'label' => E::ts('Thinkific LMS Settings'),
'name' => 'thinkific_lms_settings',
'url' => 'civicrm/admin/setting/preferences/thinkific',
'permission' => 'administer CiviCRM',
'operator' => 'OR',
'separator' => 0,
));
_thinkific_civix_navigationMenu($menu);
}
Empty file removed xml/.gitkeep
Empty file.
9 changes: 9 additions & 0 deletions xml/Menu/thinkific.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0"?>
<menu>
<item>
<path>civicrm/admin/setting/preferences/thinkific</path>
<page_callback>CRM_Thinkific_Form_Settings</page_callback>
<title>Thinkific LMS Settings</title>
<access_arguments>administer CiviCRM</access_arguments>
</item>
</menu>