mirror of
https://github.com/opensourcepos/opensourcepos.git
synced 2026-07-25 22:27:05 -04:00
Fix bugs
- Add missing `MailchimpPlugin.` prefix to lang() calls. - Do not subscribe customer if consent is not true. - Escape output in tabular_helper.php - Removed testConnection() as unneeded code - Fix activity count logic - Whitelist Sort Column Headers for Plugins.php - Store encrypted API key as base64 instead of raw binary to prevent truncation - Rollback on batchSave partial failure. - Remove dead code. - Disable plugin before uninstalling it. - Fix getPluginSettings() internal key leak - Add action column to plugin headers function - Automatically add grant to all admins in case person_id 1 is not active Signed-off-by: objec <objecttothis@gmail.com>
This commit is contained in:
@@ -26,7 +26,7 @@ class Plugins extends Secure_Controller
|
||||
$search = strtolower($this->request->getGet('search') ?? '');
|
||||
$limit = (int)($this->request->getGet('limit') ?? 0);
|
||||
$offset = (int)($this->request->getGet('offset') ?? 0);
|
||||
$sort = $this->request->getGet('sort', FILTER_SANITIZE_FULL_SPECIAL_CHARS) ?? 'name';
|
||||
$sort = $this->sanitizeSortColumn(plugin_headers(), $this->request->getGet('sort', FILTER_SANITIZE_FULL_SPECIAL_CHARS), 'name');
|
||||
$order = strtolower($this->request->getGet('order', FILTER_SANITIZE_FULL_SPECIAL_CHARS) ?? 'asc');
|
||||
|
||||
$pluginData = $this->buildPluginDataArray();
|
||||
|
||||
@@ -12,5 +12,5 @@ INSERT IGNORE INTO `ospos_modules` (`name_lang_key`, `desc_lang_key`, `sort`, `m
|
||||
INSERT IGNORE INTO `ospos_permissions` (`permission_id`, `module_id`) VALUES
|
||||
('plugins', 'plugins');
|
||||
|
||||
INSERT IGNORE INTO `ospos_grants` (`permission_id`, `person_id`, `menu_group`) VALUES
|
||||
('plugins', 1, 'office');
|
||||
INSERT IGNORE INTO `ospos_grants` (`permission_id`, `person_id`, `menu_group`)
|
||||
SELECT 'plugins', `person_id`, 'office' FROM `ospos_grants` WHERE `permission_id` = 'config';
|
||||
|
||||
@@ -940,6 +940,7 @@ function plugin_headers(): array
|
||||
['description' => lang('Plugins.description')],
|
||||
['version' => lang('Plugins.version'), 'escape' => false],
|
||||
['status' => lang('Plugins.status'), 'escape' => false],
|
||||
['actions' => lang('Plugins.actions'), 'escape' => false],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -970,7 +971,7 @@ function get_plugin_data_row(array $plugin): array
|
||||
return [
|
||||
'plugin_id' => $pluginId,
|
||||
'name' => '<strong>' . esc($plugin['name']) . '</strong><br><small class="text-muted">' . esc($pluginId) . '</small>',
|
||||
'description' => $plugin['description'],
|
||||
'description' => esc($plugin['description']),
|
||||
'version' => '<span class="label label-default">' . esc($plugin['version']) . '</span>',
|
||||
'status' => $statusHtml,
|
||||
'edit' => $editHtml,
|
||||
|
||||
@@ -166,6 +166,8 @@ class PluginManager
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->disablePlugin($pluginId);
|
||||
|
||||
if (!$plugin->uninstall()) {
|
||||
log_message('error', "Failed to uninstall plugin: {$pluginId}");
|
||||
return false;
|
||||
|
||||
@@ -49,7 +49,8 @@ class PluginConfig extends Model
|
||||
public function getPluginSettings(string $pluginId): array
|
||||
{
|
||||
$builder = $this->db->table('plugin_config');
|
||||
$builder->like('key', $pluginId . '_', 'after');
|
||||
$builder->like('key', $pluginId . '_', 'after')
|
||||
->notLike('key', $pluginId . '__', 'after');
|
||||
$query = $builder->get();
|
||||
|
||||
$settings = [];
|
||||
@@ -79,17 +80,17 @@ class PluginConfig extends Model
|
||||
|
||||
public function batchSave(array $data): bool
|
||||
{
|
||||
$success = true;
|
||||
|
||||
$this->db->transStart();
|
||||
$this->db->transBegin();
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
$success &= $this->setValue($key, $value);
|
||||
if (!$this->setValue($key, $value)) {
|
||||
$this->db->transRollback();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->transComplete();
|
||||
|
||||
return $success && $this->db->transStatus();
|
||||
$this->db->transCommit();
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getAll(): array
|
||||
|
||||
@@ -303,33 +303,30 @@ class MailchimpLibrary
|
||||
$customerActivities = $this->getMemberActivity($listId, $customerData->email);
|
||||
if ($customerActivities !== false) {
|
||||
if (array_key_exists('activity', $customerActivities)) {
|
||||
$sent = 0;
|
||||
$open = 0;
|
||||
$unopen = 0;
|
||||
$click = 0;
|
||||
$total = 0;
|
||||
$lastOpen = '';
|
||||
|
||||
foreach ($customerActivities['activity'] as $activity) {
|
||||
if ($activity['action'] == 'sent') {
|
||||
++$unopen;
|
||||
} elseif ($activity['action'] == 'open') {
|
||||
if ($activity['action'] === 'sent') {
|
||||
++$sent;
|
||||
} elseif ($activity['action'] === 'open') {
|
||||
if (empty($lastOpen)) {
|
||||
$lastOpen = substr($activity['timestamp'], 0, 10);
|
||||
}
|
||||
++$open;
|
||||
} elseif ($activity['action'] == 'click') {
|
||||
} elseif ($activity['action'] === 'click') {
|
||||
if (empty($lastOpen)) {
|
||||
$lastOpen = substr($activity['timestamp'], 0, 10);
|
||||
}
|
||||
++$click;
|
||||
}
|
||||
|
||||
++$total;
|
||||
}
|
||||
|
||||
$mailchimpData['mailchimpActivity']['total'] = $total;
|
||||
$mailchimpData['mailchimpActivity']['total'] = $sent;
|
||||
$mailchimpData['mailchimpActivity']['open'] = $open;
|
||||
$mailchimpData['mailchimpActivity']['unopen'] = $unopen;
|
||||
$mailchimpData['mailchimpActivity']['unopen'] = max(0, $sent - $open);
|
||||
$mailchimpData['mailchimpActivity']['click'] = $click;
|
||||
$mailchimpData['mailchimpActivity']['last_open'] = $lastOpen;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,12 @@ class MailchimpPlugin extends BasePlugin
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->mailchimpLibrary = new MailchimpLibrary($this->getSettings());
|
||||
$apiKey = $this->decryptApiKey($this->getSetting('api_key', ''));
|
||||
$this->mailchimpLibrary = new MailchimpLibrary([
|
||||
'api_key' => $apiKey,
|
||||
'list_id' => $this->getSetting('list_id', ''),
|
||||
'sync_on_save' => $this->getSetting('sync_on_save', '1'),
|
||||
]);
|
||||
log_message('debug', 'MailchimpPlugin initialized');
|
||||
}
|
||||
|
||||
@@ -80,17 +85,7 @@ class MailchimpPlugin extends BasePlugin
|
||||
|
||||
public function getSettings(): array
|
||||
{
|
||||
$encryptedKey = $this->getSetting('api_key', '');
|
||||
$apiKey = '';
|
||||
|
||||
if (!empty($encryptedKey)) {
|
||||
try {
|
||||
$apiKey = Services::encrypter()->decrypt($encryptedKey);
|
||||
} catch (\Exception $e) {
|
||||
// Key stored as plaintext (pre-encryption migration) — use as-is
|
||||
$apiKey = $encryptedKey;
|
||||
}
|
||||
}
|
||||
$apiKey = $this->decryptApiKey($this->getSetting('api_key', ''));
|
||||
|
||||
return [
|
||||
'api_key' => $apiKey,
|
||||
@@ -101,6 +96,22 @@ class MailchimpPlugin extends BasePlugin
|
||||
];
|
||||
}
|
||||
|
||||
private function decryptApiKey(string $encryptedKey): string
|
||||
{
|
||||
if (empty($encryptedKey)) {
|
||||
return '';
|
||||
}
|
||||
try {
|
||||
$decoded = base64_decode($encryptedKey, true);
|
||||
if ($decoded !== false) {
|
||||
return Services::encrypter()->decrypt($decoded);
|
||||
}
|
||||
} catch (\Exception) {
|
||||
// Legacy plaintext or old binary-encrypted key — fall through
|
||||
}
|
||||
return $encryptedKey;
|
||||
}
|
||||
|
||||
private function getFormattedLists(string $apiKey): array
|
||||
{
|
||||
if (empty($apiKey)) {
|
||||
@@ -127,7 +138,7 @@ class MailchimpPlugin extends BasePlugin
|
||||
if (array_key_exists('api_key', $settings)) {
|
||||
$rawKey = (string)$settings['api_key'];
|
||||
$normalized['api_key'] = !empty($rawKey)
|
||||
? Services::encrypter()->encrypt($rawKey)
|
||||
? base64_encode(Services::encrypter()->encrypt($rawKey))
|
||||
: '';
|
||||
}
|
||||
|
||||
@@ -186,7 +197,10 @@ class MailchimpPlugin extends BasePlugin
|
||||
|
||||
log_message('debug', "Customer saved event received for ID: {$customerData['person_id']}");
|
||||
|
||||
$statusInt = (int)(($postData ?? [])['status'] ?? SubscriptionStatus::SUBSCRIBED->value);
|
||||
$statusInt = !empty($customerData['consent'])
|
||||
? (int)(($postData ?? [])['status'] ?? SubscriptionStatus::SUBSCRIBED->value)
|
||||
: SubscriptionStatus::UNSUBSCRIBED->value;
|
||||
|
||||
$statusEnum = SubscriptionStatus::tryFrom($statusInt) ?? SubscriptionStatus::SUBSCRIBED;
|
||||
$subscriptionStatus = strtolower($statusEnum->name);
|
||||
|
||||
@@ -214,27 +228,4 @@ class MailchimpPlugin extends BasePlugin
|
||||
{
|
||||
return $this->getSetting('sync_on_save', '1') === '1';
|
||||
}
|
||||
|
||||
|
||||
public function testConnection(): array
|
||||
{
|
||||
$apiKey = $this->getSetting('api_key');
|
||||
|
||||
if (empty($apiKey)) {
|
||||
return ['success' => false, 'message' => lang('api_key_required')];
|
||||
}
|
||||
|
||||
$result = $this->mailchimpLibrary->getLists();
|
||||
|
||||
if ($result && isset($result['lists'])) {
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => lang('key_successfully'),
|
||||
'lists' => $result['lists']
|
||||
];
|
||||
}
|
||||
|
||||
return ['success' => false, 'message' => lang('key_unsuccessfully')];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user