Increment migration_version to 267 and add Migration_tag_2_8_13. The new migration updates the 'options' table to set version = '2.8.13' and resets user_options (version_dialog confirmed) to 'false' to trigger the version info dialog; the down() method reverts the version to '2.8.12'.
Support multiple comma-separated grids (VUCC) across SimpleFLE. Updated Logbook_model to accept vucc_grids and prefer it over single locator when present. Frontend changes: parse and detect single vs multiple grids, add Grid column to preview table, send both `locator` and `vucc_grids` to the save endpoint, tighten time regex, add qsotime variable, include X band in satellite mode handling, and clear satellite state when switching to a regular band. Documentation and help updated: improved syntax help and added full docs/SimpleFLE.md explaining grids, VUCC usage, satellite handling and other SimpleFLE features.
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.
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.
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.
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.
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.
Use min(COL_TIME_ON) for ordering in multiple Timeline_model queries (replacing ORDER BY date DESC) and remove unnecessary date() wrappers in SELECTs so groups are sorted by the earliest contact time. In the timeline view, only render the DXCC End Date column when at least one entry has an end date and adjust row output to avoid displaying an empty column.
Update language and timeline view: correct gridsquares label from "Show QSO's" to "Show QSOs" for grammatical accuracy. In timeline/index.php, add an else branch to display a green "Active" badge when a DXCC entry has no end date (keeping the existing deleted badge for ended entries) and wrap the conditional in braces for clarity. These changes improve UI clarity and code readability.
Replace the old timeline dialog with a Bootstrap 5 modal (with HTMX support and jQuery fallback) and move AJAX content into the modal body. Add initTimelineDetailsTable to initialize/destroy DataTables for detail views and guard DataTable initialization when tables are absent. Revamp the timeline index: convert filters into a Bootstrap card with improved form layout, add Reset button, show a summary badge/count and a DXCC info alert for DXCC award, and embed the details modal markup. Convert timeline tables to responsive containers, use semantic <th> headers, and replace text links with accessible buttons that call displayTimelineContacts. Minor UI tweaks: change empty-result alert to warning and add a CSS class (menuOnResultTab) to the dropdown-menu in log_ajax for result-row actions.
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.
Insert form_open('options/email_save') into the normal email configuration branch in application/views/options/email.php so the form is properly opened and will post to the email_save handler, enabling saving of email settings.
Fixes#3429
Remove a stray backslash-n sequence from the QSL tab conditional in application/views/qso/index.php. This prevents the backslash-n from being emitted into the HTML and restores proper spacing/formatting for the QSL tab pane.
Remove a stray backslash-n sequence from the QSL tab conditional in application/views/qso/index.php. This prevents the backslash-n from being emitted into the HTML and restores proper spacing/formatting for the QSL tab pane.
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.
Update application/config/migration.php to increment migration_version from 265 to 266 to reflect the latest applied migrations and ensure the app recognizes the new migration state.
Update application/config/migration.php to increment migration_version from 265 to 266 to reflect the latest applied migrations and ensure the app recognizes the new migration state.
Add migration (application/migrations/266_tag_2_8_12.php) to update the stored application version to 2.8.12. The up() method sets options.version to '2.8.12' and resets user_options where option_type='version_dialog' and option_name='confirmed' to 'false' to trigger the version info dialog. The down() method reverts the options.version back to '2.8.11'.
Make action buttons in the logbooks index conditional while keeping layout intact. Always show the Edit button; show Embed only when a public_slug exists; show Share only for the owner or admin; show Delete only when the logbook is not the active station logbook and the user is the owner. Hidden placeholder buttons include tabindex and aria-hidden to preserve spacing and accessibility. Added inline comments for clarity.
Remove fallback logic that forced the cluster band filter to 'all' when no radio (or radio '0') was selected. The syncBandFromRadio() method now simply applies the current #band value if present and otherwise leaves the filter unchanged. Also cleaned up related comments to reflect the simplified behavior, so band changes always sync without implicitly switching to 'all'.
Change htmx.ajax swap option from 'outerHTML' to 'innerHTML' when updating the #qso-last-table. This ensures only the container's contents are replaced (preserving the container element and any bindings/listeners) and avoids DOM reattachment issues.
Add an optional preserveDxccState parameter to resetDefaultQSOFields to keep DXCC-related UI state (country, callsign_info text/title/class, dxcc_id, CQ zone, ITU zone) when requested, preventing badge/country flicker. Update the callsign focusout flow to use this option. Also remove unnecessary .trigger('change') calls on dxcc_id assignments to avoid redundant change events and adjust field clearing/restoration order accordingly.
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.
Prevent the logbook auto-refresh from overwriting callsign lookup results by introducing a previousContactsLookupMode flag and pauseAutoRefresh()/resumeAutoRefresh() helpers that remove/restore the htmx hx-trigger on the qso list element. Clear and hide partial_view on reset and resume auto-refresh there. Show an explicit "no past QSOs" info message when a callsign is found but has no previous QSOs, and ensure the previous-contacts panel state is set deterministically. Also harden CAT radioID checking to treat undefined and falsy values as invalid. Changes touch qso.js and the footer view.
Treat lookup.partial defensively (ensure it's a string) before injecting into #partial_view, and determine whether to show previous-contacts panel based on trimmed content instead of always enabling it. Clear #partial_view when hiding the panel to avoid stale content, and simplify the htmx init check to rely on the presence of content rather than visibility. These changes prevent accidental HTML insertion and make panel toggling deterministic.
Hide the #partial_view container by default and only reveal the previous-contacts panel when it contains non-empty HTML. Invoke getDxccResult after injecting the partial result so the DXX summary is fetched and displayed. Add handling to reset QSO fields and switch back to the Previous Contacts tab when the callsign is cleared, and remove duplicated/unused code in the callsign focusout handler. This prevents showing an empty lookup panel and cleans up the logic around loading lookup details.
Add helper methods to determine a QSO's primary grid, lookup DXCC coordinates (with caching and logbook_model.dxcc_lookup fallback), and resolve candidate distances by preferring grid-to-grid QRA distance with a DXCC-coordinate fallback. Update highlight DX queries to select time, DXCC and DXCC lat/long and join dxcc_entities. Replace duplicated inline distance logic with a single build_highlight_dx_from_candidates routine that picks the farthest candidate and sets COL_DISTANCE (rounded to int). This centralizes distance computation and ensures QSOs without stored distances but with grids or DXCC data are correctly considered.
After saving a QSO, switch to the QSP tab if present (using bootstrap.Tab.show()). Enhance reset_fields() to clear wwff and pota info (HTML and title) and safely clear the sota_ref Selectize control (if initialized) instead of using .val(""), preventing leftover values/tooltips and avoiding errors when Selectize isn't present.
Remove inline CSS rules from application/views/user/edit.php: deleted .user_edit custom properties (--gh-border, --gh-muted, --gh-bg-subtle) and .settings-nav .list-group-item styles (cursor, margin-bottom). This cleans up duplicated/unused styling that should be provided by global stylesheets.
Revamp user edit view into a responsive two-column settings UI: add sidebar navigation, GitHub-like styles, and JS to switch setting panes. Reorganize and present existing options (main menu, map params, default values, Hams.at, AMSAT, Mastodon, hardware, QSO form) as cards inside accordion panes, remove duplicated blocks, and align the save button. Changes improve usability and navigation in application/views/user/edit.php.
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.
Move the "Show SSTV Images" menu checkbox out of its previous standalone section and into a new "Storage Options" card. Removed the old <hr> and explanatory paragraph and added a new card column with a header, brief description, and the existing checkbox so image-related menu controls are grouped together and the form layout is cleaner.
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.
Add setPreviousContactsPanelState(showLookupDetails) to centralize show/hide logic for #qso-last-table, its trailing <small>, and #partial_view. Replace inline show/hide calls with this helper, update resetToPreviousContactsTab to use it, and add an htmx:afterSwap listener to reapply the visibility state after HTMX updates the previous contacts markup. This ensures the lookup details and default previous-contacts table remain consistent across dynamic updates.
When resetting the UI, clear the stored CAT value data on frequency, satellite and mode inputs so re-selecting a radio with identical values will still repopulate fields. Adds a jQuery removeData('catValue') call for #frequency, #frequency_rx, #sat_name, #sat_mode, #transmit_power, #selectPropagation and #mode, with a comment explaining the intent.
Add a suppressNextResetHandler flag to avoid running the reset event handler when the QSO form is reset programmatically (set the flag before calling qsoFormElement.reset()). Introduce clearCatTrackedFieldState() to remove stored 'catValue' data from frequency/satellite/mode/power fields and call it from reset_fields(). Also initialize suppressNextResetHandler and short-circuit the '#qso_input' reset handler when the flag is set to prevent unintended UI side effects.
Add request/version tracking to CAT JSON handling to avoid applying stale responses when radio selection changes. Introduce catRequestCounter, lastProcessedCatRequest and catSelectionContextVersion, compare request IDs and context version before updating UI, and increment context version on radio selection change. Also add a syncFromSelectedRadioAfterReset helper in qso.js (with a throttled user notice) and call it after form reset and on escape-based reset so the form prefers live CAT values from the selected radio.
Before resetting the QSO form, capture the user's current operating context (band, mode, satellite name/mode, propagation). After qsoForm.reset() and the built-in reset handlers run, reapply those captured values (with a 150ms timeout) so the UI doesn't revert to server-session defaults or the previous QSO values. Also call setRst(mode) if available to update RST for the restored mode.
Rename the container id from "qso-last-table" to "qso-last-table-content" and update all pagination links' hx-target attributes accordingly. This ensures htmx swaps and the 5s auto-refresh target the correct element for the previous contacts component.
Prevent caching for the HTMX endpoint and move polling into the component. Qso controller now sets Cache-Control/Pragma/Expires headers for the component_past_contacts endpoint to ensure fresh data. The previous_contacts view was updated to self-poll (hx-get with page param, hx-trigger every 5s, hx-target="this", and hx-vals timestamp) to include a cache-busting timestamp. The main qso index removed the redundant recurring poll (now only triggers on load) to avoid duplicate requests.
Ensure satellite fields are cleared whenever propagation mode is not 'SAT' and when the QSO form is reset. In Qso.php, sat_name and sat_mode are set to empty strings if prop_mode != 'SAT' (added in two handling points). In assets/js/sections/qso.js, added clearSatelliteFields() to clear sat_name, sat_mode, satellite_modes_list and related state, call it from reset_fields(), and invoke the form reset flow to ensure no stale satellite data remains after resetting or changing propagation mode.
Removed the dynamic contact count from the 'View QSO List' summary in application/views/station_diary/public_index.php. The inline '<span class="qso-count">' count display was removed, leaving only the static summary text; data attributes and table markup remain unchanged.
Bump migration version to 265 and add a migration (265_add_station_time_on_index) that creates idx_station_time_on on (station_id, COL_TIME_ON) for faster time-based queries. Introduce lazy QSO loading: controller now sets defer_qso_list and uses a new POST endpoint get_filtered_qsos to fetch filtered QSO lists and summaries; also updated cache keys/render version and added per-entry cache deletion. Note model: add include_qso_list flag, memoize QSO summaries/lists and station IDs, replace DATE(COL_TIME_ON) filters with half-open datetime ranges (start inclusive, end exclusive) via helper methods to improve index use and performance. View: update public_index to render QSO list containers with data attributes, add JS to fetch and render QSO rows on demand, and support highlight DX updates. Overall changes optimize QSO queries and enable deferred loading to reduce initial page load and DB cost.
When a QSO is saved, preserve the selected mode, satellite name, and satellite mode so the user doesn't have to reselect them for the next entry. Capture savedMode, savedSatName, and savedSatMode into postSaveDefaults and call a new reapplyPostSaveDefaults() after reset_fields(). Added reapplyPostSaveDefaults() to restore band, mode, sat_name, sat_mode and call setRst() if available.
When resetting QSO fields, reapply the default RST for the current mode. The change calls setRst($('.mode').val()) if the setRst function exists, ensuring mode-specific defaults (e.g., CW => 599) are restored after a form reset to avoid stale RST values.
Add escapeNoticeValue to sanitize user-provided strings (&, <, >, ", ') to prevent XSS in notice messages. Use a new savedBand variable and update saveMessage assembly to include both callsign and band when available, with fallbacks for when only one is present. Changes are in assets/js/sections/qso.js.
Introduce an AJAX-based QSO save flow: add QSO::ajax_saveqso() in the controller to validate input, set session/cookies, create the QSO and return JSON success/error payloads. Update the QSO view to include a data-ajax-save-url on the form and add a notice-alerts container location. Enhance the qso.js client: add isSubmitting guard, showQsoNotice helper, intercept form submit to POST serialized form data to the AJAX endpoint, handle success (reset form, show notice, refresh recent QSOs via htmx), display validation/server errors, and restore UI state when complete. These changes avoid full page reloads on save and provide inline validation feedback.
Introduce API method logbook_public_slugs_accessible($key) that authorizes the API key, updates last-used timestamp, and returns JSON of all station logbooks (owned or shared) that have non-empty public slugs for the key owner. Add Logbooks_model::public_slugs_accessible_by_user($user_id) to select matching logbooks, label access_level as "owner" for owners or the stored permission_level for shared entries, and order results by logbook_name. Endpoint returns 401 for missing/invalid keys and a success payload with status, count, and logbooks on success.