Commit graph

2046 commits

Author SHA1 Message Date
Peter Goodhall
8e741b5d36 Improve search with exact match and recent history
Refactors the callsign/gridsquare search to support exact match toggling, partial/broad match fallback, and a recent searches history stored in localStorage. Search terms are now passed via GET parameters (bookmarkable URLs) instead of POST. The advanced filter panel gains result count badges, a clear button, and persists query rules across page loads. DataTable initialization is extracted into reusable helpers.
2026-08-06 15:05:04 +01:00
Peter Goodhall
4fe8ca4093 Handle AMSAT override for OscarWatch
Add a persisted `force_amsat` OscarWatch user option, expose it on the user edit form, and keep the AMSAT upload setting in sync when OscarWatch status uploads are enabled. The controller now defaults unchecked values correctly and shows a notice when OscarWatch disables direct AMSAT uploads or when the override keeps both enabled.
2026-08-04 13:15:01 +01:00
Peter Goodhall
9e1b10169e Add OscarWatch status upload controls
Adds full user-setting support for OscarWatch SAT status uploads: new per-user `status_upload` option initialization, save/load wiring, and session hydration. The user edit page now includes an enable/disable selector plus a “Test Token” action that calls a new `validate_oscarwatch_token` controller endpoint to verify tokens against OscarWatch and return clear JSON status messages. Logbook SAT upload logic is updated so AMSAT and OscarWatch uploads are handled independently based on their respective user toggles.
2026-08-03 14:51:19 +01:00
Peter Goodhall
8e0e28cd84 Add OscarWatch SAT status upload support
This adds optional OscarWatch integration for SAT QSOs: users can store an OscarWatch API token in account settings, and Cloudlog now reports SAT status uploads to OscarWatch alongside AMSAT uploads when enabled. It includes payload building, mode remapping for common satellites/modes, timestamp/grid handling, and error logging for failed submissions. The edit flow was also updated to load/save these options for the correct edited user, and the account label was generalized from “AMSAT Status Upload” to “Satellite Status Upload.”
2026-08-03 14:45:59 +01:00
Peter Goodhall
0c9c48779f Notify user on Clublog credential reset
When Clublog returns HTTP 403 (access denied), automatically clear the stored credentials and optionally send the user an email notification with context about the failure. Also sets a session flash warning when the affected user is logged in. Adds an email view template for the notification.
2026-08-02 22:32:50 +01:00
Peter Goodhall
0ee50ba627 Add Clublog upload station toggle and warning
Introduces a new `station_profile.clublogcron` flag (migration 276) and wires it into station create/edit save flows so users can explicitly enable locations for `/clublog/upload`. Clublog station selection now only includes enabled locations (with fallback to `clublogrealtime` if the new column is not present), and the dashboard shows a warning when Clublog credentials exist but no station is enabled. It also tightens Clublog upload-status query grouping and fixes `Clublog::uploadall` return flow so upload results are returned consistently.
2026-08-02 22:21:27 +01:00
Peter Goodhall
984bfde498 Improve DXCC awards filtering and map legend
- Fix returnStatus() to evaluate all band statuses before returning, so 'C' takes priority over 'W' across all bands instead of returning on first match
- Add band preset buttons (HF Only, VHF/UHF+, SAT Only, All, Clear) to the DXCC filter form
- Update map legend to show total worked count (confirmed + unconfirmed) alongside the unconfirmed breakdown
2026-07-21 13:09:12 +01:00
Peter Goodhall
15c8947bdf Pass year and eQSL filters to DXCC map
Wires missing DXCC map filters through the request pipeline. The frontend now submits `year` and `eqsl` with the map AJAX payload, and the Awards controller now reads/sanitizes `year` into `postdata` so year-based filtering can be applied server-side.
2026-07-20 22:10:04 +01:00
Peter Goodhall
e5ce621734 Replace band dropdown with multi-select checkboxes
Replaces the single band dropdown on the DXCC awards page with a group of checkboxes allowing multiple bands to be selected simultaneously.

