Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class GlificClient {
public function __construct() {
$this->client = new Client();
$this->token = GlificHelper::getToken();
$this->url = rtrim(CIVICRM_GLIFIC_API_BASE_URL, '/') . '/api/';
$this->url = rtrim(\Civi::settings()->get('civiglific_api_base_url'), '/') . '/api/';
}
Comment on lines 19 to 23

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add validation for missing settings.

If civiglific_api_base_url is not configured in CiviCRM settings, get() returns null, causing rtrim() to produce an empty string. This results in a malformed URL (/api/) that will break all API calls.

🔎 Proposed fix with validation
 public function __construct() {
   $this->client = new Client();
   $this->token = GlificHelper::getToken();
-  $this->url = rtrim(\Civi::settings()->get('civiglific_api_base_url'), '/') . '/api/';
+  $baseUrl = \Civi::settings()->get('civiglific_api_base_url');
+  if (empty($baseUrl)) {
+    throw new \Exception('Civiglific API base URL is not configured. Please set civiglific_api_base_url in CiviCRM settings.');
+  }
+  $this->url = rtrim($baseUrl, '/') . '/api/';
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function __construct() {
$this->client = new Client();
$this->token = GlificHelper::getToken();
$this->url = rtrim(CIVICRM_GLIFIC_API_BASE_URL, '/') . '/api/';
$this->url = rtrim(\Civi::settings()->get('civiglific_api_base_url'), '/') . '/api/';
}
public function __construct() {
$this->client = new Client();
$this->token = GlificHelper::getToken();
$baseUrl = \Civi::settings()->get('civiglific_api_base_url');
if (empty($baseUrl)) {
throw new \Exception('Civiglific API base URL is not configured. Please set civiglific_api_base_url in CiviCRM settings.');
}
$this->url = rtrim($baseUrl, '/') . '/api/';
}


/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,53 +8,61 @@
*
*/
if (!class_exists('CRM\Civiglific\GlificHelper')) {
class GlificHelper {

/**
* Get the Glific API access token using phone and password.
*
* @return string|false Access token or FALSE on failure.
*/
public static function getToken() {
$url = rtrim(CIVICRM_GLIFIC_API_BASE_URL, '/') . '/api/v1/session';

$data = [
'user' => [
'phone' => CIVICRM_GLIFIC_PHONE,
'password' => CIVICRM_GLIFIC_PASSWORD,
],
];

try {
$client = new Client();
$response = $client->post($url, [
'headers' => ['Content-Type' => 'application/json'],
'json' => $data,
'http_errors' => FALSE,
]);

$json = json_decode($response->getBody(), TRUE);

if (!empty($json['data']['access_token'])) {
return $json['data']['access_token'];
class GlificHelper {

/**
* Get the Glific API access token using phone and password.
*
* @return string|false Access token or FALSE on failure.
*/
public static function getToken() {
$apiBaseUrl = \Civi::settings()->get('civiglific_api_base_url');
$phone = \Civi::settings()->get('civiglific_phone');
$password = \Civi::settings()->get('civiglific_password');
Comment on lines +23 to +25

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate settings exist and are non-empty before use.

The code retrieves settings without checking if they're configured. If any setting is NULL or empty (e.g., on first-time installation), subsequent operations will fail with unclear error messages. This degrades the user experience and makes debugging difficult.

🔎 Proposed validation logic
   public static function getToken() {
     $apiBaseUrl = \Civi::settings()->get('civiglific_api_base_url');
     $phone = \Civi::settings()->get('civiglific_phone');
     $password = \Civi::settings()->get('civiglific_password');
+
+    // Validate required settings are configured
+    if (empty($apiBaseUrl) || empty($phone) || empty($password)) {
+      \Civi::log()->error('Glific configuration error:', [
+        'error' => 'Required Glific settings are not configured. Please configure API credentials in CiviCRM settings.',
+        'missing' => [
+          'apiBaseUrl' => empty($apiBaseUrl),
+          'phone' => empty($phone),
+          'password' => empty($password),
+        ],
+      ]);
+      return FALSE;
+    }

     $url = rtrim($apiBaseUrl, '/') . '/api/v1/session';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$apiBaseUrl = \Civi::settings()->get('civiglific_api_base_url');
$phone = \Civi::settings()->get('civiglific_phone');
$password = \Civi::settings()->get('civiglific_password');
$apiBaseUrl = \Civi::settings()->get('civiglific_api_base_url');
$phone = \Civi::settings()->get('civiglific_phone');
$password = \Civi::settings()->get('civiglific_password');
// Validate required settings are configured
if (empty($apiBaseUrl) || empty($phone) || empty($password)) {
\Civi::log()->error('Glific configuration error:', [
'error' => 'Required Glific settings are not configured. Please configure API credentials in CiviCRM settings.',
'missing' => [
'apiBaseUrl' => empty($apiBaseUrl),
'phone' => empty($phone),
'password' => empty($password),
],
]);
return FALSE;
}
🤖 Prompt for AI Agents
In wp-content/civi-extensions/civiglific/CRM/Civiglific/GlificHelper.php around
lines 23–25, the three settings ($apiBaseUrl, $phone, $password) are fetched
without validation; update the code to trim and verify each value is set and
non-empty before use, and if any are missing/empty, return early or throw a
clear exception (or log an explicit error) that names the missing setting(s) so
callers get a helpful message instead of failing later; ensure validation is
centralized (e.g., a small helper or guard block) and avoid proceeding with API
calls when validation fails.


$url = rtrim($apiBaseUrl, '/') . '/api/v1/session';

$data = [
'user' => [
'phone' => $phone,
'password' => $password,
],
];

try {
$client = new Client();
$response = $client->post($url, [
'headers' => ['Content-Type' => 'application/json'],
'json' => $data,
'http_errors' => FALSE,
]);

$json = json_decode($response->getBody(), TRUE);

if (!empty($json['data']['access_token'])) {
return $json['data']['access_token'];
}
else {
\Civi::log()->error('Glific token error:', [
'error' => 'Invalid response from Glific API',
'response' => json_encode($json),
]);
return FALSE;
}
}
else {
\Civi::log()->error('Glific token error:', [
'error' => 'Invalid response from Glific API',
'response' => json_encode($json),
catch (\Exception $e) {
\Civi::log()->error('Glific token exception:', [
'error' => 'Glific token exception',
'exception' => $e->getMessage(),
]);
return FALSE;
}
}
catch (\Exception $e) {
\Civi::log()->error('Glific token exception:', [
'error' => 'Glific token exception',
'exception' => $e->getMessage(),
]);
return FALSE;
}
}

}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public function run() {
*
*/
protected function fetchGlificGroups($token) {
$url = rtrim(CIVICRM_GLIFIC_API_BASE_URL, '/') . '/api/';
$url = rtrim(\Civi::settings()->get('civiglific_api_base_url'), '/') . '/api/';
$client = new Client();

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,10 @@ public static function triggerMonetaryEmail($op, $entity, $id, $objectRef = NULL

$contributionPageName = $contributionData['contribution_page_id:name'] ?? '';
if ($contributionPageName === 'Team_5000') {
$templateId = CIVICRM_GLIFIC_TEMPLATE_ID_TEAM5000;
$templateId = \Civi::settings()->get('civiglific_template_id_team5000');
}
else {
$templateId = CIVICRM_GLIFIC_TEMPLATE_ID_DEFAULT;
$templateId = \Civi::settings()->get('civiglific_template_id_default');
}
Comment on lines 90 to 95

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Validate template ID settings.

If the template ID settings are not configured, get() returns null, causing the Glific sendMessage API call (line 240) to fail. The method should validate that template IDs exist before attempting to send messages.

🔎 Proposed fix with validation
 $contributionPageName = $contributionData['contribution_page_id:name'] ?? '';
 if ($contributionPageName === 'Team_5000') {
   $templateId = \Civi::settings()->get('civiglific_template_id_team5000');
 }
 else {
   $templateId = \Civi::settings()->get('civiglific_template_id_default');
 }
+
+if (empty($templateId)) {
+  \Civi::log()->error("[MonetaryReceiptService] Template ID not configured for page: {$contributionPageName}");
+  return NULL;
+}


$input = [
Expand Down Expand Up @@ -152,7 +152,7 @@ public static function triggerMonetaryEmail($op, $entity, $id, $objectRef = NULL

// Save PDF to persistent location.
$uploadBase = defined('WP_CONTENT_DIR') ? rtrim(WP_CONTENT_DIR, '/') : rtrim($_SERVER['DOCUMENT_ROOT'] ?? __DIR__, '/');
$saveDir = $uploadBase . CIVICRM_PERSIST_PDF_PATH;
$saveDir = $uploadBase . \Civi::settings()->get('civiglific_persist_pdf_path');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Ensure path settings include leading separators.

Path concatenation assumes that civiglific_persist_pdf_path starts with /. If the setting value lacks a leading separator, the resulting path will be malformed (e.g., /var/www/wp-contentuploads/pdfs/ instead of /var/www/wp-content/uploads/pdfs/). Consider normalizing the path or documenting the expected format in the settings UI.

🔎 Proposed fix with path normalization
 $uploadBase = defined('WP_CONTENT_DIR') ? rtrim(WP_CONTENT_DIR, '/') : rtrim($_SERVER['DOCUMENT_ROOT'] ?? __DIR__, '/');
 $persistPath = \Civi::settings()->get('civiglific_persist_pdf_path');
 if (empty($persistPath)) {
   throw new \Exception('PDF persist path not configured in settings');
 }
-$saveDir = $uploadBase . $persistPath;
+// Ensure path starts with separator
+$persistPath = '/' . ltrim($persistPath, '/');
+$saveDir = $uploadBase . $persistPath;

Apply similar logic for civiglific_saved_pdf_path at line 167.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In wp-content/civi-extensions/civiglific/Civi/MonetaryReceiptService.php around
line 155, the code concatenates $uploadBase and the civiglific_persist_pdf_path
setting without ensuring a leading directory separator; normalize the setting by
trimming whitespace and leading/trailing slashes and then prepend the directory
separator (or use DIRECTORY_SEPARATOR) when concatenating so the resulting path
is always valid; apply the same normalization procedure to
civiglific_saved_pdf_path at line 167.


if (!file_exists($saveDir)) {
mkdir($saveDir, 0755, TRUE);
Expand All @@ -164,7 +164,7 @@ public static function triggerMonetaryEmail($op, $entity, $id, $objectRef = NULL
}

$baseUrl = rtrim(\CRM_Core_Config::singleton()->userFrameworkBaseURL, '/');
$pdfUrl = $baseUrl . CIVICRM_SAVED_PDF_PATH . $fileNameForPdf;
$pdfUrl = $baseUrl . \Civi::settings()->get('civiglific_saved_pdf_path') . $fileNameForPdf;

$contributionContactId = $contributionData['contact_id'];

Expand Down
50 changes: 31 additions & 19 deletions wp-content/civi-extensions/civiglific/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Integrates Glific with CiviCRM, enabling you to send messages to users directly from CiviCRM and sync your group contacts between CiviCRM and Glific.

Latest releases can be found in the [CiviCRM extensions directory](https://lab.civicrm.org/dashboard/projects/personal)

## Documentation

### Installation
Expand All @@ -11,7 +12,7 @@ Latest releases can be found in the [CiviCRM extensions directory](https://lab.c
2. Put it in your CiviCRM extension directory
3. Go to `CiviCRM > Administer > System Settings > Extensions`
4. Find `civiglific` in the extension list and enable it
5. Optionally, you can also use `cv` command-line tool to perform step 3 and 4
5. Optionally, you can also use the `cv` command-line tool to perform steps 3 and 4

## Setup

Expand All @@ -21,12 +22,20 @@ There are two main setup configurations you can perform with this extension:

### Setup 1: Migrate CiviCRM Group Contacts to Glific Groups

1. **Add configuration details in your `settings.php` file** to connect Glific with CiviCRM:
```php
define('CIVICRM_GLIFIC_PHONE', 'XXXXXXXXXX');
define('CIVICRM_GLIFIC_PASSWORD', 'ABC');
define('CIVICRM_GLIFIC_API_BASE_URL', 'https://api.abs.com');
1. **Configure Glific credentials in CiviCRM Settings UI**

Go to:

```
Administer → System Settings → Civiglific Settings
Comment on lines 29 to +30

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Specify a language for the fenced code block.

The code block should include a language identifier for proper syntax highlighting.

-   ```
+   ```bash
    Administer → System Settings → Civiglific Settings
-   ```
+   ```
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

29-29: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In wp-content/civi-extensions/civiglific/README.md around lines 29 to 30, the
fenced code block lacks a language identifier; update the opening fence to
include the language (bash) so the block starts with ```bash and keep the
existing content and closing fence unchanged to enable proper syntax
highlighting.

```

Add the following values:

* Glific Phone Number
* Glific Password
* Glific API Base URL

2. **Add a navigation menu entry** in CiviCRM to access the Group Mapping screen. You can use the following URL:
```
civicrm/group-mapping
Expand All @@ -40,16 +49,21 @@ Civiglific.civicrm_glific_contact_sync_cron

### Setup 2: Send Receipts or Messages to Monetary Contributors

1. **Add the following configurations in your `settings.php` file:**
```php
// Glific Template IDs
define('CIVICRM_GLIFIC_TEMPLATE_ID_DEFAULT', 111111);
define('CIVICRM_GLIFIC_TEMPLATE_ID_TEAM5000', 111111);
1. **Configure Template IDs and PDF paths in CiviCRM Settings UI**

Go to:

// Persistent PDF storage
define('CIVICRM_PERSIST_PDF_PATH', '');
define('CIVICRM_SAVED_PDF_PATH', '');
```
Administer → System Settings → Civiglific Settings
Comment on lines 56 to +57

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Specify a language for the fenced code block.

The code block should include a language identifier for proper syntax highlighting.

-   ```
+   ```bash
    Administer → System Settings → Civiglific Settings
-   ```
+   ```
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

56-56: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In wp-content/civi-extensions/civiglific/README.md around lines 56 to 57 the
fenced code block lacks a language identifier; update the opening fence to
include the appropriate language (e.g., ```bash) so the snippet is
syntax-highlighted correctly, leaving the block contents and closing fence
unchanged.

```

Configure:

* Default Glific Template ID
* Team 5000 Template ID
* Persistent PDF Path (private storage path)
* Saved PDF Path (public access path)

2. **Create a custom field** (Alphanumeric, Radio Buttons type) for **Contributions** to identify which contributors should receive WhatsApp messages or receipts.
3. Create message template on glific end, fetch those id and added it to step 1.
4. **Send messages or receipts automatically:** When submitting a contribution form, simply select the configured field, the message or receipt will be automatically sent to the contributor’s WhatsApp number using the Glific integration.
Expand All @@ -68,13 +82,11 @@ Once the extension is installed and configured:
2. **Automated Message Sending**
- When a new contribution is recorded in CiviCRM and the configured custom field is selected, an automated WhatsApp message or receipt will be sent to the contributor using the linked Glific template.

3. **Template Management**
- Update your Glific template IDs in `settings.php` if you need to use different message templates for various campaigns or donor tiers.
- The system will automatically pick the correct template ID during message sending.

4. **PDF Receipts**
- If you’ve configured persistent PDF paths in your `settings.php`, a copy of the sent receipt PDF will be saved for each transaction..
### 4. PDF Receipts

* If persistent PDF paths are configured in **Civiglific Settings**,
a copy of the sent receipt PDF will be stored for each transaction.

## Maintainers

Expand Down
3 changes: 2 additions & 1 deletion wp-content/civi-extensions/civiglific/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@
</civix>
<mixins>
<mixin>mgd-php@1.0.0</mixin>
<mixin>setting-php@1.0.0</mixin>
<mixin>smarty@1.0.3</mixin>
<mixin>scan-classes@1.0.0</mixin>
<mixin>entity-types-php@2.0.0</mixin>
<mixin>setting-php@1</mixin>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove duplicate setting-php mixin declaration.

Line 42 already declares setting-php@1.0.0, making this declaration redundant. Having both version-specific and unversioned mixins may cause conflicts or unpredictable behavior in CiviCRM's mixin resolution.

🔎 Proposed fix
-    <mixin>setting-php@1</mixin>
     <mixin>setting-admin@1</mixin>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<mixin>setting-php@1</mixin>
<mixin>setting-admin@1</mixin>
🤖 Prompt for AI Agents
In wp-content/civi-extensions/civiglific/info.xml around line 46, remove the
redundant <mixin>setting-php@1</mixin> entry because line 42 already declares
setting-php@1.0.0; keep the versioned mixin (setting-php@1.0.0) and delete the
unversioned duplicate to avoid mixin resolution conflicts.

<mixin>setting-admin@1</mixin>
</mixins>
<upgrader>CRM_Civiglific_Upgrader</upgrader>
</extension>
13 changes: 13 additions & 0 deletions wp-content/civi-extensions/civiglific/settings/civiglific.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

/**
* @file
*/

return [
'civiglific' => [
'title' => ts('Civiglific Settings'),
'weight' => 10,
'tab' => 'System',
],
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

/**
* @file
*/


return [

'civiglific_phone' => [
'name' => 'civiglific_phone',
'type' => 'String',
'html_type' => 'text',
'title' => ts('Glific Phone Number'),
'description' => ts('Registered Glific WhatsApp phone number'),
'is_domain' => 1,
'is_contact' => 0,
'settings_pages' => ['civiglific' => ['weight' => 10]],
],

'civiglific_password' => [
'name' => 'civiglific_password',
'type' => 'String',
'html_type' => 'password',
'title' => ts('Glific Password'),
'description' => ts('Glific API password'),
'is_domain' => 1,
'is_contact' => 0,
'settings_pages' => ['civiglific' => ['weight' => 20]],
],
Comment on lines +21 to +30

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

CiviCRM settings encryption API credentials storage best practices

💡 Result:

Short best-practices for storing API credentials and other secrets used by CiviCRM

  • Use CiviCRM’s built-in encrypted-credentials feature (CIVICRM_CRED_KEYS) rather than storing plaintext in DB or settings. Put keys in civicrm.settings.php as documented. [1]

  • Follow CiviCRM’s cipher/key format and sizes (use AES-256 variants; prefer hkdf-sha256-encoded strong random keys). Configure multiple keys if you need rotation/roll‑forward. [1][2]

  • Rotate keys properly: add the new key first in CIVICRM_CRED_KEYS, run System.rotateKey (cv api4 System.rotateKey tag=CRED), then remove old keys after a grace period. Test rotation on staging first. [1]

  • Keep the encryption keys out of source control and limit filesystem access to civicrm.settings.php (tight file permissions, separate deploy/build artifacts from runtime secrets). [1][2]

  • Prefer a dedicated secret store (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, or an HSM) for managing high-value keys or for automated rotation; retrieve into the host environment at startup and inject into civicrm.settings.php or environment variables securely. Audit and RBAC-enable access. [3][4]

  • Minimize exposure: give each API credential the least privileges required, use per-integrator keys where possible (API‑key extension / contact-level keys), and avoid sharing long‑lived credentials between systems. [2][5]

  • Log and monitor access to secrets, keep secure backups of keys (offline/encrypted), and have an incident/revocation plan so you can quickly revoke and rekey if compromised. [3][4]

References

  1. CiviCRM System Administrator Guide — Secret keys (CIVICRM_CRED_KEYS, CIVICRM_SIGN_KEYS, CIVICRM_SITE_KEY).
  2. CiviCRM Developer Docs — Cryptography / CryptoRegistry & CryptoToken.
  3. AWS Secrets Manager / Well‑Architected guidance on secret storage and rotation.
  4. Microsoft Azure key vault best practices (secret protection, HSM, rotation).

🏁 Script executed:

# First, let's understand the file structure and see how this password setting is used
find . -type f -name "*.php" | grep -E "(civiglific|settings)" | head -20

Repository: ColoredCow/goonj

Length of output: 1448


🏁 Script executed:

# Look for how the civiglific_password setting is retrieved or used in the codebase
rg "civiglific_password" -A 3 -B 3

Repository: ColoredCow/goonj

Length of output: 1632


🏁 Script executed:

# Check if there are any encryption-related functions or utilities being used in this extension
rg "encrypt|decrypt|hash" wp-content/civi-extensions/civiglific/ || echo "No encryption utilities found in grep"

Repository: ColoredCow/goonj

Length of output: 97


🏁 Script executed:

# Read the full civiglific settings file to understand the complete context
cat -n wp-content/civi-extensions/civiglific/settings/civiglific.setting.php

Repository: ColoredCow/goonj

Length of output: 3188


🏁 Script executed:

# Check other extensions to see how they define password settings
rg "html_type.*password|type.*password" wp-content/civi-extensions/ -A 2 -B 2

Repository: ColoredCow/goonj

Length of output: 50374


🏁 Script executed:

# Look for any CiviCRM-specific encryption or credential handling in the extension
rg "CryptoRegistry|CryptoToken|credential|secret|encrypt" wp-content/civi-extensions/civiglific/ -i

Repository: ColoredCow/goonj

Length of output: 42


Store API password securely using CiviCRM's encryption.

The Glific API password is stored as plain text in the database. CiviCRM settings are stored unencrypted by default, exposing credentials to anyone with database access.

Use CiviCRM's built-in CIVICRM_CRED_KEYS encryption feature (AES-256) instead. Configure encryption keys in civicrm.settings.php and this setting will be automatically encrypted at rest.


'civiglific_api_base_url' => [
'name' => 'civiglific_api_base_url',
'type' => 'String',
'html_type' => 'text',
'title' => ts('Glific API Base URL'),
'description' => ts('Base URL for Glific API'),
'default' => 'https://api.goonj.glific.com',
'is_domain' => 1,
'is_contact' => 0,
'settings_pages' => ['civiglific' => ['weight' => 30]],
],

'civiglific_template_id_default' => [
'name' => 'civiglific_template_id_default',
'type' => 'Integer',
'html_type' => 'text',
'title' => ts('Default Template ID'),
'is_domain' => 1,
'settings_pages' => ['civiglific' => ['weight' => 40]],
],

'civiglific_template_id_team5000' => [
'name' => 'civiglific_template_id_team5000',
'type' => 'Integer',
'html_type' => 'text',
'title' => ts('Team 5000 Template ID'),
'is_domain' => 1,
'settings_pages' => ['civiglific' => ['weight' => 50]],
],

'civiglific_persist_pdf_path' => [
'name' => 'civiglific_persist_pdf_path',
'type' => 'String',
'html_type' => 'text',
'title' => ts('Persistent PDF Path (Private)'),
'description' => ts('Server path where contribution PDFs are stored persistently (private path)'),
'default' => '/uploads/civicrm/persist/contribute/contribution/',
'is_domain' => 1,
'is_contact' => 0,
'settings_pages' => ['civiglific' => ['weight' => 60]],
],

'civiglific_saved_pdf_path' => [
'name' => 'civiglific_saved_pdf_path',
'type' => 'String',
'html_type' => 'text',
'title' => ts('Saved PDF Path (Public)'),
'description' => ts('Public path where contribution PDFs are accessible'),
'default' => '/wp-content/uploads/civicrm/persist/contribute/contribution/',
'is_domain' => 1,
'is_contact' => 0,
'settings_pages' => ['civiglific' => ['weight' => 70]],
],

];