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,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Engagement Report tab page.
|
||||
*
|
||||
* Loaded as an AJAX snippet by the contact-view tabset (snippet=1). The
|
||||
* template emits a single iframe pointing at the WebForm-mw staff report
|
||||
* plus a small postMessage listener that auto-sizes the iframe to the
|
||||
* report's content height.
|
||||
*/
|
||||
class CRM_WebformMw_Page_Tab extends CRM_Core_Page {
|
||||
|
||||
public function run() {
|
||||
$cid = CRM_Utils_Request::retrieve('cid', 'Positive', $this, TRUE);
|
||||
|
||||
$appUrl = _webform_mw_app_url();
|
||||
$key = _webform_mw_staff_key();
|
||||
$configured = ($appUrl !== '' && $key !== '');
|
||||
|
||||
if ($configured) {
|
||||
$src = $appUrl . '/staff/report'
|
||||
. '?org=' . urlencode((string) $cid)
|
||||
. '&key=' . urlencode($key)
|
||||
. '&frame=1';
|
||||
$this->assign('iframeSrc', $src);
|
||||
// Expose just the app origin so the parent-side postMessage
|
||||
// listener can validate event.origin without leaking the secret.
|
||||
$this->assign('appOrigin', parse_url($appUrl, PHP_URL_SCHEME)
|
||||
. '://' . parse_url($appUrl, PHP_URL_HOST));
|
||||
}
|
||||
$this->assign('configured', $configured);
|
||||
|
||||
parent::run();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user