- Controller and model updated to accept `bands[]` array instead of a single `band` value
- `addBandToQuery()` in Dxcc model now handles array input, building appropriate IN clauses and SAT propagation mode logic
- `returnStatus()` priority fixed: Confirmed (C) now takes precedence over Worked (W); unworked entries now return 'x' instead of null
- JS updated to collect checked band values; map and filter reset functions updated accordingly
- Uninitialized `$newdxcc`/`$newiota` arrays now explicitly initialized before use
2026-07-20 22:08:53 +01:00
Peter Goodhall
f36c9a686d Improve eQSL mapping error messages
Add granular failure reason tracking to Eqsl_mappings_model via last_failure_reason and last_db_error properties. Controller now differentiates between encryption failures, schema migration errors (DB error 1406), and generic DB errors, providing more actionable user-facing messages and structured error logging.
2026-07-18 23:10:06 +01:00
Peter Goodhall
ef263eb65b qsl bits 2026-07-18 23:06:37 +01:00
Peter Goodhall
45220b3360 moar eqsl 2026-07-18 23:00:08 +01:00
Peter Goodhall
1f7c130c46 Encrypt stored eQSL passwords at rest
Adds migration 275 to convert eQSL password columns to TEXT and encrypt existing user/mapping passwords with an `enc:` prefix. Updates eQSL controller/model flows to decrypt on read, encrypt on create/update, and keep plaintext backward compatibility for legacy rows. User profile updates now avoid reusing stored ciphertext when no new password is submitted, and mapping updates report a clear error if secure password storage fails.
2026-07-18 22:53:51 +01:00
Peter Goodhall
4b063e7fe0 Small eqsl changes 2026-07-18 22:31:33 +01:00
Peter Goodhall
9c0b616e6e Update Eqsl.php 2026-07-18 22:29:58 +01:00
Peter Goodhall
5e29c66fba Allow blank password reuse in eQSL mappings
When editing or creating an eQSL mapping, the password field can now be left blank. Cloudlog will reuse an existing saved password for the same eQSL username. A password is only required when no saved password exists for that username. The mappings form hint text and docs are updated accordingly.
2026-07-18 22:13:27 +01:00
Peter Goodhall
840f52dc4b Adds improved eqsl functionality 2026-07-18 22:01:33 +01:00
Peter Goodhall
4efaffa882 Show callbook tab only for supported sources
Gate the QSO callbook tab on the configured callbook provider instead of the profile image setting. The controller now exposes a `show_callbook_tab` flag when the user's callbook type is `HAMQTH` or `QRZ`, and the view uses that flag for both the tab button and pane.
2026-07-17 14:39:33 +01:00
Peter Goodhall
35369a0996 Merge activator call variants
Canonicalize activator callsigns and combine results for variant forms such as portable suffixes and MM/MW prefixes. This updates activator listings, max-grid counts, and contact details to aggregate grids across matching calls while keeping the table output consistently sorted.
2026-07-17 13:49:42 +01:00
Peter Goodhall
53019ade11 Add satellite orbit filters to grid maps
Extends Gridmap and Activated Gridmap to accept a new `sat_orbit` filter and applies it across worked/confirmed grid and VUCC queries, including SAT-specific SQL filtering for LEO, MEO (IO-117), and GEO (QO-100). Updates the gridmap UI to show/hide orbit controls with SAT selection, sends the new filter in AJAX requests, and adds activated-grid summary cards with worked/confirmed counts and satellite class breakdown data from new model methods.
2026-07-17 13:44:06 +01:00
Peter Goodhall
3e944f71cc Normalize location lists and sanitize SQL
Replace fragile quoted-implode station lists with normalized integer CSVs and where_in usage to ensure station_id lists are numeric. Add normalize_location_list helpers in multiple models (Lookup_model, Sig, Timeline_model) and update controllers to cast inputs (e.g. dxcc_id) to ints. Escape user-supplied SQL fragments with $this->db->escape_str/escape_like_str and sanitize band/mode parameters; consolidate band/mode filtering into add_band_mode_filters in Timeline_model. Add guards for empty location lists (returning empty results) and a method_exists check around a legacy vucc_shit call. Overall this improves input validation and reduces SQL injection risk while removing duplicated list-building logic.
2026-06-24 22:29:00 +01:00
Peter Goodhall
c6bdee1bc5 Sanitize location lists and harden SQL
Replace string-quoted station_id lists with comma-separated, integer-casted lists and harden SQL across awards models. This diff converts constructions like "'a','b'" to implode(',', array_map('intval', ...)) and uses $this->db->escape_str() for band/mode values and band lists to reduce injection/format issues. Added helper methods (addModeToQuery, addBandToQuery variants) to centralize mode/band filtering, parameterized time/mode/band queries in Gmdxsummer_model, and escaped band arrays when building IN() lists. Also added null/empty-checks for logbook arrays in several models and small refactors (e.g. VUCC addBandToQuery) to keep SQL building consistent.
2026-06-24 22:26:11 +01:00
Peter Goodhall
4440582389 Sanitize IDs and migrate SQL to Query Builder
Replace ad-hoc xss_clean calls with explicit casting and stronger type checks for ID/parameter handling, and convert many raw SQL strings to CodeIgniter Query Builder usage. Added normalize_location_ids helpers (Activators_model, Oqrs_model) to safely parse location lists and used where_in/parameter binding/escaping to avoid injection and improve maintainability. Also adjusted session user_id handling, improved LIKE/DATE/TIMEDIFF usage, and tightened several model/controller methods (Labels, Activators, Bands, Contesting, Labels_model, Modes, Oqrs_model, Qsl_model, Setup_model, Sstv_model, User_model) for safer, clearer DB queries and inputs.
2026-06-24 22:15:31 +01:00
Peter Goodhall
f5e3aac0d0 Cast IDs to int; use query builder
Normalize incoming ID parameters to integers and replace concatenated/raw SQL with CodeIgniter query builder. Controllers (Contesting.php, Themes.php) now cast $id to (int) instead of using xss_clean; models (Contesting_model.php, Themes_model.php) cast $id and use $this->db->where()/get()/select() to build queries. This improves type safety and reduces risk of injection while using more idiomatic CI DB APIs.
2026-06-24 22:08:30 +01:00
Peter Goodhall
5b3272eafc Return upload status and report no Clublog QSOs
Track and return whether any QSOs were actually uploaded to Clublog and log/report when none are pending. index(): introduced $uploaded_any_qsos and aggregate per-user upload results; if nothing uploaded, echo a message and write an info log. uploadUser(): return false on invalid Clublog username (was previously void); added $uploaded_for_user tracking while iterating station profiles and set it when Clublog responds with 'accepted'. uploadUser now returns true when QSOs were uploaded for the user, otherwise false.
2026-05-28 13:42:41 +01:00
Peter Goodhall
0e0244dd73 Resolve file path for Clublog uploads
Use FCPATH to build the upload file path and fall back to DOCUMENT_ROOT plus the configured install directory if the file is missing. Normalizes leading/trailing slashes and casts values to strings to avoid notices, and checks file existence before attempting the fallback. This improves robustness when the app is installed in a subdirectory or when DOCUMENT_ROOT is not set.
2026-05-28 13:40:47 +01:00
Peter Goodhall
3ca30c161a Remove redundant qso['lng'] assignments
Delete duplicate qso['lng'] assignments in application/controllers/Api.php. Longitude is stored in qso['long'], so removing the extra qso['lng'] assignments (from plot_latlng and dxcc fallback) avoids redundant/conflicting keys and keeps the QSO data consistent.
2026-05-18 22:02:40 +01:00
Peter Goodhall
318d9a4c6f Prefer VUCC grids for gridsquare and coords
Prefer COL_VUCC_GRIDS over COL_GRIDSQUARE when populating the gridsquare field, and compute plotting coordinates from the grid (via qralatlng) to support VUCC multi-grid line/corner records. Populate lat, long and lng from the grid-derived coordinates, and only fall back to DXCC table coordinates if grid-derived values are not present, avoiding accidental overrides.
2026-05-18 22:00:22 +01:00
Peter Goodhall
769837ce09 Apply user remote_operation option and messaging
Load and interpret the 'remote_operation' user option when no POST value is provided, converting stored 'true'/'1' to 1 and falling back to the existing database value or 0. Ensure post_data always has a default 'user_remote_operation' of '0'. Use a strict check on $post_data['user_remote_operation'] === '1' when persisting the option via user_options_model, and append a concise enabled/disabled status to the success flash message after editing the user.
2026-05-16 16:01:12 +01:00
Peter Goodhall
c9e0761383 Persist remote operation secret to account
Add server endpoints to store and retrieve the remote operation link password in the user's account and update the client to use them. Qso controller: added remoteoperationsecret_json and remoteoperationsecret_save which use user_options_model and CI encryption to get/set an encrypted link_password (save enforces a minimum 16-char password, empty value clears it). View and JS: default WebSocket URL changed to wss://relay.cloudlog.org/ws-webrtc; client no longer stores link password in localStorage and instead loads/saves it via the new endpoints (with error handling and UI feedback). Also clear the account secret on reset and stop exposing the password in browser storage.
2026-05-16 13:55:11 +01:00
Peter Goodhall
7c0c7515a5 Add experimental Remote Operation feature
Introduce an experimental browser-based remote audio feature: bump migration version to 270 and add a migration to add a remote_operation flag to the users table. Add server-side support to User and QSO controllers/models to read/save the remote_operation option (stored via user_options_model) and expose isRemoteOperationEnabled to views and session updates. Add UI: a Remote Operation card on the QSO page, a modal component for detailed settings, a toggle in the user edit page, and conditional loading of assets/js/remote-operation.js. Add a large client-side implementation (assets/js/remote-operation.js) implementing WebRTC signalling, device selection, level meters and diagnostics. Minor session/session-update and helper changes to keep UI state in sync.
2026-05-16 13:44:09 +01:00
Peter Goodhall
66e81bbdc4 Add TEVEL2 satellites and migration
Bump migration version to 269, add TEVEL2-1..TEVEL2-9 entries to the Lotw satellite mapping, and add migration 269_set_tevel2_series_to_notsent. The migration sets COL_LOTW_QSL_SENT = 'N' for TEVEL2-1 through TEVEL2-9 so their QSOs are marked not-sent to LoTW; down() is intentionally empty since previous per-QSO sent state cannot be safely restored.
2026-05-16 11:45:03 +01:00
Peter Goodhall
cc7593c892 Add WinKey relay settings API and UI persistence
Introduce server endpoints to get/save WinKey WebSocket relay settings and token (winkeyrelaysettings_json, winkeyrelaysettings_save, winkeyrelaytoken_json, winkeyrelaytoken_save) using the user_options_model. Add client-side changes to load settings from the account (with a localStorage fallback), save settings to the account, validate URL/token (ws:// or wss:// and token ≥ 8 chars), and clear legacy localStorage keys after a successful save. Defer WebSocket connect until account settings are loaded and update UI text to reflect that relay settings are stored in the user account and follow login across devices.
2026-05-15 14:19:48 +01:00
Peter Goodhall
0413d8a6bf Refactor dashboard helpers and improve UX
Move repeated table rendering helpers into a new application/helpers/dashboard_helper.php and load it from the Dashboard controller to remove duplication in views. Improve mobile/responsive dashboard behavior: detect mobile user agent to reduce recent-QSO rows, add mobile-specific quick links, collapse cards for compact display, and adjust HTMX polling intervals and targets. Add htmx loading indicators and last-updated timestamps with a small JS handler, and include minor CSS tweaks. Also add a session validation early-return in radio_display_component and load the user_agent library in the controller.
2026-05-07 22:12:05 +01:00
Peter Goodhall
e25714b37a Update GMDX Summer Challenge to 2026
Advance the GMDX Summer Challenge event to 2026: update controller week end timestamps, and update view copy and table dates/entry link to reflect the new event window (11 May – 5 July 2026). In the model introduce START_DATE (2026-05-11), add validation to ignore empty/null COL_GRIDSQUARE values, and change combined-count logic to normalize gridsquares and group modes into CW / VOICE / DIGITAL so counts are consistent and case-insensitive.
2026-05-06 10:33:35 +01:00
Peter Goodhall
177f605183 Add year filter to DXCC awards
Add a year filter to the DXCC awards UI and backend. Controller: populate worked_years for the view and read/sanitize a year POST value (default 'All'). Model: introduce addYearToQuery() to append a YEAR(col_time_on) clause to existing DXCC queries and call it in relevant query paths; add get_worked_years() to return distinct worked years for the active logbook locations. View: add a year select dropdown to the DXCC awards form. This enables narrowing DXCC results by year.
2026-05-03 22:48:10 +01:00
Peter Goodhall
e6f6dd2265 Disable caching for radio JSON; use $.ajax
Add no-cache HTTP headers in Radio::json to prevent cached responses. Replace client-side $.getJSON with $.ajax(url, {dataType:'json', cache:false}) and preserve the stale-response checks while updating lastProcessedCatRequest only when a response is applied. Also ensure login errors return early. These changes ensure fresh CAT data and avoid stale or cached responses affecting the UI.
2026-05-01 10:22:11 +01:00
Peter Goodhall
fd49056a98 Enforce plugin read-only policy and show alerts
Add server-side read-only policy checks for plugins and surface security alerts to admins. Plugin_manager now validates plugin code (collects PHP files and scans for forbidden file/system/exec functions) during install and before enabling; installs are blocked or plugins auto-disabled on violation with a security alert returned. Plugins controller updated to use session validation for login, enforce admin (user_type 99) access, store/display security alerts via a flash session key, and pass a CSRF token to the view. The plugins index view now renders a warning banner for plugin security alerts. Documentation updated to link and include a new QSO Hooks plugin guide for third-party sync plugins.
2026-04-30 23:03:29 +01:00
Peter Goodhall
264d6f0b0b Add plugin award menu and settings
Support plugin-provided awards and user-configurable visibility. Controller now loads plugin entries and user options, exposes plugin_award_entries and plugin_award_visibility to views, and adds savePluginAward() to validate input and persist visibility via user_options_model. Views: settings page renders plugin award rows with checkboxes; header only shows plugin awards that are enabled. JS: award_settings.js and awards.js handle plugin award checkbox events, call the new savePluginAward endpoint, and provide saving/feedback UI. Includes input sanitization, slug validation, and preserves existing core award flows.
2026-04-29 22:40:24 +01:00
Peter Goodhall
4cfab6fe4e Add plugin system and Plugin Manager
Introduce a plugin framework and management UI: adds Plugin Manager controller, Plugin_awards controller, Plugin_manager and Cloudlog_hooks libraries, Plugins_model, migration (268) to create the plugins table, and views for plugin manager and award pages. Integrates hooks into Logbook_model (qso.filter.before_save, qso.action.after_save, qso.action.after_edit), updates header to show award plugin entries and a Plugin Manager menu link, and bumps migration_version to 268. Also adds .gitignore rules, plugin index placeholder, docs and example plugin packages. The Plugin Manager supports uploading/installing ZIP packages, safe extraction, manifest validation, enable/disable/delete actions, and CSRF protection.
2026-04-29 22:33:22 +01:00
Peter Goodhall
aa636697bd Add HTMX component and filters for EME initials
Introduce an HTMX-driven filtering UI and a server-side component for EME initials. The controller Emeinitials.php was simplified (removed inline POST handling in index) and now exposes component_eme_results() which accepts band/mode (defaulting to 'All'), determines the user's date format, loads initials via Emeinitials_model, and returns a partial view. A new view emeinitales/component_results.php renders the results table or a no-data alert. The index view was refactored to an HTMX-enabled filters form, async results container (#emeResults), a Reset button, and a small client-side handler to reset and re-submit the form. These changes enable dynamic filtering without a full page reload and clean up server-side responsibilities.
2026-04-26 22:20:45 +01:00
Peter Goodhall
85210aa2ae Add HTMX continent component and refactor JS
Introduce an HTMX-driven continents results component and refactor the continents page/JS to load/filter results dynamically. Added Continents::component_continent_results() and a new view (continents/component_results.php) which renders the chart/table container and exposes filter params via data attributes. Updated application/views/continents/index.php to use a filter card with an HTMX-enabled form that posts to the new component endpoint and injects results into #continentResults. Reworked assets/js/sections/continents.js to centralize AJAX logic into continentsRender(), add setContinentsLoading(), handle HTMX afterSwap to initialize results, improve error/info messaging, and implement a JS reset button that triggers an HTMX submit. Overall this enables incremental loading of continent stats and simplifies the previous full-form AJAX flow.
2026-04-26 22:15:35 +01:00
Peter Goodhall
bdd72588a6 Add HTMX-driven Timeplot UI and JS refactor
Introduce an HTMX-powered results component and refactor Timeplotter flow: add component_timeplot_results controller action that supplies band/dxcc/cqzone defaults and renders a new timeplotter/component_results view. Revamp the index view to use Bootstrap cards, a filter form wired to HTMX (hx-post/hx-target/hx-trigger) and a results container that auto-loads on page load. Refactor assets/js/sections/timeplot.js: separate loading state, implement timeplotRender/timeplot helper functions, improve AJAX error handling, and add renderTimeplotFromComponent + htmx:afterSwap hook to initialize charts when the component is swapped in. Also update getTimes controller to rely on the model to output JSON directly.
2026-04-26 22:10:29 +01:00
Peter Goodhall
d1446a6c22 Add HTMX component and refactor accumulated UI/JS
Introduce a new partial view and controller endpoint for rendering accumulated-results as an HTMX component, and refactor the accumulated statistics page and JS. Changes include: adding component_accumulated_results() in the Accumulated controller and a new view (accumulate/component_results.php) exposing filter params via data-attributes; updating accumulate/index.php to use a card-based layout, improved form markup, and HTMX attributes to load the results component; and a major rewrite of assets/js/sections/accumulatedstatistics.js to modularize logic (getAwardText, setAccumulateLoading, accumulateRender), improve loading/error handling, rebuild chart/table markup, update DataTables usage, and hook into htmx:afterSwap to render the chart when the component is swapped in. Overall this enables partial updates, better UX, and cleaner client-side code.
2026-04-26 22:05:52 +01:00
Peter Goodhall
91ff3f51b0 Add HTMX activators endpoint and table view
Introduce an HTMX-driven activators UI: add Activators::component_activators() to serve the activators table, create a new view activators/component_table.php to render the activators list (merging VUCC grids and sorting/counting grids), and update activators/index.php to use an HTMX form/target for dynamic filtering. Also improve the filters UI (band, LEO/GEO toggle, min count), add a Reset button, and include client-side JS to show/hide the LEO/GEO control when SAT is selected. Date format handling and empty-result messages are preserved.
2026-04-26 21:55:55 +01:00
Peter Goodhall
e34afd51eb Allow partial option saves and surface save errors
Change success logic in Options controller to treat the save as successful if any individual option update persisted (use OR instead of AND), preventing a single-failure from marking the whole operation as failed. Fix Options_model to return TRUE after inserting a new option so inserts are reported as successful. Add a saveFailed flash message display in the email options view so users see explicit failure alerts when appropriate.
2026-04-26 18:04:58 +01:00
Peter Goodhall
9113aae3bf Add public station-diary search
Adds searching for public Station Diary entries by callsign. Introduces a new route for /search and implements Stationdiary::search() which validates the callsign, resolves the public user, handles the q GET parameter, redirects on empty queries, and sets up pagination. Adds Note model methods count_public_station_diary_search_results() and search_public_station_diary_entries() to perform the search, attach images, and prepare entries. Updates public_index view to include a search form in the top nav, display search result metadata, and show a contextual message when no matches are found.
2026-04-15 11:38:38 +01:00
Peter Goodhall
9913eb08a2 Improve callsign lookup, caching & recent details
Add a cached DXCC lookup and a jsondxcc endpoint to speed up country resolution and reduce DB load (file-cache with 1h TTL). Introduce cached_dxcc_lookup(), build_confirmation_where(), and callsign_status() in the Logbook controller; refactor confirmed_grid_before() and worked_grid_before() to accept logbook relationships and confirmation prefs to avoid redundant queries. Use a new Logbook_model::get_recent_callsign_details() to populate recent name/gridsquare/qth/iota/qsl/state/county values in a single query. Update JS to better manage lookup state, invalidate in-flight requests, and perform a debounced quick DXCC lookup to improve UX. Tweak previous-contacts pagination to a compact sliding window and remove a redundant UI note.
2026-04-13 13:57:03 +01:00
Peter Goodhall
e197a5c1f2 Add toggle for QSL Cards menu item
Introduce a user-controllable option to show/hide the "View QSL Cards" link in the Logbook menu. Controller: initialize menu_show_qsl_cards, load/save the 'show_qsl_cards' menu option and sync it to the session during profile updates. Model: fetch the 'show_qsl_cards' option and expose user_show_qsl_cards in the user data. Views: add a checkbox in the user edit form to control the setting, remove the duplicate SSTV storage card UI, and update the header to robustly read the session value and conditionally render the QSL menu item. Defaults to showing the menu item when the option is unset.
2026-04-08 22:26:05 +01:00
Peter Goodhall
297ff0af42 Add SSTV menu toggle for users
Introduce a user preference to show or hide the "View SSTV Images" menu item. Controller: initialize menu_show_sstv_images, load/save the 'menu:show_sstv_images' option and set session user_show_sstv_images when profile is saved. Model: fetches the menu option and exposes user_show_sstv_images (and has_eqsl_credentials) in the returned user data. Views: add a checkbox to the user edit form to toggle the setting, and conditionally render the eQSL/SSTV menu items in the header based on session flags. This lets users control visibility of the SSTV menu entry from their profile.
2026-04-08 22:18:02 +01:00