Staff report: CSP frame-ancestors + frame-mode + WebForm-mw Civi extension
App side: - Per-route CSP: /staff/report now sets frame-ancestors 'self' <CIVI_BASE_URL origin> and drops X-Frame-Options so the CiviCRM extension can iframe it. All other routes keep frame-ancestors 'none' + X-Frame-Options: DENY via a path-negation source. - Staff page recognises ?frame=1 and renders without SiteHeader/ SiteFooter so it fills the iframe cleanly. - StaffReportView posts its scrollHeight to the parent window via postMessage when framed; the Civi tab listens and auto-resizes the iframe (no nested scrollbar). Anchor strip drops its sticky positioning in frame mode since there's no internal scroll. CiviCRM extension (civi-extension/webform-mw/, key webform-mw): - info.xml + main hook file (webform_mw.php) implementing hook_civicrm_tabset to add an 'Engagement Report' tab to Organization contact-view pages. - CRM/WebformMw/Page/Tab.php + Smarty template render an iframe pointing at <WEBFORM_MW_APP_URL>/staff/report?org=<cid>&key=&frame=1, with a postMessage listener that validates event.origin against the configured app URL before resizing. - Config via PHP constants in civicrm.settings.php (WEBFORM_MW_APP_URL, WEBFORM_MW_STAFF_KEY) or matching env vars. Help banner shown when unconfigured. - README documents install, config, behaviour, security caveats.
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* WebForm-mw CiviCRM extension.
|
||||
*
|
||||
* Adds an "Engagement Report" tab to Organization contact-view pages that
|
||||
* embeds the WebForm-mw Next.js staff report via an iframe.
|
||||
*
|
||||
* Configuration (in civicrm.settings.php or as env vars on the Civi server):
|
||||
*
|
||||
* define('WEBFORM_MW_APP_URL', 'https://survey.fci.coop');
|
||||
* define('WEBFORM_MW_STAFF_KEY', '<the STAFF_REPORT_KEY shared with the app>');
|
||||
*
|
||||
* Both values must be set or the tab renders a help banner explaining what
|
||||
* to configure. See README.md.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolve the app URL from constant or env. Empty string when unset.
|
||||
*/
|
||||
function _webform_mw_app_url(): string {
|
||||
if (defined('WEBFORM_MW_APP_URL')) {
|
||||
return rtrim((string) constant('WEBFORM_MW_APP_URL'), '/');
|
||||
}
|
||||
$env = getenv('WEBFORM_MW_APP_URL');
|
||||
return is_string($env) && $env !== '' ? rtrim($env, '/') : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the staff secret from constant or env. Empty string when unset.
|
||||
*/
|
||||
function _webform_mw_staff_key(): string {
|
||||
if (defined('WEBFORM_MW_STAFF_KEY')) {
|
||||
return (string) constant('WEBFORM_MW_STAFF_KEY');
|
||||
}
|
||||
$env = getenv('WEBFORM_MW_STAFF_KEY');
|
||||
return is_string($env) ? $env : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_civicrm_tabset().
|
||||
*
|
||||
* Adds the Engagement Report tab to the Organization contact summary tabset.
|
||||
* Other contact types (Individual, Household) get no tab.
|
||||
*/
|
||||
function webform_mw_civicrm_tabset($tabsetName, &$tabs, $context) {
|
||||
if ($tabsetName !== 'civicrm/contact/view') {
|
||||
return;
|
||||
}
|
||||
$cid = $context['contact_id'] ?? NULL;
|
||||
if (!$cid) {
|
||||
return;
|
||||
}
|
||||
// Restrict to Organization contacts.
|
||||
$contactType = NULL;
|
||||
try {
|
||||
$contactType = civicrm_api3('Contact', 'getvalue', [
|
||||
'id' => (int) $cid,
|
||||
'return' => 'contact_type',
|
||||
]);
|
||||
}
|
||||
catch (\Throwable $e) {
|
||||
// Quietly skip — failing here should not break the contact page.
|
||||
return;
|
||||
}
|
||||
if ($contactType !== 'Organization') {
|
||||
return;
|
||||
}
|
||||
|
||||
$tabs[] = [
|
||||
'id' => 'engagement_report',
|
||||
'title' => ts('Engagement Report'),
|
||||
'weight' => 200,
|
||||
'count' => NULL,
|
||||
'icon' => 'crm-i fa-line-chart',
|
||||
'url' => CRM_Utils_System::url(
|
||||
'civicrm/contact/view/engagement-report',
|
||||
"reset=1&cid={$cid}&snippet=1"
|
||||
),
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user