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.
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.
Sanitize location lists and escape query parameters across multiple models to prevent SQL injection and fix query formatting. Changes include: converting location arrays to comma-separated integer lists (array_map('intval')) instead of quoted implode, adding escape_str for band/mode/sat, adding a sanitize_location_list helper and early-return checks in CQ, escaping band lists, and updating Adif_data date where clauses to use $this->db->escape(..., NULL, FALSE). Files modified: Accumulate_model, Activated_gridmap_model, Adif_data, Cq, and Gridmap_model.
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.
Replace concatenated SQL with CodeIgniter query builder across models to improve safety and readability. Changes in Cat, Contesting_model and Oqrs_model convert raw SQL strings into active-record calls, add integer casting for ID/station values, use select/from/join/where/order_by/get patterns, and simplify delete/query flows. Affected methods include radio_status, multiple contest-related selects/inserts/deletes, and several oqrs/station_profile queries.
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.
In Logbook_model, only sanitize 'a_index' and 'age' when the input is not an empty string; if filter_var returns an empty string or false, set the variable to NULL. This prevents empty/invalid sanitized values from being treated as valid numeric inputs.
Replace hardcoded encryption key in installer with a %encryption_key% placeholder and generate a unique per-install key during installation (uses random_bytes, falls back to openssl_random_pseudo_bytes or sha1(uniqid)). Inject the generated key into config.php. Also tighten User_Model::validate_session(): return early if no user_id, clear session if user_hash is missing, re-read the user from the database to verify user_type before authenticating, and refresh or clear the session accordingly. These changes improve security by using a unique encryption key per install and by validating session state against persisted user data.
Check isset($u->row()->winkey_websocket) before casting to bool and default to false if the property is missing. This prevents PHP notices and ensures a consistent boolean value when the winkey_websocket field is null or not present.
Add get_slp_permission_column() to detect and cache the permission column name in station_logbooks_permissions (checks permission_level, access_level, then falls back to legacy permission). Replace hardcoded slp.permission_level references with the resolved column (aliasing to permission_level where needed) across show_all(), public_slugs_accessible_by_user(), check_logbook_is_accessible(), get_user_permission(), add_user_to_logbook(), and list_logbook_collaborators() to avoid failures on partially migrated databases.
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.
Fixes a conditional in Logbook_model.php where the cache-clear check used the wrong variable. The condition now uses $skipexport instead of $skipCacheClear so dashboard cache is skipped appropriately during export operations and cleared otherwise.
Add a $skipCacheClear parameter to Logbook_model::import() to skip clearing the dashboard cache for each imported record. Bulk import loop now calls import(..., true) and clears the cache once after all records are processed, reducing repeated cache invalidations and improving import performance. Default behavior remains unchanged (parameter defaults to false).
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Introduce GET /api/logbook_public_slugs/{key} in the API controller to return a JSON list of the API key owner's station logbooks that have non-empty public slugs. The endpoint authorizes the key, updates last-used timestamp, responds with 401 for missing/invalid keys or 200 with status, count and logbook entries. Add Logbooks_model::public_slugs_by_user to query station_logbooks for non-null/non-empty public_slug values ordered by logbook_name.
Introduce an embeddable "on air" status widget. Adds Widgets::on_air controller action to lookup a user by callsign, sanitize input, and render a new widgets/on_air view (iframe-friendly). Adds Cat::recent_status_by_user_id to fetch recent CAT entries (last 15 minutes) for public display. View shows ON AIR/ QRT badges, radio/satellite details, and includes auto-refresh and minimal styling; usage: /widgets/on_air/YOURCALL.
Prefer COL_GRIDSQUARE when present and fall back to COL_VUCC_GRIDS when building grid information. Added COL_VUCC_GRIDS to select lists in Note model queries and updated Logbook_model to set plot HTML to VUCC grids if gridsquare is missing. Also updated the public station diary view to display VUCC grids in the table when COL_GRIDSQUARE is empty. Files changed: Logbook_model.php, Note.php, public_index.php.
Expand highlight DX query to include QSOs with gridsquares/VUCC when stored distance is missing. Join station_profile to access station_gridsquare, select up to 50 candidate QSOs, prefer stored COL_DISTANCE but compute distances via the Qra library when needed, and pick the farthest candidate (rounded to int). Apply the same logic for daily and date-range queries and preserve the satellite-only filter. Replaces the previous strict COL_DISTANCE>0 single-row lookup to avoid excluding valid DXs derivable from grids.
Replace inline DB queries in Dxcluster with a new Logbook_model::check_if_dxcc_worked_in_logbook method to centralize DXCC existence checks (per-band and overall). Remove several debug log statements from Dxcluster. Add client-side logic in dxcluster and qso views to enforce a CW↔RBN rule: when mode is 'cw' the "hide RBN" option is forced off and the checkbox is disabled (and restored when leaving CW), and apply that lock on load and mode changes. Also disable track-band when the user manually changes band in the QSO cluster UI.
Add a full Cabrillo export workflow and harden Cabrillo/QSO formatting. Introduces a modal UI to export contest logs (new button + modal form with fields for location, category time, operators, club, soapbox, date range and other Cabrillo categories). Controller updates pass the new fields to the export action. Cabrilloformat library extended to accept and emit LOCATION and CATEGORY-TIME, improve header field ordering and presence checks, map ADIF modes to the five Cabrillo modes (CW/PH/FM/RY/DG), fix a band label (2.4G -> 2.3G), and emit placeholders for missing received exchanges to preserve column alignment. Contesting_model: more robust date parsing with UTC fallback, ensure session QSO marker only persists when timestamp valid, and build start timestamp when LIVE mode omits start_date/start_time. Frontend JS: setSession() now returns the ajax promise so callers can await it; several callers updated to await setSession and re-fetch session data before refreshing the QSO table; restore full table search on callsign blur and when suggestions are cleared. Misc: small form/input fixes (club field type, default overlay option) and additional server-supplied data loaded into the contesting view (active station id, contest session, station profile). These changes add required Cabrillo fields for certain contests and make exports and session handling more reliable.
Implement server-side pagination for previous contacts and add a DXCC Summary tab. Controller Qso now uses last_custom_paginated and exposes total_rows/total_pages/current_page/limit. Logbook_model gained last_custom_paginated and last_custom_count to support paged queries. The previous_contacts view was updated with HTMX pagination controls and wrapping for a scrollable table; qso/index was refactored to a tabbed UI (Previous Contacts / DXCC Summary). Lookup result display was improved: responsive table, sticky header, consolidation of LSB/USB into SSB and filtering out empty modes. JS and CSS updates load DXCC HTML into the new tab, hide legacy dxccsummary elements, and add form reset / Escape handling to return focus to the previous contacts tab.
Introduce a user preference to treat satellite QSOs as "worked" for DXPedition status and wire it through the UI, controller, and model. Changes: add dashboard_dxpedition_sat_worked default and POST handling in User controller, add checkbox to user settings view to toggle the option, update Workabledxcc_model to optionally include satellite results when computing workedBefore via a new shouldIncludeSatelliteWorked() helper, and tweak upcoming_dxccs view styling and row classes to visually indicate worked vs needed DXPeditions. This makes DXPedition cards respect users' preference for counting satellite contacts.
Replace dash placeholder with the string "Unknown" for DXpedition callsign display. The Workabledxcc_model::normalize method now returns "Unknown" for empty or placeholder patterns (e.g. F/H, M/S). The upcoming_dxccs view was updated to default to and check for "Unknown" when rendering callsigns and tooltips so the UI shows a clearer, consistent placeholder.
Add normalizeDxpedCallsign() to Workabledxcc_model and use it in the controller and model to standardize/replace placeholder callsigns (e.g. F/H, M/S) with '-'. Improve week filtering and date handling: set explicit times for start/end of week and records, use overlap logic to include DXpeditions that intersect the week, and compute daysLeft with proper past/last-day labeling. Revamp upcoming_dxccs view: implement client-side pagination, page size, prev/next controls, escaped tooltips, and graceful display for placeholder callsigns; add a link to the full list. These changes improve display consistency and UI usability.
Add callsign uniqueness checks and prevent demotion of the last admin. Introduces ECALLSIGNEXISTS and ELASTADMIN constants, new model methods (exists_by_callsign, count_admin_users, would_remove_last_admin) and integrates checks into add/update flows to return appropriate error codes. Update User controller to add validation callbacks (check_unique_callsign, check_last_admin_role) and wire errors into multiple user actions. Update signup/edit views to display callsign and usertype validation feedback using Bootstrap invalid-feedback and improve alert layout.
Handle compound callsigns (e.g. F/MM9SQL/P) when looking up call history and database records. Added extract_base_callsign() to normalize and extract the longest segment of a slash-separated callsign, updated controller lookup to fall back to the base callsign and to skip empty rows, and tightened matching logic to accept either the full input or its base call. Updated model WHERE clause to match normalized COL_CALL against exact, first, middle, or last slash-separated segments (with Ø→0 normalization) to ensure compound variants are found.
Include the number of selected QSOs in the flash notice and update feedback to show both selected and actually updated counts. Also fix the model logic to only increment the applied counter when DB affected_rows() > 0 (previously >= 0), preventing non-updates from being counted.
Add an optional start_date filter to the callhistory preview workflow. Controller: validate YYYY-MM-DD input, set flash notice and redirect on invalid format, pass the start_date to the model and include it in view data. Model: accept a start_date parameter and apply a COL_TIME_ON >= start_date 00:00:00 WHERE clause when provided. View: add a date input to the form and display the selected start date in the no-matches message to indicate the time scope.
Adds a workflow to scan uploaded call history files for matching QSOs and apply SIG/SIG_INFO backfills. Controller Callhistory: loads logbooks, adds scan_preview and scan_apply actions, CSV parsing helpers, and safety checks to ensure files are readable and station ownership matches. Model Callhistory_model: adds get_station_ids_for_logbook, get_qsos_for_callsigns, and apply_sig_backfill to fetch candidate QSOs within a user's station/logbook scope and apply updates in a DB transaction. View callhistory/index.php: introduces a preview UI with selection controls, logbook scope selector, and client-side JS to manage selections and submission. Changes include normalization/heuristics for extracting callsigns/exchanges and only propose updates where existing SIG fields are blank.
Introduce a new Call History feature: adds Callhistory controller, Callhistory_model, migration (create callhistory_files table) and view for uploading/managing call history files. Implements CSV/TXT upload handling, storage per-user, activation/priority controls, checksum, and server-side lookup that scans active files for matching callsigns. Integrates UI: header menu link, contesting and QSO views show call history results inline, and JavaScript to perform lookups and render results with copy-to-SIG functionality. Also updates .gitignore to exclude uploaded callhistory directory and bumps migration_version to 261.
Normalize COL_CNTY/COL_STATE handling and use normalized expressions in queries to ensure consistent grouping/filtering and avoid mismatches.
- application/models/Counties.php: add normalized SQL expressions (trim/case/substring) and apply them to counting, grouping and ordering queries; filter out empty normalized states and adjust joins to compare normalized values.
- application/models/Logbook_model.php: normalize incoming $county/$state, add dxcc_entities join and selects, and use normalized WHERE clauses (case/trim and substring for 'ST, County' formats); also standardize band filter to COL_BAND != 'SAT'.
- application/views/view_log/partial/log_ajax.php: avoid undefined property notices by checking for name/end on the row object and use those values for Country and Flag display.
These changes address inconsistent imported county/state formats (e.g. "ST, County"), whitespace/case variations, and prevent PHP notices while keeping query semantics intact.
Add support for comma-separated POTA references across the app: bump migration version to 259 and add migration 260 to expand COL_MY_POTA_REF and station_pota to VARCHAR(255). Introduce Pota::split_refs and collect_refs_from_rows to normalize and aggregate multiple refs; refactor Pota counts to compute unique refs per band/mode. Normalize station_pota when saving in Stations and Logbook_model (uses normalize_pota_refs), and adjust POTA search to match refs inside comma-separated values. Update views to render multiple POTA links and hashtags, and add Selectize-based autocomplete for station POTA input on create/edit forms. Also minor change to user_owns_station query (removed user_id where clause).
Refactor eQSL batch update to operate on resolved primary keys instead of re-matching QSOs by time/callsign/band/mode. Eqslmethods_model now queries current eQSL status by COL_PRIMARY_KEY, builds update batches keyed by primary key, and returns updated_ids and duplicate_ids. This avoids re-querying with un-normalized ADIF modes, improves reliability (avoids CI3 affected_rows() pitfalls), and makes duplicate detection deterministic. EqslImporter was updated to enqueue qso_id for batch updates and to mark QSOs as Updated or Already received based on the returned ID lists.
Enhance created_at parsing to accept date-only inputs (YYYY-MM-DD). If only a date is provided, append a time part: reuse the existing note's time if available and valid, otherwise use the current time. For other input formats, attempt to parse with strtotime and normalize to Y-m-d H:i:s. All stored values are xss_clean()-sanitized and invalid/unparsable inputs are ignored.
Add interactive QSO map support for public station diary entries. Controller: add stationdiary::get_qso_map_data() to return JSON map data for a given entry/date range/logbook with station location. Models: Note::get_public_entry_user_id() to resolve the entry owner; Logbook_model: get_qsos_for_public_map() to query QSOs for the map, set a default stn_loc fallback, and skip markers with invalid 0,0 coordinates. View: public_index.php includes Leaflet CSS/JS, a Show/Hide QSO Map toggle, map container per entry, client-side map initialization and marker/popups loaded via POST to the new endpoint. These changes ensure public diary entries can display QSO locations and the station marker while avoiding bogus markers and limiting results to the correct user/logbook context.