Repository navigation
Embed configurable Freshdesk feedback widget - #342
Merged
Merged
Conversation
Contributor
|
@A-Souhei conflicts 😢 |
Add the in-page Freshworks/Freshdesk feedback widget, matching the embed Avenir uses on its other Spectrum apps. It is gated on a widget id read from CKAN_UNAIDS_FRESHDESK_WIDGET_ID (env) or the ckanext.unaids.freshdesk_widget_id config key; when unset the widget is not rendered. get_freshdesk_widget_id() coerces the value to a positive integer within JavaScript's safe-integer range, so a malformed value can neither inject into the inline script nor lose precision in the emitted fwSettings. All widget cosmetics (button colour, text, position, form fields) are controlled server-side in Freshdesk.
Add get_freshdesk_app_name() (env CKAN_UNAIDS_FRESHDESK_APP_NAME or
config ckanext.unaids.freshdesk_app_name). When set, the widget bootstrap
calls FreshworksWidget('prefill', 'ticketForm', ...) to auto-select the
"Select application" custom field (cf_select_model) so tickets raised from
ADR are tagged/routed correctly, matching the client's own integration.
The prefill call is placed right after the queue stub (before the widget
script loads) so it is buffered and applied on init - the client's own
snippet gates it inside the script's load handler on
"FreshworksWidget is not a function", which is false by then, so their
prefill likely never fires.
Render base.html and assert the widget behaviour the helper unit tests can't cover: the app name is emitted escaped (no </script> breakout via tojson), the prefill call only appears when the app name is set, and no widget/fwSettings is emitted when the widget id is absent.
A-Souhei
force-pushed
the
freshdesk-feedback-widget
branch
from
July 15, 2026 08:36
c2ce7fd to
0a68b5f
Compare
cooper667
approved these changes
Jul 15, 2026
Contributor
|
14 tests for an invalid id is very defensive but it's fine 😅 |
The template regression test renders the app via the `app` fixture; every
other app-loading test class in the suite loads scheming_datasets. Without
it, this test built a Flask URL map lacking the scheming dataset-type
routes, which leaked into test_plugin.py::TestValidatePackage and broke its
url_for('validate-package_resource.download'). Aligning the plugin set
keeps the widget assertions intact and stops the cross-test pollution.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds the in-page Freshworks/Freshdesk feedback widget (floating "Contact us" launcher → in-page ticket form), matching the embed Avenir already uses on its other Spectrum apps (FamPlan, DemProj). It is config-gated: nothing is rendered unless a widget id is set.
helpers.get_freshdesk_widget_id()— readsCKAN_UNAIDS_FRESHDESK_WIDGET_ID(env) →ckanext.unaids.freshdesk_widget_id(config); returns a positive int within JS safe-integer range, elseNone.helpers.get_freshdesk_app_name()— readsCKAN_UNAIDS_FRESHDESK_APP_NAME(env) →ckanext.unaids.freshdesk_app_name(config). When set, the widget bootstrap callsFreshworksWidget('prefill', 'ticketForm', …)to auto-select the "Select application" custom field (cf_select_model), so tickets raised from ADR are tagged/routed correctly — matching the client's own integration. Optional: leave it unset and the widget still works, just without the prefill.base.htmlhead_extras— when the widget id is set, injectswindow.fwSettings={'widget_id': <id>}+ the Freshworks bootstrap (and the prefill call when an app name is set), then loadswidget.freshworks.com/widgets/<id>.js.This embeds a third-party script on every page. Before turning it on in any environment, the deploy/proxy layer must:
script-src https://widget.freshworks.comframe-src https://*.freshworks.comconnect-src https://*.freshworks.comfwSettingsbootstrap is an inline<script>. Under a strict CSP it needs a nonce or hash (or'unsafe-inline').Until these are in place, leave the widget id unset (default) → widget is not rendered.
Configuration
Widget id (required to show the widget; unset = widget off):
CKAN_UNAIDS_FRESHDESK_WIDGET_ID=<numeric widget id from Freshdesk>ckanext.unaids.freshdesk_widget_id = <numeric widget id>Application name (optional; enables the "Select application" prefill):
CKAN_UNAIDS_FRESHDESK_APP_NAME=AIDS Data Repository (ADR)ckanext.unaids.freshdesk_app_name = AIDS Data Repository (ADR)Both follow env-first, then config; blank/whitespace is treated as unset.
Security / validation
Both values are operator-controlled (env/ini), never user input.
get_freshdesk_widget_id()coerces to a positive integer ≤ 2⁵³−1; any non-numeric/negative/zero/oversized value →None(widget hidden). As a guaranteedintemitted via| tojson, it cannot break out of the inline<script>or the scriptsrc.get_freshdesk_app_name()is free text but is emitted via| tojson, which HTML-safe-escapes< > & '→\uXXXX, so it cannot break out of the inline<script>either.Reviewed by Claude + Codex; both converged on MERGE (no injection; prefill correctly buffered before the widget script loads; gating correct).
Testing
TestGetFreshdeskWidgetId(14 cases): valid ids, whitespace trim,0/negative/non-numeric/injection →None, the 2⁵³−1 / 2⁵³ safe-integer boundary, blank-env→config fallback, env-over-config precedence, unset→None.TestGetFreshdeskAppName(7 cases): value pass-through + trim, blank/whitespace →None, config fallback, env-over-config precedence, unset→None.TestFreshdeskWidgetTemplate(3 cases): rendersbase.htmland asserts the app name is emitted escaped (no</script>breakout), the prefill appears only when the app name is set, and no widget/fwSettingsis emitted when the id is unset.Verified live against the real ADR Freshworks widget (launcher renders, form opens, "Select application" pre-set to "AIDS Data Repository (ADR)").