Permit-iframe hardening: - public/embed.php — replace the 302 redirect on unauthenticated visits with an in-iframe HTML "Sign in to view the map" card (HTTP 401) whose primary button uses target="_top" to break the iframe and send the parent window to the SSO portal. The 302 was broken UX inside an iframe because the LUSPA portal refuses to be framed. - public/embed.php + public/.htaccess — strip X-Frame-Options at the embed endpoint (defence in depth). Apache's <Files "embed.php"> Header always unset X-Frame-Options + PHP's header_remove() both ensure the only iframe-policy header on the response is our CSP frame-ancestors (which already allows the permits subdomain). Fixes Safari's "Refused to display ... because it set 'X-Frame-Options' to 'SAMEORIGIN'" when the container's reverse proxy injects it. Import UX refinements: - Spinner overlay (index.html #import-spinner-overlay + main.js showImportSpinner/hideImportSpinner) shown during the file-drop → mapping-modal gap. Wired at the top of each handle*Import and at every error / early-return path; hidden by stageImport() just before openImportMappingModal() so it spans both the JS parse and the SQLocal staging insert. - Per-feature client_uuid tagging — each imported OL feature now carries _externalImportId + _clientUuid set in stageImport(). These tags are the link that lets later edits find the matching staging row, and they are passed through to addExternalImportFeatures. - Geometry-edit persistence — new public callback registry MapView.onFeatureModified(cb) fired from a modifyend listener on _modifyInteraction. main.js handler writes the new WKT (EPSG:4326) back to external_import_features.geometry_wkt via new helper updateExternalImportFeatureGeometry(clientUuid, wkt). Non-imported features carry no tags, so the handler is a no-op for them. - Delete persistence — removefeature listener on each imported layer's source. New helper deleteExternalImportFeature(clientUuid) runs an atomic DELETE + decrement of external_imports.feature_count and broadcasts the changes so the LayerSwitcher badge can recount. - Field-mapping dropdown — sample values + bold field names. New helpers sampleSourceValues(fc) in import-detect.js (picks first non-empty value per attribute, JSON-stringifies objects, collapses whitespace, truncates to 35 chars) and toBoldUnicode(s) in import-modal.js (ASCII letters/digits → Mathematical Alphanumeric Symbols block). Options now read as "𝐮𝐩𝐧 — [12345-6789]"; HTML/CSS bold doesn't render inside <option> elements, so Unicode bold codepoints are the cross-browser way. Workshop deliverables: - LUPMIS2_Improvements_Mar_to_Jun_2026.docx — handout mirroring the slide deck one-to-one (160 paragraphs, branded styling). - LUPMIS2_Workshop_Mar_to_Jun_2026.pptx — 16-slide pptxgenjs deck (16:9 widescreen, brand palette, hero + content + closing masters, embedded staged-upload diagram on slide 9). - LUPMIS2_Staged_Upload_Flow.svg + .png — three swim-lane diagram of the staged-upload pipeline with a dedicated "Client QA Gate" callout. Hand-crafted SVG + 2400 px PNG. save_gps_trail.php diagnosis (no code change, on the database team): the reported "CORS" error is a missing endpoint — Apache returns 404 with no CORS headers and the browser surfaces it as access-control. Once the endpoint is deployed the API server's global CORS handling attaches the right headers and the GPS-trail sync will work without client changes. dist/ rebuilt. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
48 lines
2.1 KiB
ApacheConf
48 lines
2.1 KiB
ApacheConf
# ============================================================================
|
|
# LUPMIS2 PWA — Apache config
|
|
# ============================================================================
|
|
|
|
# Apache's default DirectoryIndex order serves index.html before index.php.
|
|
# We need the opposite so the SSO-aware index.php gets a chance to run first,
|
|
# inject session data into the page, and then return the index.html content.
|
|
DirectoryIndex index.php index.html
|
|
|
|
# Make sure .php files are executed (defensive — usually enabled site-wide,
|
|
# but explicit here in case the deployment dropped this association).
|
|
<FilesMatch "\.php$">
|
|
SetHandler application/x-httpd-php
|
|
</FilesMatch>
|
|
|
|
<IfModule mod_rewrite.c>
|
|
RewriteEngine On
|
|
|
|
# Clean URL for the iframe embed endpoint: /embed → embed.php
|
|
# Must come BEFORE the SPA fallback so /embed doesn't get routed to
|
|
# index.php. Query strings (?mode=permit&...) pass through automatically.
|
|
RewriteRule ^embed/?$ embed.php [L]
|
|
|
|
# Common single-page-app behaviour: if a route doesn't map to a real file
|
|
# or directory, send the request to index.php so the PWA can handle it
|
|
# client-side. Comment out this block if hash-based routing is preferred.
|
|
RewriteCond %{REQUEST_FILENAME} !-f
|
|
RewriteCond %{REQUEST_FILENAME} !-d
|
|
RewriteRule ^ index.php [L]
|
|
</IfModule>
|
|
|
|
# Iframe-policy override for the embed endpoint. Some Apache deployments set
|
|
# `X-Frame-Options: SAMEORIGIN` as a default security header for every
|
|
# response — that prevents `permits.lupmis4luspa.org` from framing
|
|
# `pwa.lupmis4luspa.org/embed`, even though our Content-Security-Policy
|
|
# `frame-ancestors` directive explicitly allows it. Safari prefers
|
|
# `X-Frame-Options` when both are present, so we have to remove it.
|
|
#
|
|
# We unset it ONLY for embed.php (so index.php still inherits the
|
|
# site-wide SAMEORIGIN protection against clickjacking). embed.php's own
|
|
# `Content-Security-Policy: frame-ancestors` header (set in PHP) is then
|
|
# the sole iframe-policy header and permits the configured embedder.
|
|
<IfModule mod_headers.c>
|
|
<Files "embed.php">
|
|
Header always unset X-Frame-Options
|
|
</Files>
|
|
</IfModule>
|