fix(idaa): upgrade Novi UUID verification to server-side API call
Previously, IDAA iframe access relied on trusting URL params (uuid, email, full_name) passed from Novi — any 36-char string granted authenticated access with no actual verification. The (idaa)/+layout.svelte now performs an async Novi API call on every UUID load to verify the UUID exists, fetches name/email directly from Novi (cannot be spoofed via URL), and sets $idaa_loc.novi_verified on success. All-or-nothing: if novi_idaa_api_key is absent or the call fails, access denied. - ae_idaa_stores.ts: add novi_verified boolean field to idaa_loc - (idaa)/+layout.svelte: async UUID verification with spinner to prevent Access Denied flash; permission upgrade-only strategy preserved - video_conferences/+page.svelte: skip duplicate Novi member details call if layout already verified ($idaa_loc.novi_verified check) - iframe HTML files: remove browser-side Novi API fetch and email/full_name params; pass only uuid; add README/START/STOP/WARNING comments for client staff; fix iframe-before-script DOM ordering bug - documentation: CLIENT__IDAA_and_customized_mods.md updated with full verification flow, site_cfg_json fields, permission table, access gate Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
**Client:** International Doctors in Alcoholics Anonymous (IDAA)
|
||||
**Module Path:** `src/routes/idaa/`
|
||||
**State Stores:** `src/lib/stores/ae_idaa_stores.ts`
|
||||
**Last Updated:** 2026-02-26
|
||||
**Last Updated:** 2026-03-09 (Novi UUID verification upgrade)
|
||||
|
||||
---
|
||||
|
||||
@@ -74,15 +74,31 @@ src/routes/idaa/
|
||||
│ │ ├── ae_idaa_comp__post_obj_id_edit.svelte
|
||||
│ │ └── ae_idaa_comp__post_comment_obj_id_edit.svelte
|
||||
│ ├── recovery_meetings/ # Recovery Meetings (Events repurposed)
|
||||
│ │ ├── +layout.ts # Layout loader (auth, stores)
|
||||
│ │ ├── +layout.svelte # Layout wrapper
|
||||
│ │ ├── +page.svelte # Meeting list + search filters
|
||||
│ │ ├── ae_idaa_comp__event_obj_li_wrapper.svelte # List container/modal host
|
||||
│ │ ├── ae_idaa_comp__event_obj_li.svelte # Individual list item card
|
||||
│ │ ├── ae_idaa_comp__event_obj_qry.svelte # Query/filter bar
|
||||
│ │ ├── ae_idaa_comp__event_obj_id_view.svelte # Meeting detail (read-only)
|
||||
│ │ ├── ae_idaa_comp__event_obj_id_edit.svelte # Meeting edit form (v1, legacy — do not touch)
|
||||
│ │ ├── ae_idaa_comp__event_obj_id_edit_v2.svelte # Meeting edit form (v2, active)
|
||||
│ │ └── [event_id]/
|
||||
│ │ ├── +page.svelte # Meeting detail
|
||||
│ │ ├── ae_idaa_comp__event_obj_id_view.svelte
|
||||
│ │ └── ae_idaa_comp__event_obj_id_edit.svelte
|
||||
│ │ ├── +page.svelte # Meeting detail page — renders view OR edit based on session flag
|
||||
│ │ └── +page.ts
|
||||
│ └── video_conferences/ # Jitsi video conference integration
|
||||
└── jitsi_reports/ # External Jitsi reporting
|
||||
```
|
||||
|
||||
> **Note:** Recovery Meetings has **two UI entry points**:
|
||||
> 1. **Modal pattern** (primary list flow) — list, view, and edit components live at `recovery_meetings/`
|
||||
> level, toggled via `$idaa_sess.recovery_meetings` session flags (`show__modal_view`, `show__modal_edit`).
|
||||
> 2. **Direct page** (`[event_id]/+page.svelte`) — navigating to `/idaa/recovery_meetings/<id>` renders
|
||||
> the same view/edit components gated by `$idaa_sess.recovery_meetings.edit__event_obj`.
|
||||
>
|
||||
> Both patterns use `ae_idaa_comp__event_obj_id_edit_v2.svelte`. The edit form clears **both**
|
||||
> `show__modal_edit` and `edit__event_obj` on save/cancel so it works correctly from either entry point.
|
||||
|
||||
---
|
||||
|
||||
## Authentication: Novi UUID System
|
||||
@@ -92,18 +108,46 @@ IDAA members do not log in through Aether — they log in through Novi (idaa.org
|
||||
### URL Parameters (on iframe load)
|
||||
```
|
||||
?uuid=<36-char-uuid>
|
||||
&email=<url-encoded-email>
|
||||
&full_name=<url-encoded-name>
|
||||
&iframe=true
|
||||
&key=<site-access-key>
|
||||
```
|
||||
|
||||
> **Security note (2026-03-09):** The iframe HTML files previously also passed `email` and `full_name`
|
||||
> via URL params. These were unverifiable claims that could be spoofed via URL. They have been removed.
|
||||
> The SvelteKit layout now fetches verified identity directly from the Novi API.
|
||||
> See "Iframe Integration" → "Novi UUID Verification Flow" below.
|
||||
|
||||
### Verification Flow (`(idaa)/+layout.svelte`)
|
||||
|
||||
When a `uuid` param is present in the URL, the layout performs an **async Novi API call** to verify:
|
||||
|
||||
1. The UUID actually exists in Novi's system (prevents fake/crafted UUIDs)
|
||||
2. Gets verified name and email directly from Novi — these can't be forged via URL
|
||||
3. Sets `$idaa_loc.novi_uuid`, `$idaa_loc.novi_email`, `$idaa_loc.novi_full_name`
|
||||
4. Sets `$idaa_loc.novi_verified = true` on success
|
||||
|
||||
A `novi_verifying` UI state prevents the "Access Denied" screen from flashing during the API round-trip.
|
||||
|
||||
**All or nothing:** If the Novi API key is not configured, or the verification call fails, access is denied. There is no URL-param fallback.
|
||||
|
||||
**Required `site_cfg_json` fields:**
|
||||
```json
|
||||
{
|
||||
"novi_idaa_api_key": "Base64-encoded-key-from-Novi",
|
||||
"novi_api_root_url": "https://www.idaa.org/api", // optional, this is the default
|
||||
"novi_admin_li": ["uuid-1", "uuid-2"],
|
||||
"novi_trusted_li": ["uuid-3", "uuid-4"],
|
||||
"novi_idaa_group_guid_li": ["group-uuid"] // Jitsi moderators only
|
||||
}
|
||||
```
|
||||
|
||||
### Permission Levels (Ascending)
|
||||
| Level | Condition | Access |
|
||||
|---|---|---|
|
||||
| Anonymous | No UUID or unrecognized | No access |
|
||||
| Authenticated | Valid UUID (36 chars) | View own content, limited actions |
|
||||
| Trusted | UUID in `novi_trusted_li` | Full member access to all IDAA content |
|
||||
| Administrator | UUID in `novi_admin_li` | Full access + edit/manage |
|
||||
| Anonymous | No UUID, unrecognized UUID, or verification failure | No access |
|
||||
| Authenticated | UUID verified against Novi API | View own content, limited actions |
|
||||
| Trusted | Verified UUID in `novi_trusted_li` | Full member access to all IDAA content |
|
||||
| Administrator | Verified UUID in `novi_admin_li` | Full access + edit/manage |
|
||||
|
||||
`novi_trusted_li` and `novi_admin_li` are managed in Aether site config (not in Novi directly).
|
||||
|
||||
@@ -116,9 +160,14 @@ IDAA members do not log in through Aether — they log in through Novi (idaa.org
|
||||
|
||||
This ensures that OSIT staff with `super` or `manager` roles retain full access regardless of Novi UUID status.
|
||||
|
||||
### Non-Novi Sign-in Paths (unaffected)
|
||||
- **User/Pass or Auth Link:** No `uuid` in URL → layout Novi block does not run
|
||||
- **Shared Passcode:** No `uuid` in URL → layout Novi block does not run
|
||||
|
||||
### Access Gate (`(idaa)/+layout.svelte`)
|
||||
The inner layout blocks ALL rendering if the user is not authorized:
|
||||
- Anonymous → "Access Denied" error page
|
||||
- `novi_verifying = true` → "Verifying identity..." spinner
|
||||
- Verification failed or no UUID → "Access Denied" error page
|
||||
- Access check runs before any child routes render
|
||||
|
||||
---
|
||||
@@ -214,9 +263,49 @@ Members can filter meetings by:
|
||||
|
||||
Search is debounced (250ms) and uses the standard Aether SWR pattern.
|
||||
|
||||
### Edit Form — Sections and Key Fields
|
||||
|
||||
The edit form (`ae_idaa_comp__event_obj_id_edit.svelte`) is organized into these sections.
|
||||
All fields map directly to the `ae_Event` object; none are IDAA-specific custom fields.
|
||||
|
||||
| Section | Key Fields |
|
||||
| --- | --- |
|
||||
| **General Information** | `name` (required), `description` (TipTap rich text), `type` (IDAA / Caduceus / Family Recovery) |
|
||||
| **How to Attend** | `physical` (bool), `virtual` (bool) toggles; conditionally shows: |
|
||||
| → Physical | `location_address_json` (name, line_1–3, city, state, postal, country), `location_text` (TipTap) |
|
||||
| → Virtual | Platform toggle: **Zoom** (`attend_url_code` meeting ID, `attend_url_passcode`, `attend_json.zoom.passcode_enc`, `attend_json.zoom.domain`, `attend_json.zoom.full_url`), **Jitsi** (`attend_json.jitsi.*`), **Other** (`attend_url`, `attend_url_passcode`, `attend_phone`, `attend_phone_passcode`) |
|
||||
| → Both | `attend_text` (TipTap — additional attendance instructions) |
|
||||
| **Schedule** | `recurring_pattern` (weekly/every other week/monthly/other), `weekday_*` (Sun–Sat booleans), `timezone`, `recurring_start_time`, `recurring_end_time`, `recurring_text` (optional TipTap, auto-generated with `*gen*` prefix if blank) |
|
||||
| **Contacts** | `external_person_id` (Novi UUID link), `contact_li_json[0]` (Contact 1: name, email, phone_mobile, phone_home, phone_office — name/email locked to Novi user by default), `contact_li_json[1]` (Contact 2: same fields, optional) |
|
||||
| **Admin Options** | `status`, `hide`, `priority`, `sort`, `group`, `enable`, `notes` (TipTap) — **trusted_access only** |
|
||||
|
||||
**Rich text fields** all use `AE_Comp_Editor_TipTap` with separate `*_new_html` state variables
|
||||
(not bound to `$idaa_slct.event_obj` directly) to track change state for the save-button logic.
|
||||
|
||||
**Zoom URL auto-generation:** Triggered by `$idaa_trig = 'update_zoom_full_url'`. An `$effect`
|
||||
reconstructs `attend_json.zoom.full_url` from domain + meeting_id + passcode_enc whenever
|
||||
the Meeting ID, Passcode, Encrypted Passcode, or Domain fields change.
|
||||
|
||||
**Recurring text auto-generation:** If `recurring_text` is blank or contains the `*gen*` prefix,
|
||||
the submit handler generates a human-readable string (e.g., `*gen* weekly: Monday, Wednesday at 7:00 PM America/Chicago`).
|
||||
Members can opt into a custom text via "Add More Details?" (admin/trusted only).
|
||||
|
||||
**Contact 1 lock:** Contact 1 name and email default to the logged-in Novi member's identity
|
||||
(`$idaa_loc.novi_full_name`, `$idaa_loc.novi_email`). They are `readonly` unless the user
|
||||
explicitly unlocks them via confirm dialog (or has administrator access).
|
||||
|
||||
### Jitsi Integration
|
||||
Some virtual meetings are hosted via Jitsi. Members with a Jitsi moderator UUID (`novi_jitsi_mod_li`) have elevated permissions in video sessions.
|
||||
|
||||
### Edit Form — Implementation Notes (v2)
|
||||
|
||||
- The v2 edit form uses a `<style>` block with `@apply`. Tailwind v4 requires
|
||||
`@reference "../../../../app.css";` at the top of any component `<style>` block that uses `@apply`.
|
||||
- The country subdivision lookup list (`lu_country_subdivision_list`) contains duplicate entries —
|
||||
specifically Puerto Rico (`PR`) has two rows with `code = '-'`. The `{#each}` key must use
|
||||
the array index (`i`) rather than `sub.code` to avoid a Svelte `each_key_duplicate` error.
|
||||
The duplicate entries are a **backend data quality issue** that should be cleaned up in the DB.
|
||||
|
||||
### Demo / Test IDs
|
||||
No dedicated IDAA recovery meeting demo records — uses the standard Event demo record for dev:
|
||||
- Event: `pjrcghqwert` (id: 1) "Demo One Sky IT Conference"
|
||||
@@ -241,9 +330,10 @@ Four stores manage all IDAA state:
|
||||
Stores Novi auth context and per-submodule query settings:
|
||||
```typescript
|
||||
{
|
||||
novi_uuid: string | null // Member UUID from Novi
|
||||
novi_email: string | null
|
||||
novi_full_name: string | null
|
||||
novi_uuid: string | null // Member UUID (set on verification success)
|
||||
novi_email: string | null // Verified email from Novi API
|
||||
novi_full_name: string | null // Verified name from Novi API
|
||||
novi_verified: boolean // true after successful Novi API verification
|
||||
novi_admin_li: string[] // Admin UUID list (from site config)
|
||||
novi_trusted_li: string[] // Trusted member UUID list
|
||||
novi_jitsi_mod_li: string[] // Jitsi moderator UUIDs
|
||||
@@ -288,6 +378,44 @@ The IDAA module is embedded in `idaa.org` via iframe. This requires:
|
||||
2. **URL parameter auth** — Novi passes member context via query string on load
|
||||
3. **No standard navigation** — Members navigate within the iframe; Aether's nav chrome is hidden or minimal in this context
|
||||
|
||||
### Novi UUID Verification Flow
|
||||
|
||||
**Iframe HTML files** (in `static/`): Pass only `uuid` to the iframe src — no Novi API calls in the browser:
|
||||
```text
|
||||
idaa_novi_iframe_archives.html
|
||||
idaa_novi_iframe_bulletin_board.html
|
||||
idaa_novi_iframe_recovery_meetings.html
|
||||
idaa_novi_iframe_jitsi_meeting.html ← reference pattern (unchanged)
|
||||
```
|
||||
|
||||
**SvelteKit layout** (`(idaa)/+layout.svelte`): Calls `GET /customers/{uuid}` on the Novi API using the `novi_idaa_api_key` from `site_cfg_json`. Sets verified name/email in `$idaa_loc` and grants permissions. Shows a "Verifying identity..." spinner during the async call.
|
||||
|
||||
**Jitsi page** (`video_conferences/+page.svelte`): Checks `$idaa_loc.novi_verified` in `fetch_novi_data()`. If the layout already verified the user, it reuses `$idaa_loc.novi_email` / `$idaa_loc.novi_full_name` and skips the duplicate member details API call. The group moderator check (`get_novi_group_moderators`) always runs — it is Jitsi-specific.
|
||||
|
||||
### ⚠️ Iframe CSS Conflicts (Bootstrap v3)
|
||||
|
||||
When `$ae_loc.iframe = true`, the root layout (`+layout.svelte`) injects two external stylesheets from Novi's CDN:
|
||||
|
||||
```text
|
||||
https://assets-staging.noviams.com/novi-core-assets/css/fontawesome.css — safe, icon-only
|
||||
https://assets-staging.noviams.com/novi-core-assets/css/c/idaa/idaa.css — Bootstrap v3.4.1 ⚠️
|
||||
```
|
||||
|
||||
`idaa.css` is a full **Bootstrap v3.4.1** bundle. It applies global styles to bare HTML elements
|
||||
(`input`, `select`, `textarea`, `h1–h6`) and commonly named classes (`.btn`, `.badge`, `.active`,
|
||||
`.text-*`, `.bg-*`). These will compete with Tailwind v4 + Skeleton UI.
|
||||
|
||||
**Known consequences:**
|
||||
- Bare form elements (`<input>`, `<select>`) receive Bootstrap's height/padding resets on top of Tailwind
|
||||
- `.btn` class gets Bootstrap button colors, potentially overriding `preset-*` Skeleton classes
|
||||
- `<section>` and heading elements may get unexpected margins/padding from Bootstrap's typography reset
|
||||
- Class names `.field-input` and `.field-label` (used in the v2 edit form's scoped `<style>` block)
|
||||
also exist in `idaa.css`'s date picker — Svelte's scoped attribute selector wins, but be aware
|
||||
|
||||
**Mitigation:** The iframe CSS conflicts existed before v2 and are not new. The v2 form uses the
|
||||
same Skeleton/Tailwind component classes as the rest of the app. Avoid using bare `<section>`,
|
||||
`<article>`, or block-level HTML5 elements as style hooks; use `<div>` with explicit classes instead.
|
||||
|
||||
---
|
||||
|
||||
## Testing Requirements
|
||||
@@ -348,5 +476,5 @@ ae_loc.idaa_loc = { novi_uuid: 'test-uuid-value', ... };
|
||||
|
||||
---
|
||||
|
||||
**Document Status:** ✅ Complete (initial)
|
||||
**Last Verified:** 2026-02-26 — reverse-engineered from source code
|
||||
**Document Status:** ✅ Current
|
||||
**Last Verified:** 2026-03-09 — updated for Novi UUID verification upgrade
|
||||
|
||||
@@ -23,13 +23,20 @@ const idaa_local_data_struct: key_val = {
|
||||
novi_uuid: null,
|
||||
novi_email: null,
|
||||
novi_full_name: null,
|
||||
novi_admin_li: ['2b078deb-b4e7-4203-99da-9f7cd62159a9'], // '2b078deb-b4e7-4203-99da-9f7cd62159a5'
|
||||
novi_trusted_li: [
|
||||
'c9ea07b5-06b0-4a43-a2d0-8d06558c8a82',
|
||||
'58db22ee-4b0a-49a7-9f34-53d2ba85a84b',
|
||||
'2b078deb-b4e7-4203-99da-9f7cd62159a5',
|
||||
// True after a successful Novi API verification (UUID confirmed to be a real Novi member).
|
||||
// False on load, on verification failure, or for non-Novi sign-in paths.
|
||||
novi_verified: false,
|
||||
novi_admin_li: [
|
||||
// '2b078deb-b4e7-4203-99da-9f7cd621xxxx', // Scott - '2b078deb-b4e7-4203-99da-9f7cd62159a5'
|
||||
],
|
||||
novi_trusted_li: [
|
||||
// 'c9ea07b5-06b0-4a43-a2d0-8d06558c8a82', // Michelle - 'c9ea07b5-06b0-4a43-a2d0-8d06558c8a82'
|
||||
// '58db22ee-4b0a-49a7-9f34-53d2ba85a84b', // Brie - '58db22ee-4b0a-49a7-9f34-53d2ba85a84b'
|
||||
// '2b078deb-b4e7-4203-99da-9f7cd62159a5', // Scott - '2b078deb-b4e7-4203-99da-9f7cd62159a5'
|
||||
],
|
||||
novi_jitsi_mod_li: [
|
||||
// '5724aad7-6d89-47e7-8943-966fd22911bd', // '5724aad7-6d89-47e7-8943-966fd22911bd'
|
||||
],
|
||||
novi_jitsi_mod_li: ['5724aad7-6d89-47e7-8943-966fd22911bd'],
|
||||
|
||||
novi_archives_base_url: 'https://www.idaa.org/idaa-archives',
|
||||
novi_bb_base_url: 'https://www.idaa.org/idaa-bulletin-board',
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
// *** Import Svelte specific
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
// *** Import other supporting libraries
|
||||
|
||||
// *** Import Aether specific variables and functions
|
||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||
import {
|
||||
@@ -20,8 +18,6 @@
|
||||
} from '$lib/stores/ae_stores';
|
||||
import { idaa_loc, idaa_sess, idaa_slct } from '$lib/stores/ae_idaa_stores';
|
||||
|
||||
// import Help_tech from '$lib/e_app_help_tech.svelte';
|
||||
|
||||
interface Props {
|
||||
/** @type {import('./$types').LayoutData} */
|
||||
data: any;
|
||||
@@ -30,6 +26,11 @@
|
||||
|
||||
let { data, children }: Props = $props();
|
||||
|
||||
// True while the async Novi API verification call is in flight.
|
||||
// Prevents the access gate from flashing "Access Denied" during the network round-trip.
|
||||
let novi_verifying: boolean = $state(false);
|
||||
|
||||
// Effect 1: Set URL origin and params (unchanged from original)
|
||||
$effect(() => {
|
||||
untrack(() => {
|
||||
$ae_loc.url_origin = data.url.origin;
|
||||
@@ -41,143 +42,164 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Effect 2: Novi UUID verification
|
||||
// Only fires when a uuid is present in the URL (i.e. the Novi iframe path).
|
||||
// Non-Novi sign-in paths (User/Pass, shared passcode) will never have a uuid param,
|
||||
// so this block won't run for them — their permissions are unaffected.
|
||||
$effect(() => {
|
||||
if (browser) {
|
||||
untrack(() => {
|
||||
if (data.url.searchParams.get('uuid')) {
|
||||
$idaa_loc.novi_uuid = data.url.searchParams.get('uuid'); // data.params.uuid;
|
||||
}
|
||||
if (data.url.searchParams.get('email')) {
|
||||
$idaa_loc.novi_email = decodeURIComponent(data.url.searchParams.get('email'));
|
||||
} else {
|
||||
$idaa_loc.novi_email = null;
|
||||
}
|
||||
if (data.url.searchParams.get('full_name')) {
|
||||
$idaa_loc.novi_full_name = decodeURIComponent(data.url.searchParams.get('full_name'));
|
||||
} else {
|
||||
$idaa_loc.novi_full_name = null;
|
||||
}
|
||||
// Only override lists from site_cfg_json if it actually provides them.
|
||||
// Falling back to [] would overwrite the hardcoded defaults in ae_idaa_stores.ts
|
||||
// and cause staff UUIDs to be silently ignored.
|
||||
if ($ae_loc.site_cfg_json?.novi_admin_li?.length) {
|
||||
$idaa_loc.novi_admin_li = $ae_loc.site_cfg_json.novi_admin_li;
|
||||
}
|
||||
if ($ae_loc.site_cfg_json?.novi_trusted_li?.length) {
|
||||
$idaa_loc.novi_trusted_li = $ae_loc.site_cfg_json.novi_trusted_li;
|
||||
}
|
||||
if (!browser) return;
|
||||
|
||||
// Determine target Novi-based access level
|
||||
let target_novi_level = 'anonymous';
|
||||
if ($idaa_loc.novi_uuid) {
|
||||
if ($idaa_loc.novi_admin_li?.includes($idaa_loc.novi_uuid)) {
|
||||
target_novi_level = 'administrator';
|
||||
} else if ($idaa_loc.novi_trusted_li?.includes($idaa_loc.novi_uuid)) {
|
||||
target_novi_level = 'trusted';
|
||||
} else if ($idaa_loc?.novi_uuid?.length == 36) {
|
||||
// Any valid Novi UUID (36 chars) grants authenticated access.
|
||||
// We do NOT require $ae_loc.iframe here — that flag may not be set
|
||||
// yet due to effect ordering, and having a UUID in the URL is
|
||||
// sufficient proof of a Novi-originated request.
|
||||
target_novi_level = 'authenticated';
|
||||
}
|
||||
}
|
||||
const uuid = data.url.searchParams.get('uuid'); // tracked — re-runs if URL changes
|
||||
|
||||
// PERMISSION UPGRADE STRATEGY:
|
||||
// Only apply Novi-based permissions if they are HIGHER than the current level.
|
||||
// This prevents a global 'manager' from being downgraded to 'administrator' or 'authenticated' by the IDAA layout.
|
||||
const current_level = $ae_loc.access_type || 'anonymous';
|
||||
if (ae_util.compare_access_levels(target_novi_level, current_level) === 1) {
|
||||
console.log(`IDAA Layout: Upgrading access from ${current_level} to ${target_novi_level} (Novi detected)`);
|
||||
const perms = ae_util.process_permission_checks(target_novi_level);
|
||||
$ae_loc = { ...$ae_loc, ...perms };
|
||||
} else {
|
||||
if (log_lvl > 1) console.log(`IDAA Layout: Keeping current access ${current_level} (Novi level ${target_novi_level} is not an upgrade)`);
|
||||
}
|
||||
untrack(() => {
|
||||
if (!uuid) {
|
||||
// No UUID in URL — non-Novi path, nothing to do here.
|
||||
$idaa_loc.novi_verified = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Resetting these just in case...
|
||||
$idaa_loc.bb.qry__hidden = 'not_hidden';
|
||||
$idaa_loc.bb.qry__enabled = 'enabled';
|
||||
});
|
||||
}
|
||||
// Load admin/trusted lists from site config first — needed by verify function.
|
||||
// Only override if site_cfg_json actually provides them; falling back to [] would
|
||||
// silently overwrite the hardcoded defaults in ae_idaa_stores.ts.
|
||||
if ($ae_loc.site_cfg_json?.novi_admin_li?.length) {
|
||||
$idaa_loc.novi_admin_li = $ae_loc.site_cfg_json.novi_admin_li;
|
||||
}
|
||||
if ($ae_loc.site_cfg_json?.novi_trusted_li?.length) {
|
||||
$idaa_loc.novi_trusted_li = $ae_loc.site_cfg_json.novi_trusted_li;
|
||||
}
|
||||
|
||||
const novi_api_key = $ae_loc.site_cfg_json?.novi_idaa_api_key ?? null;
|
||||
const novi_api_root_url =
|
||||
$ae_loc.site_cfg_json?.novi_api_root_url ?? 'https://www.idaa.org/api';
|
||||
|
||||
// Fire-and-forget the async verification. After the first await, Svelte's
|
||||
// reactive tracking no longer applies, so writes to stores are safe.
|
||||
novi_verifying = true;
|
||||
verify_novi_uuid(uuid, novi_api_key, novi_api_root_url);
|
||||
});
|
||||
});
|
||||
|
||||
// let iframe = data.url.searchParams.get('iframe');
|
||||
// if (browser && iframe == 'true') {
|
||||
// console.log('Use iframe layout!');
|
||||
// // data_struct['iframe'] = iframe;
|
||||
// $ae_loc.iframe = true;
|
||||
/**
|
||||
* Verifies a Novi UUID against the Novi API and sets permissions accordingly.
|
||||
* "All or nothing" — if no API key is configured or the call fails, access is denied.
|
||||
* Called from within untrack(), so store writes here will not trigger reactive loops.
|
||||
*/
|
||||
async function verify_novi_uuid(
|
||||
uuid: string,
|
||||
api_key: string | null,
|
||||
api_root_url: string
|
||||
) {
|
||||
if (!api_key) {
|
||||
// No Novi API key in site config. All-or-nothing means no UUID-based access.
|
||||
console.warn('IDAA Layout: Novi API key not configured. UUID-based access denied.');
|
||||
$idaa_loc.novi_uuid = null;
|
||||
$idaa_loc.novi_email = null;
|
||||
$idaa_loc.novi_full_name = null;
|
||||
$idaa_loc.novi_verified = false;
|
||||
novi_verifying = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// document.getElementsByTagName('html')[0].classList.add('iframe');
|
||||
// // document.getElementsByTagName('html')[0].classList.remove('dark');
|
||||
// // document.getElementsByTagName('html')[0].classList.remove('light');
|
||||
try {
|
||||
if (log_lvl > 1) {
|
||||
console.log(`IDAA Layout: Verifying Novi UUID ${uuid} via API...`);
|
||||
}
|
||||
|
||||
// // $ae_loc.app_cfg.show_element__access_type = false;
|
||||
// // $ae_loc.app_cfg.show_element__cfg = false;
|
||||
// } else if (browser && iframe == 'false') {
|
||||
// // data_struct['iframe'] = false;
|
||||
// $ae_loc.iframe = false;
|
||||
const headers = new Headers();
|
||||
headers.append('Authorization', `Basic ${api_key}`);
|
||||
const response = await fetch(`${api_root_url}/customers/${uuid}`, {
|
||||
method: 'GET',
|
||||
headers
|
||||
});
|
||||
|
||||
// document.getElementsByTagName('html')[0].classList.remove('iframe');
|
||||
// // document.getElementsByTagName('html')[0].classList.add('light');
|
||||
// }
|
||||
if (!response.ok) {
|
||||
throw new Error(`Novi API returned ${response.status} for UUID ${uuid}`);
|
||||
}
|
||||
|
||||
// $effect(() => {
|
||||
// if ($ae_loc.iframe && $ae_loc.iframe_height && $ae_loc.iframe_height_modal_body) {
|
||||
// if (log_lvl > 1) {
|
||||
// console.log('Getting new dimensions for iframe with modal:', $ae_loc.iframe_height, $ae_loc.iframe_height_modal_body);
|
||||
// }
|
||||
const result = await response.json();
|
||||
|
||||
// let iframe_height = 0;
|
||||
// Build display name: prefer "First L." format, fall back to full Name field.
|
||||
const first_name = result?.FirstName ?? null;
|
||||
const last_initial = result?.LastName
|
||||
? `${result.LastName.charAt(0).toUpperCase()}.`
|
||||
: '';
|
||||
const verified_name =
|
||||
first_name && last_initial
|
||||
? `${first_name} ${last_initial}`
|
||||
: (result?.Name ?? null);
|
||||
|
||||
// if ($ae_loc.iframe_height > $ae_loc.iframe_height_modal_body) {
|
||||
// iframe_height = $ae_loc.iframe_height;
|
||||
// } else {
|
||||
// iframe_height = $ae_loc.iframe_height_modal_body;
|
||||
// Normalize email — Novi occasionally includes spaces where + should be.
|
||||
const verified_email = result?.Email
|
||||
? result.Email.replace(/\s+/g, '+')
|
||||
: null;
|
||||
|
||||
// // console.log($ae_loc.modal_dimensions);
|
||||
$idaa_loc.novi_uuid = uuid;
|
||||
$idaa_loc.novi_email = verified_email;
|
||||
$idaa_loc.novi_full_name = verified_name;
|
||||
$idaa_loc.novi_verified = true;
|
||||
|
||||
// if ($ae_loc.modal_dimensions && $ae_loc.modal_dimensions.header_height) {
|
||||
// iframe_height = iframe_height + $ae_loc.modal_dimensions.header_height;
|
||||
// }
|
||||
// if ($ae_loc.modal_dimensions && $ae_loc.modal_dimensions.footer_height) {
|
||||
// iframe_height = iframe_height + $ae_loc.modal_dimensions.footer_height;
|
||||
// }
|
||||
// // iframe_height = iframe_height + 50; // Just in case
|
||||
// }
|
||||
console.log(
|
||||
`IDAA Layout: Novi UUID verified. Name: ${verified_name}, Email: ${verified_email}`
|
||||
);
|
||||
|
||||
// if (log_lvl > 1) {
|
||||
// console.log(`Suggested new iframe_height with modal: ${iframe_height}`);
|
||||
// }
|
||||
// window.parent.postMessage({'iframe_height': iframe_height}, "*"); // This should be in pixels
|
||||
// } else if ($ae_loc.iframe && $ae_loc.iframe_height) {
|
||||
// if (log_lvl > 1) {
|
||||
// console.log('Suggested new iframe_height:', $ae_loc.iframe_height);
|
||||
// }
|
||||
// window.parent.postMessage({'iframe_height': $ae_loc.iframe_height}, "*"); // This should be in pixels
|
||||
// }
|
||||
// });
|
||||
// Determine permission level based on verified UUID.
|
||||
// UUID confirmed real → at minimum authenticated. Check lists for higher levels.
|
||||
let target_novi_level = 'authenticated';
|
||||
if ($idaa_loc.novi_admin_li?.includes(uuid)) {
|
||||
target_novi_level = 'administrator';
|
||||
} else if ($idaa_loc.novi_trusted_li?.includes(uuid)) {
|
||||
target_novi_level = 'trusted';
|
||||
}
|
||||
|
||||
// PERMISSION UPGRADE STRATEGY: only apply if higher than current level.
|
||||
// This prevents a global 'manager' from being downgraded by the IDAA layout.
|
||||
const current_level = $ae_loc.access_type || 'anonymous';
|
||||
if (ae_util.compare_access_levels(target_novi_level, current_level) === 1) {
|
||||
console.log(
|
||||
`IDAA Layout: Upgrading access from ${current_level} to ${target_novi_level} (Novi verified)`
|
||||
);
|
||||
const perms = ae_util.process_permission_checks(target_novi_level);
|
||||
$ae_loc = { ...$ae_loc, ...perms };
|
||||
} else {
|
||||
if (log_lvl > 1) {
|
||||
console.log(
|
||||
`IDAA Layout: Keeping current access ${current_level} (Novi level ${target_novi_level} is not an upgrade)`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset BB query filters to safe defaults in case they were left in a non-default state.
|
||||
$idaa_loc.bb.qry__hidden = 'not_hidden';
|
||||
$idaa_loc.bb.qry__enabled = 'enabled';
|
||||
} catch (error) {
|
||||
// Verification failed — all-or-nothing means deny access.
|
||||
console.error(`IDAA Layout: Novi UUID verification failed for ${uuid}:`, error);
|
||||
$idaa_loc.novi_uuid = null;
|
||||
$idaa_loc.novi_email = null;
|
||||
$idaa_loc.novi_full_name = null;
|
||||
$idaa_loc.novi_verified = false;
|
||||
} finally {
|
||||
novi_verifying = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if browser && ($ae_loc.trusted_access || ($ae_loc.authenticated_access && $idaa_loc.novi_uuid))}
|
||||
<!-- e_class="w-xl" -->
|
||||
<!-- e_class="float-right" -->
|
||||
<!-- <Help_tech
|
||||
e_class="w-full"
|
||||
show_btn_class="absolute top-0 right-0"
|
||||
btn_class="novi_btn"
|
||||
additional_kv={{
|
||||
'novi_uuid': $idaa_loc.novi_uuid,
|
||||
'novi_email': $idaa_loc.novi_email,
|
||||
'novi_full_name': $idaa_loc.novi_full_name,
|
||||
}}
|
||||
>
|
||||
</Help_tech> -->
|
||||
<!-- <div
|
||||
bind:clientHeight={$ae_loc.iframe_height}
|
||||
> -->
|
||||
{#if !browser}
|
||||
<!-- SSR / pre-hydration placeholder -->
|
||||
<p class="text-sm text-gray-500 text-center">
|
||||
<span class="fas fa-spinner fa-spin"></span>
|
||||
Loading...
|
||||
</p>
|
||||
{:else if novi_verifying}
|
||||
<!-- Async Novi API call is in flight — show spinner to prevent Access Denied flash -->
|
||||
<div class="container flex flex-col gap-2 w-full items-center justify-center p-8 m-8">
|
||||
<p class="text-sm text-gray-500 text-center">
|
||||
<span class="fas fa-spinner fa-spin"></span>
|
||||
Verifying identity...
|
||||
</p>
|
||||
</div>
|
||||
{:else if $ae_loc.trusted_access || ($ae_loc.authenticated_access && $idaa_loc.novi_uuid)}
|
||||
{@render children?.()}
|
||||
<!-- </div> -->
|
||||
{#if $idaa_loc.novi_uuid}
|
||||
<span class="text-sm text-gray-500">
|
||||
Novi: <span class="fas fa-user m-1"></span>
|
||||
@@ -188,7 +210,7 @@
|
||||
{:else}
|
||||
<p class="text-sm text-gray-500 text-center">IDAA Novi UUID not found!</p>
|
||||
{/if}
|
||||
{:else if browser}
|
||||
{:else}
|
||||
<div class="container flex flex-col gap-1 w-full items-center justify-center font-bold p-8 m-8">
|
||||
<h1>
|
||||
<span class="text-red-500">
|
||||
@@ -214,9 +236,4 @@
|
||||
<p>IDAA Novi UUID not found!</p>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<p class="text-sm text-gray-500 text-center">
|
||||
<span class="fas fa-spinner fa-spin"></span>
|
||||
Loading...
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
||||
import { idaa_loc } from '$lib/stores/ae_idaa_stores';
|
||||
import MyClipboard from '$lib/app_components/e_app_clipboard.svelte';
|
||||
import {
|
||||
create_ae_obj__activity_log,
|
||||
@@ -350,14 +351,23 @@
|
||||
const novi_api_key = $ae_loc.site_cfg_json?.novi_idaa_api_key;
|
||||
|
||||
if (novi_api_root_url && novi_api_key) {
|
||||
const member_details = await get_novi_member_details(user_id, novi_api_root_url, novi_api_key);
|
||||
if (member_details.display_name) {
|
||||
display_name = member_details.display_name;
|
||||
console.log(`Jitsi: Updated display_name from Novi: ${display_name}`);
|
||||
}
|
||||
if (member_details.email) {
|
||||
email = member_details.email;
|
||||
console.log(`Jitsi: Updated email from Novi: ${email}`);
|
||||
// If the IDAA layout already verified this UUID, re-use those results rather than
|
||||
// making a duplicate Novi API call. The layout runs on every IDAA route, so by the
|
||||
// time onMount fires here it will usually have completed verification already.
|
||||
if ($idaa_loc.novi_verified && $idaa_loc.novi_email) {
|
||||
display_name = $idaa_loc.novi_full_name ?? display_name;
|
||||
email = $idaa_loc.novi_email ?? email;
|
||||
console.log(`Jitsi: Using layout-verified Novi data for user ${user_id}`);
|
||||
} else {
|
||||
const member_details = await get_novi_member_details(user_id, novi_api_root_url, novi_api_key);
|
||||
if (member_details.display_name) {
|
||||
display_name = member_details.display_name;
|
||||
console.log(`Jitsi: Updated display_name from Novi: ${display_name}`);
|
||||
}
|
||||
if (member_details.email) {
|
||||
email = member_details.email;
|
||||
console.log(`Jitsi: Updated email from Novi: ${email}`);
|
||||
}
|
||||
}
|
||||
|
||||
const novi_idaa_group_guid_li = $ae_loc.site_cfg_json?.novi_idaa_group_guid_li ?? [];
|
||||
|
||||
@@ -3,128 +3,122 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Novi Test Page</title>
|
||||
<title>IDAA Novi Archives iframe Example Template Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
let novi_customer_uid = '<%=Novi.User.CustomerUniqueId%>'; // NOTE: The Novi UUID for the current current user/customer
|
||||
console.log(`Novi's Current User's ID: ${novi_customer_uid}`);
|
||||
|
||||
// var novi_current_user_role = '<%=Novi.User.xyz%>';
|
||||
// console.log(`Novi's Current User's Role: ${novi_current_user_role}`);
|
||||
<!-- README: This is an example template page for embedding the IDAA Archives iframe in the IDAA Novi site. Copy the code below to use it in your own page(s). -->
|
||||
|
||||
let novi_api_root_url = 'https://www.idaa.org/api';
|
||||
let novi_api_key_for_idaa = 'CmNdWgdPmgluBWjiTd8xsUCk5mio8F1O9DYAh0pVDcg=';
|
||||
<!-- START: Copy below this point -->
|
||||
<!-- IMPORTANT: The <p> and <script> elements below are for using in an iframe in the IDAA Novi site. -->
|
||||
<!-- IDAA OSIT iframe container element for Novi - Archives iframe -->
|
||||
<p>
|
||||
<iframe
|
||||
width="100%"
|
||||
height="750"
|
||||
id="ae_idaa_archives_iframe"
|
||||
src=""
|
||||
style="min-height: 600px; max-height: 100%"
|
||||
class="ae_idaa_iframe"
|
||||
></iframe>
|
||||
</p>
|
||||
<!-- IDAA and Novi specific JavaScript to get current Novi user info and load Archives iframe -->
|
||||
<script>
|
||||
// NOTE: The Novi UUID for the current user — injected server-side by Novi.
|
||||
// This is the only identity value passed to the iframe. Identity verification
|
||||
// is handled securely by the OSIT server — do not add email or name here.
|
||||
let novi_customer_uid = '<%=Novi.User.CustomerUniqueId%>';
|
||||
console.log(`Novi's Current User's ID: ${novi_customer_uid}`);
|
||||
|
||||
let novi_current_user_obj = null;
|
||||
let novi_current_user_email = null;
|
||||
// WARNING: Do *not* use relative paths here. They must be direct to the site OSIT is hosting for IDAA.
|
||||
let idaa_osit_ae_api_root_url = 'https://dev-idaa.oneskyit.com/idaa/archives'; // NOTE: DO NOT CHANGE THIS VALUE
|
||||
// Example URLs: 'https://sk-idaa.oneskyit.com/idaa/archives' OR 'https://dev-idaa.oneskyit.com/idaa/archives'
|
||||
|
||||
let idaa_osit_site_key = '8VTOJ0X5hvT6JdiTJsGEzQ'; // 'restricted-access'
|
||||
let idaa_ae_api_root_url = 'https://sk-idaa.oneskyit.com/idaa/archives';
|
||||
let idaa_ae_params = new URLSearchParams(document.location.search);
|
||||
let idaa_ae_slct_archive_id = idaa_ae_params.get('archive_id');
|
||||
// WARNING: Do *not* change this value. It is required for access control to the IDAA AE API.
|
||||
let idaa_osit_ae_site_key = 'restricted-access'; // DO NOT CHANGE THIS VALUE
|
||||
|
||||
let idaa_ae_iframe_height = null;
|
||||
let idaa_ae_params = new URLSearchParams(document.location.search);
|
||||
let idaa_ae_slct_archive_id = idaa_ae_params.get('archive_id');
|
||||
|
||||
var novi_api_headers = new Headers();
|
||||
novi_api_headers.append('Authorization', `Basic ${novi_api_key_for_idaa}`);
|
||||
let idaa_ae_iframe_height = null;
|
||||
|
||||
var requestOptions = {
|
||||
method: 'GET',
|
||||
headers: novi_api_headers,
|
||||
redirect: 'follow'
|
||||
};
|
||||
let idaa_ae_iframe_element = document.getElementById('ae_idaa_archives_iframe');
|
||||
|
||||
let novi_api_get_customer_endpoint = `${novi_api_root_url}/customers/${novi_customer_uid}`;
|
||||
// console.log(novi_api_get_customer_endpoint);
|
||||
if (idaa_ae_slct_archive_id) {
|
||||
idaa_ae_iframe_element.src = `${idaa_osit_ae_api_root_url}/${idaa_ae_slct_archive_id}?uuid=${novi_customer_uid}&iframe=true&key=${idaa_osit_ae_site_key}`;
|
||||
} else {
|
||||
idaa_ae_iframe_element.src = `${idaa_osit_ae_api_root_url}?uuid=${novi_customer_uid}&iframe=true&key=${idaa_osit_ae_site_key}`;
|
||||
}
|
||||
|
||||
fetch(novi_api_get_customer_endpoint, requestOptions)
|
||||
// .then(response => response.text())
|
||||
.then((response) => response.json())
|
||||
.then((result) => {
|
||||
novi_current_user_obj = result;
|
||||
// console.log(`Novi's Current User Obj (${novi_current_user_obj.Email}):`, novi_current_user_obj);
|
||||
// NOTE: This listener handles messages sent from the IDAA iframe back to this parent page.
|
||||
// It adjusts the iframe height dynamically, scrolls the page when navigation occurs inside
|
||||
// the iframe, and keeps the browser URL in sync with navigation inside the iframe.
|
||||
window.addEventListener('message', function (event) {
|
||||
if (event.data) {
|
||||
if (event.data.iframe_height) {
|
||||
idaa_ae_iframe_height = event.data.iframe_height;
|
||||
|
||||
let idaa_ae_iframe_element = document.getElementById('ae_idaa_archives_iframe');
|
||||
let idaa_ae_iframe_element =
|
||||
document.getElementById('ae_idaa_archives_iframe');
|
||||
idaa_ae_iframe_element.style.height = `${idaa_ae_iframe_height + 50}px`;
|
||||
}
|
||||
|
||||
if (idaa_ae_slct_archive_id) {
|
||||
// console.log(`Loading AE Archive ID: ${idaa_ae_slct_archive_id}`);
|
||||
idaa_ae_iframe_element.src = `${idaa_ae_api_root_url}/${idaa_ae_slct_archive_id}?uuid=${novi_customer_uid}&email=${novi_current_user_obj.Email}&full_name=${novi_current_user_obj.Name}&iframe=true&key=${idaa_osit_site_key}`;
|
||||
} else {
|
||||
idaa_ae_iframe_element.src = `${idaa_ae_api_root_url}?uuid=${novi_customer_uid}&email=${novi_current_user_obj.Email}&full_name=${novi_current_user_obj.Name}&iframe=true&key=${idaa_osit_site_key}`;
|
||||
if (event.data.scroll_to !== undefined) {
|
||||
console.log(`Got scroll_to: ${event.data.scroll_to}`);
|
||||
|
||||
url.searchParams.delete('archive_id');
|
||||
history.pushState({}, '', url);
|
||||
}
|
||||
|
||||
// let iframe_src = document.getElementById('iframe_src');
|
||||
// iframe_src.innerHTML = `iframe src = ${idaa_ae_iframe_element.src}`;
|
||||
})
|
||||
.catch((error) => console.log('error', error));
|
||||
|
||||
window.addEventListener('message', function (event) {
|
||||
// console.log('Message received from the child:', event.data); // Message received from child
|
||||
|
||||
if (event.data) {
|
||||
if (event.data.iframe_height) {
|
||||
idaa_ae_iframe_height = event.data.iframe_height;
|
||||
// console.log(`Got iframe height: ${idaa_ae_iframe_height}`);
|
||||
|
||||
let idaa_ae_iframe_element =
|
||||
document.getElementById('ae_idaa_archives_iframe');
|
||||
|
||||
// idaa_ae_iframe_element.style.height = idaa_ae_iframe_height;
|
||||
idaa_ae_iframe_element.style.height = `${idaa_ae_iframe_height + 50}px`;
|
||||
}
|
||||
|
||||
const url = new URL(location);
|
||||
|
||||
// Check if archive_id is defined in the message
|
||||
if (event.data.archive_id !== undefined) {
|
||||
console.log(`Got AE Archives ID: ${event.data.archive_id}`);
|
||||
idaa_ae_slct_archive_id = event.data.archive_id;
|
||||
|
||||
if (event.data.archive_id) {
|
||||
url.searchParams.set('archive_id', event.data.archive_id);
|
||||
} else {
|
||||
url.searchParams.delete('archive_id');
|
||||
}
|
||||
history.pushState({}, '', url);
|
||||
}
|
||||
|
||||
// Check if archive_content_id is defined in the message
|
||||
if (event.data.archive_content_id !== undefined) {
|
||||
console.log(`Got AE Archives Content ID: ${event.data.archive_content_id}`);
|
||||
idaa_ae_slct_archive_content_id = event.data.archive_content_id;
|
||||
|
||||
if (event.data.archive_content_id) {
|
||||
url.searchParams.set(
|
||||
'archive_content_id',
|
||||
event.data.archive_content_id
|
||||
);
|
||||
} else {
|
||||
url.searchParams.delete('archive_content_id');
|
||||
}
|
||||
history.pushState({}, '', url);
|
||||
}
|
||||
let idaa_ae_iframe_element = document.getElementById('ae_idaa_archives_iframe');
|
||||
if (idaa_ae_iframe_element) {
|
||||
// NOTE: Scroll to the top of the iframe element, not the absolute page top.
|
||||
// The iframe is embedded below Novi's own header and navigation, so
|
||||
// scrolling to (0, 0) would show the Novi site header instead of the iframe.
|
||||
let idaa_ae_iframe_top = window.pageYOffset + idaa_ae_iframe_element.getBoundingClientRect().top;
|
||||
console.log(`Scrolling to iframe top: ${idaa_ae_iframe_top}px`);
|
||||
window.scrollTo({
|
||||
top: Math.max(0, idaa_ae_iframe_top - 20),
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
} else {
|
||||
console.log(`No data in message? ${event}`);
|
||||
console.warn(`Element with ID "ae_idaa_archives_iframe" not found.`);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
}
|
||||
|
||||
<!-- <div id="iframe_src">iframe src=</div> -->
|
||||
const url = new URL(location);
|
||||
|
||||
// Check if archive_id is defined in the message
|
||||
if (event.data.archive_id !== undefined) {
|
||||
console.log(`Got AE Archives ID: ${event.data.archive_id}`);
|
||||
idaa_ae_slct_archive_id = event.data.archive_id;
|
||||
|
||||
if (event.data.archive_id) {
|
||||
url.searchParams.set('archive_id', event.data.archive_id);
|
||||
} else {
|
||||
url.searchParams.delete('archive_id');
|
||||
}
|
||||
history.pushState({}, '', url);
|
||||
}
|
||||
|
||||
// Check if archive_content_id is defined in the message
|
||||
if (event.data.archive_content_id !== undefined) {
|
||||
console.log(`Got AE Archives Content ID: ${event.data.archive_content_id}`);
|
||||
idaa_ae_slct_archive_content_id = event.data.archive_content_id;
|
||||
|
||||
if (event.data.archive_content_id) {
|
||||
url.searchParams.set(
|
||||
'archive_content_id',
|
||||
event.data.archive_content_id
|
||||
);
|
||||
} else {
|
||||
url.searchParams.delete('archive_content_id');
|
||||
}
|
||||
history.pushState({}, '', url);
|
||||
}
|
||||
} else {
|
||||
console.log(`No data in message? ${event}`);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<!-- STOP: Do not copy below this point -->
|
||||
|
||||
<!-- IDAA Aether Apps for Novi - Archives iframe -->
|
||||
<p>
|
||||
<iframe
|
||||
width="100%"
|
||||
height="750"
|
||||
id="ae_idaa_archives_iframe"
|
||||
src=""
|
||||
style="min-height: 600px; max-height: 100%"
|
||||
class="ae_idaa_iframe"
|
||||
></iframe>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,141 +3,118 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Novi Test Page</title>
|
||||
<title>IDAA Novi Bulletin Board iframe Example Template Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
let novi_customer_uid = '<%=Novi.User.CustomerUniqueId%>'; // NOTE: The Novi UUID for the current current user/customer
|
||||
console.log(`Novi's Current User's ID: ${novi_customer_uid}`);
|
||||
|
||||
// var novi_current_user_role = '<%=Novi.User.xyz%>';
|
||||
// console.log(`Novi's Current User's Role: ${novi_current_user_role}`);
|
||||
<!-- README: This is an example template page for embedding the IDAA Bulletin Board iframe in the IDAA Novi site. Copy the code below to use it in your own page(s). -->
|
||||
|
||||
let novi_api_root_url = 'https://www.idaa.org/api';
|
||||
let novi_api_key_for_idaa = 'CmNdWgdPmgluBWjiTd8xsUCk5mio8F1O9DYAh0pVDcg=';
|
||||
<!-- START: Copy below this point -->
|
||||
<!-- IMPORTANT: The <p> and <script> elements below are for using in an iframe in the IDAA Novi site. -->
|
||||
<!-- IDAA OSIT iframe container element for Novi - Bulletin Board iframe -->
|
||||
<p>
|
||||
<iframe
|
||||
width="100%"
|
||||
height="750"
|
||||
id="ae_idaa_bb_iframe"
|
||||
src=""
|
||||
style="min-height: 600px; max-height: 100%"
|
||||
class="ae_idaa_iframe"
|
||||
></iframe>
|
||||
</p>
|
||||
<!-- IDAA and Novi specific JavaScript to get current Novi user info and load Bulletin Board iframe -->
|
||||
<script>
|
||||
// NOTE: The Novi UUID for the current user — injected server-side by Novi.
|
||||
// This is the only identity value passed to the iframe. Identity verification
|
||||
// is handled securely by the OSIT server — do not add email or name here.
|
||||
let novi_customer_uid = '<%=Novi.User.CustomerUniqueId%>';
|
||||
console.log(`Novi's Current User's ID: ${novi_customer_uid}`);
|
||||
|
||||
let novi_current_user_obj = null;
|
||||
let novi_current_user_email = null;
|
||||
// WARNING: Do *not* use relative paths here. They must be direct to the site OSIT is hosting for IDAA.
|
||||
let idaa_osit_ae_api_root_url = 'https://dev-idaa.oneskyit.com/idaa/bb'; // NOTE: DO NOT CHANGE THIS VALUE
|
||||
// Example URLs: 'https://sk-idaa.oneskyit.com/idaa/bb' OR 'https://dev-idaa.oneskyit.com/idaa/bb'
|
||||
|
||||
let idaa_osit_site_key = '8VTOJ0X5hvT6JdiTJsGEzQ'; // 'restricted-access'
|
||||
let idaa_ae_api_root_url = 'https://sk-idaa.oneskyit.com/idaa/bb';
|
||||
let idaa_ae_params = new URLSearchParams(document.location.search);
|
||||
let idaa_ae_slct_post_id = idaa_ae_params.get('post_id');
|
||||
// WARNING: Do *not* change this value. It is required for access control to the IDAA AE API.
|
||||
let idaa_osit_ae_site_key = 'restricted-access'; // DO NOT CHANGE THIS VALUE
|
||||
|
||||
let idaa_ae_iframe_height = null;
|
||||
let idaa_ae_params = new URLSearchParams(document.location.search);
|
||||
let idaa_ae_slct_post_id = idaa_ae_params.get('post_id');
|
||||
|
||||
var novi_api_headers = new Headers();
|
||||
novi_api_headers.append('Authorization', `Basic ${novi_api_key_for_idaa}`);
|
||||
let idaa_ae_iframe_height = null;
|
||||
|
||||
var requestOptions = {
|
||||
method: 'GET',
|
||||
headers: novi_api_headers,
|
||||
redirect: 'follow'
|
||||
};
|
||||
let idaa_ae_iframe_element = document.getElementById('ae_idaa_bb_iframe');
|
||||
|
||||
let novi_api_get_customer_endpoint = `${novi_api_root_url}/customers/${novi_customer_uid}`;
|
||||
// console.log(novi_api_get_customer_endpoint);
|
||||
if (idaa_ae_slct_post_id) {
|
||||
idaa_ae_iframe_element.src = `${idaa_osit_ae_api_root_url}/${idaa_ae_slct_post_id}?uuid=${novi_customer_uid}&iframe=true&key=${idaa_osit_ae_site_key}`;
|
||||
} else {
|
||||
idaa_ae_iframe_element.src = `${idaa_osit_ae_api_root_url}?uuid=${novi_customer_uid}&iframe=true&key=${idaa_osit_ae_site_key}`;
|
||||
}
|
||||
|
||||
fetch(novi_api_get_customer_endpoint, requestOptions)
|
||||
// .then(response => response.text())
|
||||
.then((response) => response.json())
|
||||
.then((result) => {
|
||||
novi_current_user_obj = result;
|
||||
// console.log(`Novi's Current User Obj (${novi_current_user_obj.Email}):`, novi_current_user_obj);
|
||||
// NOTE: This listener handles messages sent from the IDAA iframe back to this parent page.
|
||||
// It adjusts the iframe height dynamically, scrolls the page when navigation occurs inside
|
||||
// the iframe, and keeps the browser URL in sync with navigation inside the iframe.
|
||||
window.addEventListener('message', function (event) {
|
||||
if (event.data) {
|
||||
if (event.data.iframe_height) {
|
||||
idaa_ae_iframe_height = event.data.iframe_height;
|
||||
|
||||
let idaa_ae_iframe_element = document.getElementById('ae_idaa_bb_iframe');
|
||||
let idaa_ae_iframe_element = document.getElementById('ae_idaa_bb_iframe');
|
||||
idaa_ae_iframe_element.style.height = `${idaa_ae_iframe_height + 50}px`;
|
||||
}
|
||||
|
||||
if (idaa_ae_slct_post_id) {
|
||||
// console.log(`Loading AE Post ID: ${idaa_ae_slct_post_id}`);
|
||||
// idaa_ae_iframe_element.src = `${idaa_ae_api_root_url}?uuid=${novi_customer_uid}&email=${novi_current_user_obj.Email}&full_name=${novi_current_user_obj.Name}&post_id=${idaa_ae_slct_post_id}&iframe=true&key=${idaa_osit_site_key}`;
|
||||
if (event.data.scroll_to !== undefined) {
|
||||
console.log(`Got scroll_to: ${event.data.scroll_to}`);
|
||||
|
||||
idaa_ae_iframe_element.src = `${idaa_ae_api_root_url}/${idaa_ae_slct_post_id}?uuid=${novi_customer_uid}&email=${novi_current_user_obj.Email}&full_name=${novi_current_user_obj.Name}&iframe=true&key=${idaa_osit_site_key}`;
|
||||
} else {
|
||||
idaa_ae_iframe_element.src = `${idaa_ae_api_root_url}?uuid=${novi_customer_uid}&email=${novi_current_user_obj.Email}&full_name=${novi_current_user_obj.Name}&iframe=true&key=${idaa_osit_site_key}`;
|
||||
|
||||
url.searchParams.delete('post_id');
|
||||
history.pushState({}, '', url);
|
||||
}
|
||||
|
||||
// let iframe_src = document.getElementById('iframe_src');
|
||||
// iframe_src.innerHTML = `iframe src = ${idaa_ae_iframe_element.src}`;
|
||||
})
|
||||
.catch((error) => console.log('error', error));
|
||||
|
||||
window.addEventListener('message', function (event) {
|
||||
// console.log('Message received from the child:', event.data); // Message received from child
|
||||
|
||||
if (event.data) {
|
||||
if (event.data.iframe_height) {
|
||||
idaa_ae_iframe_height = event.data.iframe_height;
|
||||
// console.log(`Got iframe height: ${idaa_ae_iframe_height}`);
|
||||
|
||||
let idaa_ae_iframe_element = document.getElementById('ae_idaa_bb_iframe');
|
||||
|
||||
// idaa_ae_iframe_element.style.height = idaa_ae_iframe_height;
|
||||
idaa_ae_iframe_element.style.height = `${idaa_ae_iframe_height + 50}px`;
|
||||
}
|
||||
|
||||
if (event.data.scroll_to !== undefined) {
|
||||
console.log(`Got scroll_to: ${event.data.scroll_to}`);
|
||||
|
||||
let idaa_ae_iframe_element = document.getElementById('ae_idaa_bb_iframe');
|
||||
if (idaa_ae_iframe_element) {
|
||||
console.log(`Scrolling to: ${event.data.scroll_to}`);
|
||||
// window.scrollTo(0, 0); // This works for all current browsers
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
} else {
|
||||
console.warn(`Element with ID "ae_idaa_bb_iframe" not found.`);
|
||||
}
|
||||
}
|
||||
|
||||
const url = new URL(location);
|
||||
|
||||
// Check if post_id is defined in the message
|
||||
if (event.data.post_id !== undefined) {
|
||||
console.log(`Got AE Post ID: ${event.data.post_id}`);
|
||||
idaa_ae_slct_post_id = event.data.post_id;
|
||||
|
||||
if (event.data.post_id) {
|
||||
url.searchParams.set('post_id', event.data.post_id);
|
||||
} else {
|
||||
url.searchParams.delete('post_id');
|
||||
}
|
||||
history.pushState({}, '', url);
|
||||
}
|
||||
|
||||
// Check if post_comment_id is defined in the message
|
||||
if (event.data.post_comment_id !== undefined) {
|
||||
console.log(`Got AE Post Comment ID: ${event.data.post_comment_id}`);
|
||||
idaa_ae_slct_post_comment_id = event.data.post_comment_id;
|
||||
|
||||
if (event.data.post_comment_id) {
|
||||
url.searchParams.set('post_comment_id', event.data.post_comment_id);
|
||||
} else {
|
||||
url.searchParams.delete('post_comment_id');
|
||||
}
|
||||
history.pushState({}, '', url);
|
||||
}
|
||||
let idaa_ae_iframe_element = document.getElementById('ae_idaa_bb_iframe');
|
||||
if (idaa_ae_iframe_element) {
|
||||
// NOTE: Scroll to the top of the iframe element, not the absolute page top.
|
||||
// The iframe is embedded below Novi's own header and navigation, so
|
||||
// scrolling to (0, 0) would show the Novi site header instead of the iframe.
|
||||
let idaa_ae_iframe_top = window.pageYOffset + idaa_ae_iframe_element.getBoundingClientRect().top;
|
||||
console.log(`Scrolling to iframe top: ${idaa_ae_iframe_top}px`);
|
||||
window.scrollTo({
|
||||
top: Math.max(0, idaa_ae_iframe_top - 20),
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
} else {
|
||||
console.log(`No data in message? ${event}`);
|
||||
console.warn(`Element with ID "ae_idaa_bb_iframe" not found.`);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
}
|
||||
|
||||
const url = new URL(location);
|
||||
|
||||
// Check if post_id is defined in the message
|
||||
if (event.data.post_id !== undefined) {
|
||||
console.log(`Got AE Post ID: ${event.data.post_id}`);
|
||||
idaa_ae_slct_post_id = event.data.post_id;
|
||||
|
||||
if (event.data.post_id) {
|
||||
url.searchParams.set('post_id', event.data.post_id);
|
||||
} else {
|
||||
url.searchParams.delete('post_id');
|
||||
}
|
||||
history.pushState({}, '', url);
|
||||
}
|
||||
|
||||
// Check if post_comment_id is defined in the message
|
||||
if (event.data.post_comment_id !== undefined) {
|
||||
console.log(`Got AE Post Comment ID: ${event.data.post_comment_id}`);
|
||||
idaa_ae_slct_post_comment_id = event.data.post_comment_id;
|
||||
|
||||
if (event.data.post_comment_id) {
|
||||
url.searchParams.set('post_comment_id', event.data.post_comment_id);
|
||||
} else {
|
||||
url.searchParams.delete('post_comment_id');
|
||||
}
|
||||
history.pushState({}, '', url);
|
||||
}
|
||||
} else {
|
||||
console.log(`No data in message? ${event}`);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<!-- STOP: Do not copy below this point -->
|
||||
|
||||
<!-- IDAA Aether Apps for Novi - Bulletin Board (posts) iframe -->
|
||||
<p>
|
||||
<iframe
|
||||
width="100%"
|
||||
height="750"
|
||||
id="ae_idaa_bb_iframe"
|
||||
src=""
|
||||
style="min-height: 600px; max-height: 100%"
|
||||
class="ae_idaa_iframe"
|
||||
></iframe>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,152 +3,111 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Novi Test Page</title>
|
||||
<title>IDAA Novi Recovery Meetings iframe Example Template Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
let novi_customer_uid = '<%=Novi.User.CustomerUniqueId%>'; // NOTE: The Novi UUID for the current current user/customer
|
||||
console.log(`Novi's Current User's ID: ${novi_customer_uid}`);
|
||||
|
||||
// var novi_current_user_role = '<%=Novi.User.xyz%>';
|
||||
// console.log(`Novi's Current User's Role: ${novi_current_user_role}`);
|
||||
<!-- README: This is an example template page for embedding the IDAA Recovery Meetings iframe in the IDAA Novi site. Copy the code below to use it in your own page(s). -->
|
||||
|
||||
let novi_api_root_url = 'https://www.idaa.org/api';
|
||||
let novi_api_key_for_idaa = 'CmNdWgdPmgluBWjiTd8xsUCk5mio8F1O9DYAh0pVDcg=';
|
||||
<!-- START: Copy below this point -->
|
||||
<!-- IMPORTANT: The <p> and <script> elements below are for using in an iframe in the IDAA Novi site. -->
|
||||
<!-- IDAA OSIT iframe container element for Novi - Recovery Meetings iframe -->
|
||||
<p>
|
||||
<iframe
|
||||
width="100%"
|
||||
height="750"
|
||||
id="ae_idaa_recovery_meetings_iframe"
|
||||
src=""
|
||||
style="min-height: 600px; height: min-content; max-height: 2048px"
|
||||
class="ae_idaa_iframe"
|
||||
></iframe>
|
||||
</p>
|
||||
<!-- IDAA and Novi specific JavaScript to get current Novi user info and load Recovery Meetings iframe -->
|
||||
<script>
|
||||
// NOTE: The Novi UUID for the current user — injected server-side by Novi.
|
||||
// This is the only identity value passed to the iframe. Identity verification
|
||||
// is handled securely by the OSIT server — do not add email or name here.
|
||||
let novi_customer_uid = '<%=Novi.User.CustomerUniqueId%>';
|
||||
console.log(`Novi's Current User's ID: ${novi_customer_uid}`);
|
||||
|
||||
let novi_current_user_obj = null;
|
||||
let novi_current_user_email = null;
|
||||
// WARNING: Do *not* use relative paths here. They must be direct to the site OSIT is hosting for IDAA.
|
||||
let idaa_osit_ae_api_root_url = 'https://dev-idaa.oneskyit.com/idaa/recovery_meetings'; // NOTE: DO NOT CHANGE THIS VALUE
|
||||
// Example URLs: 'https://sk-idaa.oneskyit.com/idaa/recovery_meetings' OR 'https://dev-idaa.oneskyit.com/idaa/recovery_meetings'
|
||||
|
||||
let idaa_osit_site_key = '8VTOJ0X5hvT6JdiTJsGEzQ'; // 'restricted-access'
|
||||
let idaa_ae_api_root_url = 'https://sk-idaa.oneskyit.com/idaa/recovery_meetings';
|
||||
let idaa_ae_params = new URLSearchParams(document.location.search);
|
||||
let idaa_ae_slct_event_id = idaa_ae_params.get('event_id');
|
||||
// WARNING: Do *not* change this value. It is required for access control to the IDAA AE API.
|
||||
let idaa_osit_ae_site_key = 'restricted-access'; // DO NOT CHANGE THIS VALUE
|
||||
|
||||
let idaa_ae_iframe_height = null;
|
||||
let idaa_ae_params = new URLSearchParams(document.location.search);
|
||||
let idaa_ae_slct_event_id = idaa_ae_params.get('event_id');
|
||||
|
||||
var novi_api_headers = new Headers();
|
||||
novi_api_headers.append('Authorization', `Basic ${novi_api_key_for_idaa}`);
|
||||
let idaa_ae_iframe_height = null;
|
||||
|
||||
var requestOptions = {
|
||||
method: 'GET',
|
||||
headers: novi_api_headers,
|
||||
redirect: 'follow'
|
||||
};
|
||||
let idaa_ae_iframe_element = document.getElementById('ae_idaa_recovery_meetings_iframe');
|
||||
|
||||
let novi_api_get_customer_endpoint = `${novi_api_root_url}/customers/${novi_customer_uid}`;
|
||||
console.log(novi_api_get_customer_endpoint);
|
||||
if (idaa_ae_slct_event_id) {
|
||||
idaa_ae_iframe_element.src = `${idaa_osit_ae_api_root_url}/${idaa_ae_slct_event_id}?uuid=${novi_customer_uid}&iframe=true&key=${idaa_osit_ae_site_key}`;
|
||||
} else {
|
||||
idaa_ae_iframe_element.src = `${idaa_osit_ae_api_root_url}?uuid=${novi_customer_uid}&iframe=true&key=${idaa_osit_ae_site_key}`;
|
||||
}
|
||||
|
||||
fetch(novi_api_get_customer_endpoint, requestOptions)
|
||||
// .then(response => response.text())
|
||||
.then((response) => response.json())
|
||||
.then((result) => {
|
||||
novi_current_user_obj = result;
|
||||
// console.log(`Novi's Current User Obj (${novi_current_user_obj.Email}):`, novi_current_user_obj);
|
||||
// NOTE: This listener handles messages sent from the IDAA iframe back to this parent page.
|
||||
// It adjusts the iframe height dynamically, scrolls the page when navigation occurs inside
|
||||
// the iframe, and keeps the browser URL in sync with navigation inside the iframe.
|
||||
window.addEventListener('message', function (event) {
|
||||
if (event.data) {
|
||||
if (event.data.iframe_height) {
|
||||
idaa_ae_iframe_height = event.data.iframe_height;
|
||||
|
||||
let idaa_ae_iframe_element = document.getElementById(
|
||||
'ae_idaa_recovery_meetings_iframe'
|
||||
);
|
||||
let idaa_ae_iframe_element = document.getElementById(
|
||||
'ae_idaa_recovery_meetings_iframe'
|
||||
);
|
||||
idaa_ae_iframe_element.style.height = `${idaa_ae_iframe_height + 50}px`;
|
||||
}
|
||||
|
||||
if (idaa_ae_slct_event_id) {
|
||||
// console.log(`Loading AE Archive ID: ${idaa_ae_slct_event_id}`);
|
||||
// idaa_ae_iframe_element.src = `${idaa_ae_api_root_url}?uuid=${novi_customer_uid}&email=${novi_current_user_obj.Email}&full_name=${novi_current_user_obj.Name}&event_id=${idaa_ae_slct_event_id}&iframe=true&key=${idaa_osit_site_key}`;
|
||||
if (event.data.scroll_to !== undefined) {
|
||||
console.log(`Got scroll_to: ${event.data.scroll_to}`);
|
||||
|
||||
idaa_ae_iframe_element.src = `${idaa_ae_api_root_url}/${idaa_ae_slct_event_id}?uuid=${novi_customer_uid}&email=${novi_current_user_obj.Email}&full_name=${novi_current_user_obj.Name}&iframe=true&key=${idaa_osit_site_key}`;
|
||||
} else {
|
||||
idaa_ae_iframe_element.src = `${idaa_ae_api_root_url}?uuid=${novi_customer_uid}&email=${novi_current_user_obj.Email}&full_name=${novi_current_user_obj.Name}&iframe=true&key=${idaa_osit_site_key}`;
|
||||
|
||||
url.searchParams.delete('event_id');
|
||||
history.pushState({}, '', url);
|
||||
}
|
||||
|
||||
// let iframe_src = document.getElementById('iframe_src');
|
||||
// iframe_src.innerHTML = `iframe src = ${idaa_ae_iframe_element.src}`;
|
||||
})
|
||||
.catch((error) => console.log('error', error));
|
||||
|
||||
window.addEventListener('message', function (event) {
|
||||
// console.log('Message received from the child:', event.data); // Message received from child
|
||||
|
||||
if (event.data) {
|
||||
if (event.data.iframe_height) {
|
||||
// console.log(`Got iframe height: ${event.data.iframe_height}`);
|
||||
idaa_ae_iframe_height = event.data.iframe_height;
|
||||
|
||||
let idaa_ae_iframe_element = document.getElementById(
|
||||
'ae_idaa_recovery_meetings_iframe'
|
||||
);
|
||||
|
||||
// idaa_ae_iframe_element.style.height = idaa_ae_iframe_height;
|
||||
idaa_ae_iframe_element.style.height = `${idaa_ae_iframe_height + 50}px`;
|
||||
}
|
||||
|
||||
if (event.data.scroll_to !== undefined) {
|
||||
console.log(`Got scroll_to: ${event.data.scroll_to}`);
|
||||
|
||||
let idaa_ae_iframe_element = document.getElementById(
|
||||
'ae_idaa_recovery_meetings_iframe'
|
||||
);
|
||||
if (idaa_ae_iframe_element) {
|
||||
console.log(`Scrolling to: ${event.data.scroll_to}`);
|
||||
// window.scrollTo(0, 0); // This works for all current browsers
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
// idaa_ae_iframe_element.scrollTo({x: 0, y: 0}); // Scroll to top
|
||||
// document.body.scrollTo({x: 0, y: 0}); // Scroll to top
|
||||
// document.body.scrollTo({
|
||||
// top: 0,
|
||||
// behavior: 'smooth'
|
||||
// });
|
||||
// document.body.scrollTop = 0;
|
||||
// idaa_ae_iframe_element?.scrollTo(0, 0)
|
||||
// idaa_ae_iframe_element.scrollTo({
|
||||
// top: 0,
|
||||
// behavior: 'smooth'
|
||||
// });
|
||||
// idaa_ae_iframe_element.scrollTo({
|
||||
// top: event.data.scroll_to,
|
||||
// behavior: 'smooth'
|
||||
// });
|
||||
} else {
|
||||
console.warn(
|
||||
`Element with ID "ae_idaa_recovery_meetings_iframe" not found.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const url = new URL(location);
|
||||
|
||||
// Check if event_id is defined in the message
|
||||
if (event.data.event_id !== undefined) {
|
||||
console.log(`Got AE Events ID: ${event.data.event_id}`);
|
||||
idaa_ae_slct_event_id = event.data.event_id;
|
||||
|
||||
if (event.data.event_id) {
|
||||
url.searchParams.set('event_id', event.data.event_id);
|
||||
} else {
|
||||
url.searchParams.delete('event_id');
|
||||
}
|
||||
history.pushState({}, '', url);
|
||||
}
|
||||
let idaa_ae_iframe_element = document.getElementById(
|
||||
'ae_idaa_recovery_meetings_iframe'
|
||||
);
|
||||
if (idaa_ae_iframe_element) {
|
||||
// NOTE: Scroll to the top of the iframe element, not the absolute page top.
|
||||
// The iframe is embedded below Novi's own header and navigation, so
|
||||
// scrolling to (0, 0) would show the Novi site header instead of the iframe.
|
||||
let idaa_ae_iframe_top = window.pageYOffset + idaa_ae_iframe_element.getBoundingClientRect().top;
|
||||
console.log(`Scrolling to iframe top: ${idaa_ae_iframe_top}px`);
|
||||
window.scrollTo({
|
||||
top: Math.max(0, idaa_ae_iframe_top - 20),
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
} else {
|
||||
console.log(`No data in message? ${event}`);
|
||||
console.warn(
|
||||
`Element with ID "ae_idaa_recovery_meetings_iframe" not found.`
|
||||
);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
}
|
||||
|
||||
const url = new URL(location);
|
||||
|
||||
// Check if event_id is defined in the message
|
||||
if (event.data.event_id !== undefined) {
|
||||
console.log(`Got AE Events ID: ${event.data.event_id}`);
|
||||
idaa_ae_slct_event_id = event.data.event_id;
|
||||
|
||||
if (event.data.event_id) {
|
||||
url.searchParams.set('event_id', event.data.event_id);
|
||||
} else {
|
||||
url.searchParams.delete('event_id');
|
||||
}
|
||||
history.pushState({}, '', url);
|
||||
}
|
||||
} else {
|
||||
console.log(`No data in message? ${event}`);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<!-- STOP: Do not copy below this point -->
|
||||
|
||||
<!-- IDAA Aether Apps for Novi - Recovery Meetings (events) iframe -->
|
||||
<p>
|
||||
<iframe
|
||||
width="100%"
|
||||
height="750"
|
||||
id="ae_idaa_recovery_meetings_iframe"
|
||||
src=""
|
||||
style="min-height: 600px; height: min-content; max-height: 2048px"
|
||||
class="ae_idaa_iframe"
|
||||
></iframe>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user