Skip to content
Snippets Groups Projects
Commit 3f6c7354 authored by mattwire's avatar mattwire
Browse files

Move menu entry to managed entity

parent 6f3763e4
No related branches found
No related tags found
1 merge request!241Stripe Import extension
Package: com.drastikbydesign.stripe
Copyright (C) 2022, Matthew Wire (MJW Consulting) <mjw@mjwconsult.co.uk>
Copyright (C) 2024, Matthew Wire (MJW Consulting) <mjw@mjwconsult.co.uk>
Licensed under the GNU Affero Public License 3.0 (below).
-------------------------------------------------------------------------------
......
......@@ -44,6 +44,7 @@
<mixin>setting-php@1.0.0</mixin>
<mixin>smarty-v2@1.0.1</mixin>
<mixin>entity-types-php@1.0.0</mixin>
<mixin>setting-admin@1.0.1</mixin>
</mixins>
<upgrader>CRM_Stripe_Upgrader</upgrader>
</extension>
<?php
use CRM_Stripe_ExtensionUtil as E;
return [
[
'name' => 'stripe_settings',
'entity' => 'Navigation',
'cleanup' => 'always',
'update' => 'unmodified',
'params' => [
'version' => 4,
'values' => [
'label' => E::ts('Stripe Settings'),
'name' => 'stripe_settings',
'url' => 'civicrm/admin/setting/stripe',
'permission' => 'administer Stripe',
'permission_operator' => 'OR',
'parent_id.name' => 'CiviContribute',
'is_active' => TRUE,
'has_separator' => 0,
'weight' => 90,
],
'match' => ['name'],
],
],
];
<?php
/**
* The "setting-admin" mixin defines a standard idiom for managing extension settings:
*
* 1. Create a permission "administer {myext}" ("Administer {My Extension}").
* 2. Create a page "civicrm/admin/setting/{myext}" (via `CRM_Admin_Form_Generic`)
* 3. Assign all settings from "{myext}" to appear on the page.
* 4. Create a link "Administer > System Settings" to "{My Extension} Settings"
*
* (The values of "{myext}" and "{My Extension}" come from info.xml's `<file>` and `<name>`.)
*
* If you don't like the defaults, then there are a few override points:
*
* - If you manually create permission "administer {myext}", then your label/description takes precedence.
* - If you manually register route "civicrm/admin/setting/{myext}", then your definition takes precedence.
* - If you manually configure a setting with `settings_page`, then that setting will move to the other page.
* (To make a hidden setting, specify `settings_page => []`.)
* - If you manually add "civicrm/admin/setting/{myext}" to the menu, then your link takes precedence.
*
* Additionally, there is experimental support for overrides in info.xml. (Respected by v1.0.0 but not guaranteed future.)
*
* <civix><setting-page-title>My Custom Title</setting-page-title></civix>
*
* #####################################################################
* NOTE: setting-admin@1.0 is a civix-only backport revised for broader compatibility. Differences:
*
* - Don't register hooks with the `&` prefix.
* - Don't call `_ts()`. Use `$ts()` trick ourselves.
* - Omit some type-hints.
* - Use separate namespace for EVERY PATCH-VERSION. Prevent conflicts when using polyfill-based loading.
* - Downstream cannot rely on namespace-names/class-names. That's fine. These particular classes are internal helpers.
*
* The mainline release is setting-admin@1.1+.
* #####################################################################
*
* @mixinName setting-admin
* @mixinVersion 1.0.1
* @since 5.68
*/
namespace Civi\Mixin\SettingAdminV1_0_1;
use Civi;
class About {
/**
* @var \CRM_Extension_MixInfo
*/
private $mixInfo;
/**
* @var \CRM_Extension_Info
*/
private $info;
/**
* @param \CRM_Extension_MixInfo $mixInfo
*/
public static function instance($mixInfo): About {
$about = new About();
$about->mixInfo = $mixInfo;
$about->info = \CRM_Extension_System::singleton()->getMapper()->keyToInfo($mixInfo->longName);
return $about;
}
public function getPath(): string {
return 'civicrm/admin/setting/' . $this->mixInfo->shortName;
}
public function getPerm(): string {
return 'administer ' . $this->mixInfo->shortName;
}
public function getLabel(): string {
return $this->info->label ? \ts($this->info->label, ['domain' => $this->info->key]) : $this->info->key;
}
public function getPageTitle(): string {
// Changing the title any other way is slightly annoying because you have to override both route+nav.
// It might be nice if one (route or menu) reliably inherited its title from the other...
if (!empty($this->info->civix['setting-page-title'])) {
return $this->info->civix['setting-page-title'];
// Could call _ts(..., [domain=>...]), but translation appears to happen at another level,
// and double-translation might confuse multilingual.
}
return ts('%1 Settings', [1 => $this->getLabel()]);
}
public function getRoute(): array {
return [
'title' => $this->getPageTitle(),
'page_callback' => 'CRM_Admin_Form_Generic',
'access_arguments' => [['administer CiviCRM', $this->getPerm()], 'or'],
'adminGroup' => 'System Settings',
'desc' => \ts($this->info->description ?: ''),
];
}
public function getNavigation(): array {
return [
'label' => $this->getPageTitle(),
'name' => sprintf('%s_setting_admin', $this->mixInfo->shortName),
'url' => $this->getPath() . '?reset=1',
// 'icon' => 'crm-i fa-wrench', // None of the other "System Settings" have icons, so we don't.
// 'permission' => ['administer CiviCRM', $this->getPerm()],
'permission' => "administer CiviCRM,{$this->getPerm()}",
'permission_operator' => 'OR',
];
}
}
class Nav {
/**
* Visit all items in the nav-tree.
*
* @param array $items
* @param callable $callback
* function(array &$item): mixed
* To short-circuit execution, the callback should return a non-null value.
* @return string|null
* Return NULL by default. If the walk was short-circuited, then return that value.
*/
public static function walk(&$items, callable $callback) {
foreach ($items as &$item) {
$result = $callback($item);
if ($result !== NULL) {
return $result;
}
if (!empty($item['child'])) {
$result = static::walk($item['child'], $callback);
if ($result !== NULL) {
return $result;
}
}
}
return NULL;
}
}
/**
* @param \CRM_Extension_MixInfo $mixInfo
* @param \CRM_Extension_BootCache $bootCache
*/
return function ($mixInfo, $bootCache) {
// Register the setting page ("civicrm/admin/setting/{myext}").
Civi::dispatcher()->addListener('hook_civicrm_alterMenu', function ($e) use ($mixInfo) {
if (!$mixInfo->isActive()) {
return;
}
$about = About::instance($mixInfo);
if (!isset($e->items[$about->getPath()])) {
$e->items[$about->getPath()] = $about->getRoute();
}
}, -1000);
// Define a permission "administer {myext}"
Civi::dispatcher()->addListener('hook_civicrm_permission', function ($e) use ($mixInfo) {
if (!$mixInfo->isActive()) {
return;
}
$about = About::instance($mixInfo);
$perm = 'administer ' . $mixInfo->shortName;
if (!isset($e->permissions[$perm])) {
$e->permissions[$perm] = ts('%1: Administer settings', [1 => $about->getLabel()]);
}
}, -1000);
// Any settings with "group=={myext}" should be added to our setting page (unless overridden).
// By default, 'weight' is based on the order-of-declaration (spaced out with increments of 10).
Civi::dispatcher()->addListener('hook_civicrm_alterSettingsMetaData', function($e) use ($mixInfo) {
if (!$mixInfo->isActive()) {
return;
}
$weight = 1000;
$weightInterval = 10;
foreach ($e->settingsMetaData as &$setting) {
if (($setting['group'] ?? '') === $mixInfo->shortName) {
if (!array_key_exists('settings_pages', $setting)) {
$setting['settings_pages'][$mixInfo->shortName] = [
'weight' => $weight,
];
$weight += $weightInterval;
}
}
}
}, -1000);
// Add navigation-item ('civicrm/admin/setting/{myext}') unless you've already done so.
Civi::dispatcher()->addListener('hook_civicrm_navigationMenu', function ($e) use ($mixInfo) {
if (!$mixInfo->isActive()) {
return;
}
$about = About::instance($mixInfo);
$newItem = $about->getNavigation() + ['active' => 1];
// Skip if we're already in the menu. (Ignore optional suffix `?reset=1`)
$found = Nav::walk($e->menu, function(&$item) use ($about) {
if (!isset($item['attributes']['url'])) {
return NULL;
}
return strpos($item['attributes']['url'], $about->getPath()) === 0 ? 'found' : NULL;
});
if ($found) {
return;
}
Nav::walk($e->menu, function(&$item) use ($newItem) {
if ($item['attributes']['name'] === 'System Settings') {
$item['child'][] = ['attributes' => $newItem];
return 'done';
}
});
}, -1000);
};
......@@ -78,21 +78,6 @@ function stripe_civicrm_check(&$messages) {
$messages = $checks->checkRequirements();
}
/**
* Implements hook_civicrm_navigationMenu().
*/
function stripe_civicrm_navigationMenu(&$menu) {
_stripe_civix_insert_navigation_menu($menu, 'Administer/CiviContribute', [
'label' => E::ts('Stripe Settings'),
'name' => 'stripe_settings',
'url' => 'civicrm/admin/setting/stripe',
'permission' => 'administer CiviCRM',
'operator' => 'OR',
'separator' => 0,
]);
_stripe_civix_navigationMenu($menu);
}
/**
* Implements hook_civicrm_alterLogTables().
*
......
......@@ -6,10 +6,4 @@
<title>Check and Fix Stripe Webhooks</title>
<access_arguments>access CiviCRM</access_arguments>
</item>
<item>
<path>civicrm/admin/setting/stripe</path>
<title>Stripe Settings</title>
<page_callback>CRM_Admin_Form_Generic</page_callback>
<access_arguments>administer CiviCRM</access_arguments>
</item>
</menu>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment