diff --git a/application/config/migration.php b/application/config/migration.php index 46009ce35..ca0f54dc9 100644 --- a/application/config/migration.php +++ b/application/config/migration.php @@ -22,7 +22,7 @@ | */ -$config['migration_version'] = 265; +$config['migration_version'] = 266; /* |-------------------------------------------------------------------------- diff --git a/application/config/routes.php b/application/config/routes.php index 40332add2..916453336 100644 --- a/application/config/routes.php +++ b/application/config/routes.php @@ -54,6 +54,7 @@ $route['station-diary/(:any)'] = 'stationdiary/index/$1'; $route['station-diary/(:any)/rss'] = 'stationdiary/rss/$1'; +$route['station-diary/(:any)/search'] = 'stationdiary/search/$1'; $route['station-diary/(:any)/entry/(:num)'] = 'stationdiary/entry/$1/$2'; $route['station-diary/(:any)/entry/(:num)/react'] = 'stationdiary/react/$1/$2'; $route['station-diary/(:any)/(:num)'] = 'stationdiary/index/$1/$2'; diff --git a/application/controllers/Accumulated.php b/application/controllers/Accumulated.php index 152d5abff..8070da938 100644 --- a/application/controllers/Accumulated.php +++ b/application/controllers/Accumulated.php @@ -29,6 +29,15 @@ public function index() $this->load->view('interface_assets/footer'); } + public function component_accumulated_results() { + $data['band'] = $this->input->post('band') ?: 'All'; + $data['mode'] = $this->input->post('mode') ?: 'All'; + $data['award'] = $this->input->post('awardradio') ?: 'dxcc'; + $data['period'] = $this->input->post('periodradio') ?: 'year'; + + $this->load->view('accumulate/component_results', $data); + } + /* * Used for ajax-call in javascript to fetch the data and insert into table and chart */ diff --git a/application/controllers/Activators.php b/application/controllers/Activators.php index 6c66d5420..4cb90937b 100644 --- a/application/controllers/Activators.php +++ b/application/controllers/Activators.php @@ -72,4 +72,43 @@ public function details() { $this->load->view('activators/details', $data); } + public function component_activators() { + // HTMX endpoint for activators table + $this->load->model('Activators_model'); + $this->load->model('bands'); + + $band = $this->input->post('band') ?: 'All'; + $mincount = $this->input->post('mincount') ?: 2; + $leogeo = $this->input->post('leogeo') ?: 'both'; + + $activators_array = $this->Activators_model->get_activators($band, $mincount, $leogeo); + $activators_vucc_array = $this->Activators_model->get_activators_vucc($band, $leogeo); + + // Get Date format + if($this->session->userdata('user_date_format')) { + $custom_date_format = $this->session->userdata('user_date_format'); + } else { + $custom_date_format = $this->config->item('qso_date_format'); + } + + $vucc_grids = array(); + if ($activators_vucc_array) { + foreach ($activators_vucc_array as $line) { + $vucc_grids[$line->call] = $line->vucc_grids; + } + } + + if ($activators_array) { + $this->load->view('activators/component_table', array( + 'activators_array' => $activators_array, + 'vucc_grids' => $vucc_grids, + 'custom_date_format' => $custom_date_format, + 'band' => $band, + 'leogeo' => $leogeo + )); + } else { + echo ''; + } + } + } diff --git a/application/controllers/Continents.php b/application/controllers/Continents.php index 5858652d1..07b51ab9c 100644 --- a/application/controllers/Continents.php +++ b/application/controllers/Continents.php @@ -56,4 +56,11 @@ public function get_continents() { echo json_encode($continentsstats); } + public function component_continent_results() { + $data['band'] = xss_clean($this->input->post('band')) ?: ''; + $data['mode'] = xss_clean($this->input->post('mode')) ?: ''; + + $this->load->view('continents/component_results', $data); + } + } diff --git a/application/controllers/Emeinitials.php b/application/controllers/Emeinitials.php index 2334312f8..0eb117d31 100644 --- a/application/controllers/Emeinitials.php +++ b/application/controllers/Emeinitials.php @@ -16,34 +16,33 @@ public function index() // Render Page $data['page_title'] = "EME Initials"; - $this->load->model('Emeinitials_model'); - - if ($this->input->post('band') != NULL) { // Band is not set when page first loads. - $band = $this->input->post('band'); - } - else { - $band = 'All'; - } - - if ($this->input->post('mode') != NULL) { - $mode = $this->input->post('mode'); - } - else { - $mode = 'All'; - } - $this->load->model('modes'); $this->load->model('bands'); $data['modes'] = $this->modes->active(); - - $data['timeline_array'] = $this->Emeinitials_model->get_initials($band, $mode); $data['worked_bands'] = $this->bands->get_worked_bands(); - $data['bandselect'] = $band; - $data['modeselect'] = $mode; $this->load->view('interface_assets/header', $data); $this->load->view('emeinitials/index'); $this->load->view('interface_assets/footer'); } + + public function component_eme_results() { + $this->load->model('Emeinitials_model'); + + $band = $this->input->post('band') ?: 'All'; + $mode = $this->input->post('mode') ?: 'All'; + + // Get Date format + if ($this->session->userdata('user_date_format')) { + $custom_date_format = $this->session->userdata('user_date_format'); + } else { + $custom_date_format = $this->config->item('qso_date_format'); + } + + $data['timeline_array'] = $this->Emeinitials_model->get_initials($band, $mode); + $data['custom_date_format'] = $custom_date_format; + + $this->load->view('emeinitials/component_results', $data); + } } diff --git a/application/controllers/Options.php b/application/controllers/Options.php index 70fea855e..3e9628c65 100644 --- a/application/controllers/Options.php +++ b/application/controllers/Options.php @@ -337,14 +337,14 @@ function email_save() { // Update smtpPassword choice within the options system $smtpPasswordupdate = $this->optionslib->update('smtpPassword', $this->input->post('smtpPassword'), 'yes'); - // Check if all updates are successful - $updateSuccessful = $emailProtocolupdate && - $smtpEncryptionupdate && - $emailSenderNameupdate && - $emailAddressupdate && - $smtpHostupdate && - $smtpPortupdate && - $smtpUsernameupdate && + // Consider save successful when at least one value is persisted. + $updateSuccessful = $emailProtocolupdate || + $smtpEncryptionupdate || + $emailSenderNameupdate || + $emailAddressupdate || + $smtpHostupdate || + $smtpPortupdate || + $smtpUsernameupdate || $smtpPasswordupdate; // Set flash session based on update success diff --git a/application/controllers/Stationdiary.php b/application/controllers/Stationdiary.php index 480e686b5..3ed263982 100644 --- a/application/controllers/Stationdiary.php +++ b/application/controllers/Stationdiary.php @@ -397,4 +397,80 @@ public function get_qso_map_data() // Merge and return echo json_encode(array_merge($plotArray, $stationArray)); } + + public function search($callsign = NULL) + { + if ($this->security->xss_clean($callsign, TRUE) === FALSE) { + show_404(); + return; + } + + $resolution = $this->note->resolve_public_user_by_callsign($callsign); + if (!isset($resolution['status']) || $resolution['status'] !== 'ok') { + show_404(); + return; + } + + $query = trim((string)$this->input->get('q', TRUE)); + $cleanCallsign = strtoupper($resolution['callsign']); + + if ($query === '') { + redirect('station-diary/' . rawurlencode($cleanCallsign)); + return; + } + + $user_id = (int)$resolution['user_id']; + $perPage = 10; + $totalRows = $this->note->count_public_station_diary_search_results($user_id, $query); + + $config['base_url'] = site_url('station-diary/' . rawurlencode($cleanCallsign) . '/search'); + $config['total_rows'] = $totalRows; + $config['per_page'] = $perPage; + $config['num_links'] = 5; + $config['page_query_string'] = TRUE; + $config['reuse_query_string'] = TRUE; + $config['query_string_segment'] = 'page'; + $config['use_page_numbers'] = FALSE; + $config['full_tag_open'] = ''; + $config['attributes'] = array('class' => 'page-link'); + $config['first_link'] = FALSE; + $config['last_link'] = FALSE; + $config['first_tag_open'] = '
  • '; + $config['first_tag_close'] = '
  • '; + $config['prev_link'] = '«'; + $config['prev_tag_open'] = '
  • '; + $config['prev_tag_close'] = '
  • '; + $config['next_link'] = '»'; + $config['next_tag_open'] = '
  • '; + $config['next_tag_close'] = '
  • '; + $config['last_tag_open'] = '
  • '; + $config['last_tag_close'] = '
  • '; + $config['cur_tag_open'] = '
  • '; + $config['cur_tag_close'] = '(current)
  • '; + $config['num_tag_open'] = '
  • '; + $config['num_tag_close'] = '
  • '; + + $this->pagination->initialize($config); + + $pageOffset = (int)$this->input->get('page', TRUE); + if ($pageOffset < 0) { + $pageOffset = 0; + } + + $data['callsign'] = $cleanCallsign; + $data['entries'] = $this->note->search_public_station_diary_entries($user_id, $query, $perPage, $pageOffset); + $data['pagination_links'] = $this->pagination->create_links(); + $data['page_title'] = 'Search: ' . $query . ' - Station Diary - ' . $cleanCallsign; + $data['rss_url'] = site_url('station-diary/' . rawurlencode($cleanCallsign) . '/rss'); + $data['qso_datetime_format'] = $this->get_public_qso_datetime_format($resolution['user_date_format'] ?? NULL); + $data['is_single_entry'] = false; + $data['defer_qso_list'] = false; + $data['current_entry_permalink'] = ''; + $data['is_search_results'] = true; + $data['search_query'] = $query; + $data['search_total'] = $totalRows; + + $this->load->view('station_diary/public_index', $data); + } } \ No newline at end of file diff --git a/application/controllers/Timeplotter.php b/application/controllers/Timeplotter.php index 42d3aa841..0b78771f0 100644 --- a/application/controllers/Timeplotter.php +++ b/application/controllers/Timeplotter.php @@ -32,6 +32,14 @@ public function index() $this->load->view('interface_assets/footer'); } + public function component_timeplot_results() { + $data['band'] = $this->input->post('band') ?: 'All'; + $data['dxcc'] = $this->input->post('dxcc') ?: 'All'; + $data['cqzone'] = $this->input->post('cqzone') ?: 'All'; + + $this->load->view('timeplotter/component_results', $data); + } + public function getTimes() { // POST data $postData = $this->input->post(); @@ -39,10 +47,8 @@ public function getTimes() { //load model $this->load->model('Timeplotter_model'); - // get data - $data = $this->Timeplotter_model->getTimes($postData); - - return json_encode($data); + // Model method writes JSON response directly + $this->Timeplotter_model->getTimes($postData); } diff --git a/application/language/english/gridsquares_lang.php b/application/language/english/gridsquares_lang.php index 07bc32c96..55a5c8deb 100644 --- a/application/language/english/gridsquares_lang.php +++ b/application/language/english/gridsquares_lang.php @@ -16,7 +16,7 @@ $lang['gridsquares_total_count'] = 'Total count'; $lang['gridsquares_minimum_count'] = "Minimum Count"; -$lang['gridsquares_show_qsos'] = "Show QSO's"; +$lang['gridsquares_show_qsos'] = "Show QSOs"; $lang['gridsquares_show_map'] = "Show Map"; $lang['gridsquares_band'] = 'Band'; $lang['gridsquares_mode'] = 'Mode'; diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index 5363310e0..c3d7db787 100755 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -242,7 +242,8 @@ function create_qso() } //$darc_dok = $this->input->post('darc_dok'); - $qso_locator = strtoupper(trim(xss_clean($this->input->post('locator')) ?? '')); + // Handle both single gridsquare (locator) and multiple gridsquares (vucc_grids) from SimpleFLE + $qso_locator = strtoupper(trim(xss_clean($this->input->post('vucc_grids') ?? '') ?: xss_clean($this->input->post('locator') ?? ''))); $qso_name = $this->input->post('name'); $qso_age = null; $qso_usa_state = $this->input->post('usa_state') == null ? '' : $this->input->post('usa_state'); diff --git a/application/models/Note.php b/application/models/Note.php index a7ec9742f..f218533bb 100644 --- a/application/models/Note.php +++ b/application/models/Note.php @@ -401,6 +401,48 @@ public function count_public_station_diary_entries($user_id) { return (int)$this->db->count_all_results(); } + public function count_public_station_diary_search_results($user_id, $query) { + $this->db->from('notes'); + $this->db->where('user_id', (int)$user_id); + $this->db->where('cat', 'Station Diary'); + $this->db->where('is_public', 1); + $this->db->group_start(); + $this->db->like('title', $query); + $this->db->or_like('note', $query); + $this->db->group_end(); + return (int)$this->db->count_all_results(); + } + + public function search_public_station_diary_entries($user_id, $query, $limit = 10, $offset = 0) { + $this->db->from('notes'); + $this->db->where('user_id', (int)$user_id); + $this->db->where('cat', 'Station Diary'); + $this->db->where('is_public', 1); + $this->db->group_start(); + $this->db->like('title', $query); + $this->db->or_like('note', $query); + $this->db->group_end(); + $this->db->order_by('created_at', 'DESC'); + $this->db->order_by('id', 'DESC'); + $this->db->limit((int)$limit, (int)$offset); + + $entries = $this->db->get()->result(); + + $ids = array(); + foreach ($entries as $entry) { + $ids[] = (int)$entry->id; + } + + $imagesMap = $this->get_diary_images($ids); + foreach ($entries as $entry) { + $entry->images = isset($imagesMap[$entry->id]) ? $imagesMap[$entry->id] : array(); + $entry->qso_summary = null; + $entry->qso_list = array(); + } + + return $entries; + } + public function get_public_station_diary_entries($user_id, $limit = 10, $offset = 0, $include_qso_list = TRUE) { $this->db->from('notes'); $this->db->where('user_id', (int)$user_id); diff --git a/application/models/Options_model.php b/application/models/Options_model.php index d323e10f8..fc1ce720e 100644 --- a/application/models/Options_model.php +++ b/application/models/Options_model.php @@ -85,7 +85,7 @@ function update($option_name, $option_value, $auto_load = NULL) { // Save to database $this->db->insert('options', $data); - return FALSE; + return TRUE; } } diff --git a/application/models/Timeline_model.php b/application/models/Timeline_model.php index b34866d9a..328f02db4 100644 --- a/application/models/Timeline_model.php +++ b/application/models/Timeline_model.php @@ -48,7 +48,7 @@ public function get_timeline_dxcc($band, $mode, $location_list, $qsl, $lotw, $eq $sql .= $this->addQslToQuery($qsl, $lotw, $eqsl); $sql .= " group by col_dxcc, col_country - order by date desc"; + order by min(COL_TIME_ON) desc"; $query = $this->db->query($sql); @@ -80,7 +80,7 @@ public function get_timeline_was($band, $mode, $location_list, $qsl, $lotw, $eqs $sql .= $this->addQslToQuery($qsl, $lotw, $eqsl); $sql .= " group by col_state - order by date desc"; + order by min(COL_TIME_ON) desc"; $query = $this->db->query($sql); @@ -110,7 +110,7 @@ public function get_timeline_iota($band, $mode, $location_list, $qsl, $lotw, $eq $sql .= $this->addQslToQuery($qsl, $lotw, $eqsl); $sql .= " and col_iota <> '' group by col_iota, name, prefix - order by date desc"; + order by min(COL_TIME_ON) desc"; $query = $this->db->query($sql); @@ -139,7 +139,7 @@ public function get_timeline_waz($band, $mode, $location_list, $qsl, $lotw, $eqs $sql .= $this->addQslToQuery($qsl, $lotw, $eqsl); $sql .= " and col_cqz <> '' group by col_cqz - order by date desc"; + order by min(COL_TIME_ON) desc"; $query = $this->db->query($sql); @@ -280,7 +280,7 @@ public function get_timeline_vucc($band, $mode, $location_list, $qsl, $lotw, $eq public function get_gridsquare($band, $mode, $location_list, $qsl, $lotw, $eqsl) { // $sql = "select min(date(COL_TIME_ON)) date, col_gridsquare from " - $sql = "select min(date(COL_TIME_ON)) date, upper(substring(col_gridsquare, 1, 4)) gridsquare from " + $sql = "select min(COL_TIME_ON) date, upper(substring(col_gridsquare, 1, 4)) gridsquare from " .$this->config->item('table_name'). " thcv where station_id in (" . $location_list . ")"; @@ -301,7 +301,7 @@ public function get_gridsquare($band, $mode, $location_list, $qsl, $lotw, $eqsl) $sql .= $this->addQslToQuery($qsl, $lotw, $eqsl); $sql .= " and col_gridsquare <> '' group by upper(substring(col_gridsquare, 1, 4)) - order by date desc"; + order by min(COL_TIME_ON) desc"; $query = $this->db->query($sql); @@ -310,7 +310,7 @@ public function get_gridsquare($band, $mode, $location_list, $qsl, $lotw, $eqsl) public function get_vucc_grids($band, $mode, $location_list, $qsl, $lotw, $eqsl) { // $sql = "select min(date(COL_TIME_ON)) date, col_gridsquare from " - $sql = "select date(COL_TIME_ON) date, upper(col_vucc_grids) gridsquare from " + $sql = "select COL_TIME_ON date, upper(col_vucc_grids) gridsquare from " .$this->config->item('table_name'). " thcv where station_id in (" . $location_list . ")"; diff --git a/application/views/accumulate/component_results.php b/application/views/accumulate/component_results.php new file mode 100644 index 000000000..514916195 --- /dev/null +++ b/application/views/accumulate/component_results.php @@ -0,0 +1,18 @@ +
    +
    +

    Results

    + Updated +
    +
    +
    + +
    + +
    +
    +
    +
    diff --git a/application/views/accumulate/index.php b/application/views/accumulate/index.php index 151361700..033b10316 100644 --- a/application/views/accumulate/index.php +++ b/application/views/accumulate/index.php @@ -1,22 +1,35 @@
    -

    - -
    +
    +
    +

    +

    Track cumulative award progress over time by band, mode, and period.

    +
    +
    - -
    - -
    +
    +
    +

    Filters

    +
    +
    + +
    +
    +
    - -
    +
    + - +
    - +
    - +
    - +
    +
    -
    -
    - - -
    -
    - - + +
    +
    + + +
    +
    + + +
    -
    - - -
    -
    - +
    +
    - - - - -
    - -
    +
    +
    +
    diff --git a/application/views/activators/component_table.php b/application/views/activators/component_table.php new file mode 100644 index 000000000..668a098c7 --- /dev/null +++ b/application/views/activators/component_table.php @@ -0,0 +1,56 @@ + + + + + + + + + + + + '; + + $activators = array(); + foreach ($activators_array as $line) { + $call = $line->call; + $grids = $line->grids; + $count = $line->count; + if (array_key_exists($line->call, $vucc_grids)) { + foreach(explode(',', $vucc_grids[$line->call]) as $vgrid) { + if(!strpos($grids, $vgrid)) { + $grids .= ','.$vgrid; + } + } + $grids = str_replace(' ', '', $grids); + $grid_array = explode(',', $grids); + sort($grid_array); + $count = count($grid_array); + $grids = implode(', ', $grid_array); + } + array_push($activators, array($count, $call, $grids)); + } + arsort($activators); + foreach ($activators as $line) { + echo ' + + + + + + + '; + } + echo '
    #' . lang('gen_hamradio_callsign') . '' . lang('general_word_count') . '' . lang('gridsquares_gridsquares') . '' . lang('gridsquares_show_qsos') . '' . lang('gridsquares_show_map') . '
    ' . $i++ . ''.$line[1].''.$line[0].''.$line[2].'
    '; +} + +write_activators($activators_array, $vucc_grids, $custom_date_format, $band, $leogeo); diff --git a/application/views/activators/index.php b/application/views/activators/index.php index ef899bc17..d3059e54e 100644 --- a/application/views/activators/index.php +++ b/application/views/activators/index.php @@ -1,12 +1,21 @@
    -

    +
    +
    +

    +

    View activators and their activated gridsquares by band and satellite type.

    +
    +
    -
    - -
    - +
    +
    +

    Filters

    +
    +
    + +
    -
    -
    -
    - -
    -
    -
    -
    - +
    -
    +
    + Reset + +
    - -
    - -
    - -
    -
    - - + +
    +
    config->item('qso_date_format'); } ?> - call] = $line->vucc_grids; - } - } - if( $this->input->post('band') != NULL) { - if ($activators_array) { - $result = write_activators($activators_array, $vucc_grids, $custom_date_format, $this->input->post('band'), $this->input->post('leogeo')); + +
    + input->post('band') != NULL) { + $vucc_grids = array(); + if ($activators_vucc_array) { + foreach ($activators_vucc_array as $line) { + $vucc_grids[$line->call] = $line->vucc_grids; + } + } + if ($activators_array) { + $this->load->view('activators/component_table', array( + 'activators_array' => $activators_array, + 'vucc_grids' => $vucc_grids, + 'custom_date_format' => $custom_date_format, + 'band' => $this->input->post('band'), + 'leogeo' => $this->input->post('leogeo') + )); + } + else { + echo ''; + } } - else { - echo ''; - } - } - ?> + ?> +
    - - +document.getElementById('band').addEventListener('change', function() { + const leogeoContainer = document.getElementById('leogeoContainer'); + if (this.value === 'SAT') { + leogeoContainer.style.display = 'block'; + } else { + leogeoContainer.style.display = 'none'; } - $i = 1; - echo ' - - - - - - - - - - - '; +}); + + - $activators = array(); - foreach ($activators_array as $line) { - $call = $line->call; - $grids = $line->grids; - $count = $line->count; - if (array_key_exists($line->call, $vucc_grids)) { - foreach(explode(',', $vucc_grids[$line->call]) as $vgrid) { - if(!strpos($grids, $vgrid)) { - $grids .= ','.$vgrid; - } - } - $grids = str_replace(' ', '', $grids); - $grid_array = explode(',', $grids); - sort($grid_array); - $count = count($grid_array); - $grids = implode(', ', $grid_array); - } - array_push($activators, array($count, $call, $grids)); - } - arsort($activators); - foreach ($activators as $line) { - echo ' - - - - - - - '; - } - echo '
    #' . lang('gen_hamradio_callsign') . '' . lang('general_word_count') . '' . lang('gridsquares_gridsquares') . '' . lang('gridsquares_show_qsos') . '' . lang('gridsquares_show_map') . '
    ' . $i++ . ''.$line[1].''.$line[0].''.$line[2].'
    '; -} +
    diff --git a/application/views/continents/component_results.php b/application/views/continents/component_results.php new file mode 100644 index 000000000..04cdc6e44 --- /dev/null +++ b/application/views/continents/component_results.php @@ -0,0 +1,27 @@ +
    +
    +

    Continents

    + Updated +
    +
    +
    + +
    +
    +
    + + + + + + + + + +
    #Continent# of QSOs worked
    +
    +
    +
    +
    diff --git a/application/views/continents/index.php b/application/views/continents/index.php index a2482287f..cd45316fe 100644 --- a/application/views/continents/index.php +++ b/application/views/continents/index.php @@ -3,68 +3,61 @@ margin: 0 auto; } -
    - -

    - -

    -
    -