wavelog/application/controllers/Qso.php

913 lines
32 KiB
PHP
Raw Permalink Normal View History

2011-04-25 16:24:01 +01:00
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2025-01-09 09:02:58 +01:00
class QSO extends CI_Controller {
2025-02-13 14:44:53 +00:00
function __construct() {
parent::__construct();
2024-08-16 10:08:44 +02:00
if(!$this->user_model->authorize(2)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
2025-02-13 14:44:53 +00:00
$last_qso_count = empty($this->session->userdata('qso_page_last_qso_count')) ? QSO_PAGE_DEFAULT_QSOS_COUNT : $this->session->userdata('qso_page_last_qso_count');
2025-01-30 21:09:45 +01:00
$this->session->set_userdata('qso_page_last_qso_count', $last_qso_count);
}
public function index() {
$this->load->model('cat');
$this->load->library('qra');
$this->load->model('stations');
2011-07-22 01:08:47 +01:00
$this->load->model('logbook_model');
2025-06-23 10:40:32 +00:00
$this->load->model('usermodes');
$this->load->model('bands');
2024-08-16 10:08:44 +02:00
if(!$this->user_model->authorize(2)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
2025-02-13 14:44:53 +00:00
// Getting the live/post mode from GET command
// 0 = live
// 1 = post (manual)
$get_manual_mode = $this->input->get('manual', TRUE);
if ($get_manual_mode == '0' || $get_manual_mode == '1') {
$data['manual_mode'] = $get_manual_mode;
} else {
show_404();
}
2024-08-05 21:08:19 +02:00
if ($this->stations->check_station_is_accessible($this->session->userdata('station_profile_id') ?? 0)) { // Last Station from session accessible? Take it!
$data['active_station_profile'] = $this->session->userdata('station_profile_id');
} else {
$data['active_station_profile'] =$this->stations->find_active();
}
$data['notice'] = false;
2026-06-20 10:29:55 -07:00
if (!empty($this->session->userdata('user_stations_active_log_only'))) {
$data['stations'] = $this->logbooks_model->list_logbooks_linked($this->session->userdata('active_station_logbook'));
} else {
$data['stations'] = $this->stations->all_of_user();
}
Clubstations for Wavelog (#1334) * feat[clubstations]: New DB structure * feat[clubstations]: Add clubstationstable in user managment * feat[clubstations]: Show last operator * feat[clubstations]: Better solution for last operator. tnx for the hint @int2001 * feat[clubstations]: New Club Model and Controller * feat[clubstations]: Add "Add User" and "Edit User" functionality * docs[clubstations]: move comment * feat[clubstations]: Add "Delete Member" functionality * feat[clubstations]: some enhancements and javascript * fix[clubstations]: Wrong message class for flashmessages * feat[clubstations]: Added Switch in the Header menu (not functional yet) * feat[clubstations]: clubswitch modal * fix[clubstations]: Load encryption library if not already loaded * fix[clubstations]: Prevent direct login attempts to clubstations and enhance impersonation authorization * fix[clubstations]: Typo * feat[clubstations]: Only show the operator dialog if there is something fishy * fix[user]: little UI bug * feat[impersonate]: Add source uid to session data * fix[impersonate]: logic adjustment * feat[clubstations]: Add manage button in header menu for club officers * fix[clubstations]: typo in permission level check * fix[clubstations]: Full rights for the admin * feat[impersonate]: Custom sessiondata * feat[impersonate]: Implement stop impersonation feature with modal confirmation; "the way back" * fix(modal): Fix bug where modal was hidden when mouse leaved the browser content * docs(config): Adjust config description for special callsigns and clubstations * feat(club): Add club access check helper * typo * fix[impersonation]: Better text * feat(club): Selectize for a efficient user search * feat(clubstations): Restrict clubstations based on users permission level part 1/x * adjustments for dev merge * Adjusted club right for the advanced logbook * feat[user]: Refactoring of the Action Buttons in the user table * fix[club_permissions]: normal button instead small one for club permissions * remove unnecessary line break in modal body * feat[clubstations]: Add Club Mode badge to the header * fix[clubstations]: fix maintenance mode * allow switch back on http * feat(simplefle): display operator input based on club_access * small UI adjustments * small UI adjustments * moved api page to a index.php file and added support for clubstations * removed unused stuff * typo * radios and api keys * missed one binding * fix qso view, even officers do just see their own radios in QSO logging * omit the need for a relogin to see the changes as an admin * Omit the need for relogin after club changes in general. It's a question of UX. It's better to accept a little higher DB load (if clubstations are enabled) then the need of an user to relogin. There is some room for improvement by changing user_model->get_by_id() and adding a join there. This can be done later if we see that the load is too high * If the user is not the creator of the API key, it's likely a clubstation. In this case the callsign of the clubstation can not be the same as the callsign of the user (operator call provided by the user). If this is the case, we need to use the callsign of the creator of the API key * remove debug messages * better UI in header * found a typo * full access in clubstations for admins (if accessed via admin usertable) * adjusted text * adjusted text * adjust text * reduce required chars * bugfix: missing the correct authentication in case the admin was not member of the club. he wasn't able to switch back * reduce debug messages * fixed UI bug related to tooltips * load js in controller * upps.. * some UI adjustments * corrected permissions * if user gets delete we need to remove data in club_permissions and also api keys which were created by this user * Notify members about new memberships or changes in permission level * add spinner to save button * make login/logout process more bulletproof * remove the relogin cookie after the attempt * better strategy * bug where switch back failed if user is no admin * make api keys more secure * mask not owned api keys * removed annoying link * if a user gets removed from a club we also should delete the corresponding api keys and cat radios * adjusted wiki link * Auto creation of logbook and location when new user is created * store and display locator in uppercase * same for callsign * fixed a bug in user/club creation * Revert "Auto creation of logbook and location when new user is created" We found another solution to which will be addressed in a second PR This reverts commit f05f4b7bf0423a88abf0087ade81fe613d217794. * Optimized SQL for stats at userlist * Source query for lastop "out", because mysql<9.0 can't handle Windowed functions * adjust migration * add new columns to users table to get created_at and modified_at * added a partial down function * add operator dropdown for clubstations * fix mig version * Add some backend restrictions in case a user wants to try something funny with the club --------- Co-authored-by: Andreas Kristiansen <6977712+AndreasK79@users.noreply.github.com> Co-authored-by: int2001 <joerg@dj7nt.de>
2025-01-02 10:22:23 +01:00
$data['radios'] = $this->cat->radios(true);
$data['radio_last_updated'] = $this->cat->last_updated()->row();
2025-01-30 21:09:45 +01:00
$data['query'] = $this->logbook_model->last_custom($this->session->userdata('qso_page_last_qso_count'));
$this->load->is_loaded('worker') ?: $this->load->library('worker');
$data['worker_enabled'] = $this->worker->is_enabled(); // without this line the worker.js is not loaded!
$data['past_contacts_worker'] = null;
$user_id = $this->session->userdata('user_id') ?? null;
if ($this->worker->is_enabled() && $user_id) {
2026-07-10 23:00:31 +02:00
// qso past contacts (last 5) component
$topic = 'qso.' . $user_id;
$this->worker->register_topic($topic);
$data['past_contacts_worker'] = ['topic' => $topic, 'token' => $this->worker->create_token($topic)];
2026-07-10 23:00:31 +02:00
// radio polling: keyed by radio id so cat.js can look up the selected radio
$radio_worker_topics = [];
foreach ($data['radios']->result() as $radio) {
$topic = 'radio.' . $radio->id;
$this->worker->register_topic($topic);
$radio_worker_topics[$radio->id] = ['topic' => $topic, 'token' => $this->worker->create_token($topic)];
}
$data['radio_worker_topics'] = $radio_worker_topics;
}
$data['dxcc'] = $this->logbook_model->fetchDxcc();
$data['iota'] = $this->logbook_model->fetchIota();
2025-06-23 10:40:32 +00:00
$data['modes'] = $this->usermodes->active();
2022-10-05 23:23:32 +02:00
$data['bands'] = $this->bands->get_user_bands_for_qso_entry();
[$data['lat'], $data['lng']] = $this->qra->qra2latlong($this->stations->gridsquare_from_station($this->stations->find_active()));
$data['user_default_band'] = $this->session->userdata('user_default_band');
2022-10-05 23:23:32 +02:00
$data['sat_active'] = array_search("SAT", $this->bands->get_user_bands(), true);
$qkey_opt=$this->user_options_model->get_options('qso_tab',array('option_name'=>'iota','option_key'=>'show'))->result();
if (count($qkey_opt)>0) {
$data['user_iota_to_qso_tab'] = $qkey_opt[0]->option_value;
} else {
$data['user_iota_to_qso_tab'] = 0;
}
2024-03-22 07:57:31 +01:00
$qkey_opt=$this->user_options_model->get_options('qso_tab',array('option_name'=>'sota','option_key'=>'show'))->result();
if (count($qkey_opt)>0) {
$data['user_sota_to_qso_tab'] = $qkey_opt[0]->option_value;
} else {
$data['user_sota_to_qso_tab'] = 0;
}
2024-03-22 08:06:54 +01:00
$qkey_opt=$this->user_options_model->get_options('qso_tab',array('option_name'=>'wwff','option_key'=>'show'))->result();
if (count($qkey_opt)>0) {
$data['user_wwff_to_qso_tab'] = $qkey_opt[0]->option_value;
} else {
$data['user_wwff_to_qso_tab'] = 0;
}
2024-03-22 08:15:45 +01:00
$qkey_opt=$this->user_options_model->get_options('qso_tab',array('option_name'=>'pota','option_key'=>'show'))->result();
if (count($qkey_opt)>0) {
$data['user_pota_to_qso_tab'] = $qkey_opt[0]->option_value;
} else {
$data['user_pota_to_qso_tab'] = 0;
}
2024-03-22 08:26:26 +01:00
$qkey_opt=$this->user_options_model->get_options('qso_tab',array('option_name'=>'sig','option_key'=>'show'))->result();
if (count($qkey_opt)>0) {
$data['user_sig_to_qso_tab'] = $qkey_opt[0]->option_value;
} else {
$data['user_sig_to_qso_tab'] = 0;
}
2024-03-22 08:35:02 +01:00
$qkey_opt=$this->user_options_model->get_options('qso_tab',array('option_name'=>'dok','option_key'=>'show'))->result();
if (count($qkey_opt)>0) {
$data['user_dok_to_qso_tab'] = $qkey_opt[0]->option_value;
} else {
$data['user_dok_to_qso_tab'] = 0;
}
$qkey_opt=$this->user_options_model->get_options('qso_tab',array('option_name'=>'station','option_key'=>'show'))->result();
if (count($qkey_opt)>0) {
$data['user_station_to_qso_tab'] = $qkey_opt[0]->option_value;
} else {
$data['user_station_to_qso_tab'] = 0;
}
$qkey_opt = $this->user_options_model->get_options('qso_tab', array('option_name' => 'map', 'option_key' => 'show'))->result();
if (count($qkey_opt) > 0) {
$data['user_qso_show_map'] = $qkey_opt[0]->option_value;
} else {
$data['user_qso_show_map'] = 1; // default: show map
}
// Get status of DX Waterfall enable option
$qkey_opt=$this->user_options_model->get_options('dxwaterfall',array('option_name'=>'enable','option_key'=>'boolean'))->result();
if (count($qkey_opt)>0) {
$data['user_dxwaterfall_enable'] = $qkey_opt[0]->option_value;
2025-11-11 15:43:24 +01:00
$data['dxcluster_default_decont'] = $this->optionslib->get_option('dxcluster_decont') ?? 'EU';
$data['dxcluster_default_maxage'] = $this->optionslib->get_option('dxcluster_maxage') ?? 60;
} else {
$data['user_dxwaterfall_enable'] = 0;
2025-11-11 15:43:24 +01:00
// default but not used, prevent unset variable, without the need of a db call
$data['dxcluster_default_decont'] = 'EU';
$data['dxcluster_default_maxage'] = 60;
}
2025-01-30 21:09:45 +01:00
$data['qso_count'] = $this->session->userdata('qso_page_last_qso_count');
$this->load->library('form_validation');
$this->form_validation->set_rules('start_date', 'Date', 'required');
$this->form_validation->set_rules('start_time', 'Time', 'required');
$this->form_validation->set_rules('callsign', 'Callsign', 'required');
$this->form_validation->set_rules('band', 'Band', 'required');
$this->form_validation->set_rules('mode', 'Mode', 'required');
2026-08-12 11:05:03 +02:00
if (($this->input->post('locator') ?? '') != '') {
$this->form_validation->set_rules('locator', 'Locator', 'callback_check_locator[any]');
}
// [eQSL default msg] GET user options (option_type='eqsl_default_qslmsg'; option_name='key_station_id'; option_key=station_id) //
$options_object = $this->user_options_model->get_options('eqsl_default_qslmsg',array('option_name'=>'key_station_id','option_key'=>$data['active_station_profile']))->result();
$data['qslmsg'] = (isset($options_object[0]->option_value))?$options_object[0]->option_value:'';
$data['adif_propmodes'] = $this->config->item('adif_propmodes');
2025-06-03 07:55:35 +02:00
$footerData = [];
$footerData['scripts'] = [
'assets/js/leaflet/geocoding.js',
];
if ($this->form_validation->run() == FALSE) {
2024-06-08 11:01:59 +02:00
$data['page_title'] = __("Add QSO");
if (validation_errors() != '') { // we're coming from a failed ajax-call
echo json_encode(array('message' => 'Error','errors' => validation_errors()));
} else { // we're not coming from a POST
$this->load->view('interface_assets/header', $data);
$this->load->view('qso/index');
2025-06-03 07:55:35 +02:00
$this->load->view('interface_assets/footer', $footerData);
}
} else {
2011-07-22 01:08:47 +01:00
// Store Basic QSO Info for reuse
// Put data in an array first, then call set_userdata once.
// This solves the problem of CI dumping out the session
// cookie each time set_userdata is called.
// For more info, see http://bizhole.com/codeigniter-nginx-error-502-bad-gateway/
// $qso_data = [
// 18-Jan-2016 - make php v5.3 friendly!
$qso_data = array(
2024-08-13 11:04:36 +02:00
'start_date' => $this->input->post('start_date', TRUE),
'start_time' => $this->input->post('start_time', TRUE),
'end_time' => $this->input->post('end_time'),
'time_stamp' => time(),
2024-11-12 10:24:27 +00:00
'mail' => $this->input->post('mail', TRUE),
2024-08-13 11:04:36 +02:00
'band' => $this->input->post('band', TRUE),
'band_rx' => $this->input->post('band_rx', TRUE),
'freq' => $this->input->post('freq_display', TRUE),
'freq_rx' => $this->input->post('freq_display_rx', TRUE),
'mode' => $this->input->post('mode', TRUE),
'sat_name' => $this->input->post('sat_name', TRUE),
'sat_mode' => $this->input->post('sat_mode', TRUE),
'prop_mode' => $this->input->post('prop_mode', TRUE),
'radio' => $this->input->post('radio', TRUE),
'station_profile_id' => $this->input->post('station_profile', TRUE),
Clubstations for Wavelog (#1334) * feat[clubstations]: New DB structure * feat[clubstations]: Add clubstationstable in user managment * feat[clubstations]: Show last operator * feat[clubstations]: Better solution for last operator. tnx for the hint @int2001 * feat[clubstations]: New Club Model and Controller * feat[clubstations]: Add "Add User" and "Edit User" functionality * docs[clubstations]: move comment * feat[clubstations]: Add "Delete Member" functionality * feat[clubstations]: some enhancements and javascript * fix[clubstations]: Wrong message class for flashmessages * feat[clubstations]: Added Switch in the Header menu (not functional yet) * feat[clubstations]: clubswitch modal * fix[clubstations]: Load encryption library if not already loaded * fix[clubstations]: Prevent direct login attempts to clubstations and enhance impersonation authorization * fix[clubstations]: Typo * feat[clubstations]: Only show the operator dialog if there is something fishy * fix[user]: little UI bug * feat[impersonate]: Add source uid to session data * fix[impersonate]: logic adjustment * feat[clubstations]: Add manage button in header menu for club officers * fix[clubstations]: typo in permission level check * fix[clubstations]: Full rights for the admin * feat[impersonate]: Custom sessiondata * feat[impersonate]: Implement stop impersonation feature with modal confirmation; "the way back" * fix(modal): Fix bug where modal was hidden when mouse leaved the browser content * docs(config): Adjust config description for special callsigns and clubstations * feat(club): Add club access check helper * typo * fix[impersonation]: Better text * feat(club): Selectize for a efficient user search * feat(clubstations): Restrict clubstations based on users permission level part 1/x * adjustments for dev merge * Adjusted club right for the advanced logbook * feat[user]: Refactoring of the Action Buttons in the user table * fix[club_permissions]: normal button instead small one for club permissions * remove unnecessary line break in modal body * feat[clubstations]: Add Club Mode badge to the header * fix[clubstations]: fix maintenance mode * allow switch back on http * feat(simplefle): display operator input based on club_access * small UI adjustments * small UI adjustments * moved api page to a index.php file and added support for clubstations * removed unused stuff * typo * radios and api keys * missed one binding * fix qso view, even officers do just see their own radios in QSO logging * omit the need for a relogin to see the changes as an admin * Omit the need for relogin after club changes in general. It's a question of UX. It's better to accept a little higher DB load (if clubstations are enabled) then the need of an user to relogin. There is some room for improvement by changing user_model->get_by_id() and adding a join there. This can be done later if we see that the load is too high * If the user is not the creator of the API key, it's likely a clubstation. In this case the callsign of the clubstation can not be the same as the callsign of the user (operator call provided by the user). If this is the case, we need to use the callsign of the creator of the API key * remove debug messages * better UI in header * found a typo * full access in clubstations for admins (if accessed via admin usertable) * adjusted text * adjusted text * adjust text * reduce required chars * bugfix: missing the correct authentication in case the admin was not member of the club. he wasn't able to switch back * reduce debug messages * fixed UI bug related to tooltips * load js in controller * upps.. * some UI adjustments * corrected permissions * if user gets delete we need to remove data in club_permissions and also api keys which were created by this user * Notify members about new memberships or changes in permission level * add spinner to save button * make login/logout process more bulletproof * remove the relogin cookie after the attempt * better strategy * bug where switch back failed if user is no admin * make api keys more secure * mask not owned api keys * removed annoying link * if a user gets removed from a club we also should delete the corresponding api keys and cat radios * adjusted wiki link * Auto creation of logbook and location when new user is created * store and display locator in uppercase * same for callsign * fixed a bug in user/club creation * Revert "Auto creation of logbook and location when new user is created" We found another solution to which will be addressed in a second PR This reverts commit f05f4b7bf0423a88abf0087ade81fe613d217794. * Optimized SQL for stats at userlist * Source query for lastop "out", because mysql<9.0 can't handle Windowed functions * adjust migration * add new columns to users table to get created_at and modified_at * added a partial down function * add operator dropdown for clubstations * fix mig version * Add some backend restrictions in case a user wants to try something funny with the club --------- Co-authored-by: Andreas Kristiansen <6977712+AndreasK79@users.noreply.github.com> Co-authored-by: int2001 <joerg@dj7nt.de>
2025-01-02 10:22:23 +01:00
'operator_callsign' => $this->input->post('operator_callsign', TRUE) ?? $this->session->userdata('operator_callsign'),
2024-08-13 11:04:36 +02:00
'transmit_power' => $this->input->post('transmit_power', TRUE)
);
// ];
$this->session->set_userdata($qso_data);
// If SAT name is set make it session set to sat
2024-08-13 11:04:36 +02:00
if($this->input->post('sat_name', TRUE)) {
$this->session->set_userdata('prop_mode', 'SAT');
}
// All session writes are done, release the lock before the expensive part
session_write_close();
// Add QSO
// $this->logbook_model->add();
//change to create_qso function as add and create_qso duplicate functionality
$saveresult = json_decode($this->saveqso(), true);
// Clear POST data to prevent re-submission on page reload
$_POST = [];
$this->form_validation->reset_validation();
if (!is_array($saveresult) || empty($saveresult['qso_id'])) {
$returner = [
'message' => 'error',
'errors' => is_string($saveresult) ? $saveresult : __("QSO could not be saved"),
];
header('Content-Type: application/json; charset=utf-8');
echo json_encode($returner);
return;
}
$returner=[];
$actstation=$this->stations->find_active() ?? '';
$returner['activeStationId'] = $actstation;
$profile_info = $this->stations->profile($actstation)->row();
2024-09-12 12:54:30 +00:00
$returner['activeStationTXPower'] = xss_clean($profile_info->station_power ?? '');
$returner['activeStationOP'] = xss_clean($this->session->userdata('operator_callsign'));
2024-02-28 14:14:29 +00:00
$returner['message']='success';
// Include ADIF for WebSocket transmission
if (isset($saveresult['adif'])) {
$returner['adif'] = $saveresult['adif'];
}
header('Content-Type: application/json; charset=utf-8');
echo json_encode($returner);
}
}
/*
* This is used for contest-logging and the ajax-call
* Returns JSON
*/
public function saveqso() {
// CSRF mitigation: this endpoint is AJAX-only; reject plain form submissions
if ($this->input->server('HTTP_X_REQUESTED_WITH') !== 'XMLHttpRequest') {
$this->output->set_status_header(403)
->set_content_type('application/json')
->set_output(json_encode(['error' => 'Forbidden']));
return;
}
session_write_close();
2025-02-13 14:44:53 +00:00
$this->load->model('logbook_model');
2026-01-24 20:53:32 +01:00
$qso_data = [
'manual' => $this->input->get('manual', TRUE),
2026-01-24 20:53:32 +01:00
'start_date' => $this->input->post('start_date', TRUE),
'start_time' => $this->input->post('start_time', TRUE),
'end_time' => $this->input->post('end_time', TRUE),
'callsign' => $this->input->post('callsign', TRUE),
'prop_mode' => $this->input->post('prop_mode', TRUE) ?? '',
2026-01-24 20:53:32 +01:00
'email' => $this->input->post('email', TRUE) ?? NULL,
'region' => $this->input->post('region', TRUE) ?? NULL,
'exchangetype' => $this->input->post('exchangetype', TRUE) ?? NULL,
'exch_rcvd' => $this->input->post('exch_rcvd', TRUE) ?? NULL,
'exch_sent' => $this->input->post('exch_sent', TRUE) ?? NULL,
'exch_serial_r' => $this->input->post('exch_serial_r', TRUE) ?? NULL,
'exch_serial_s' => $this->input->post('exch_serial_s', TRUE) ?? NULL,
'contestname' => $this->input->post('contestname', TRUE) ?? NULL,
'transmit_power' => $this->input->post('transmit_power', TRUE) ?? NULL,
'radio' => $this->input->post('radio', TRUE) ?? 0,
'radio_ws_name' => $this->input->post('radio_ws_name', TRUE) ?? '',
'country' => $this->input->post('country', TRUE) ?? NULL,
'cqz' => $this->input->post('cqz', TRUE) ?? NULL,
'dxcc_id' => $this->input->post('dxcc_id', TRUE) ?? NULL,
'continent' => $this->input->post('continent', TRUE) ?? NULL,
'mode' => $this->input->post('mode', TRUE) ?? NULL,
'county' => $this->input->post('county', TRUE) ?? NULL,
'input_state' => $this->input->post('input_state', TRUE) ?? NULL,
'ant_az' => $this->input->post('ant_az', TRUE) ?? NULL,
'ant_el' => $this->input->post('ant_el', TRUE) ?? NULL,
'ant_path' => $this->input->post('ant_path', TRUE) ?? NULL,
'darc_dok' => $this->input->post('darc_dok', TRUE) ?? NULL,
'locator' => $this->input->post('locator', TRUE) ?? NULL,
'qth' => $this->input->post('qth', TRUE) ?? NULL,
'name' => $this->input->post('name', TRUE) ?? NULL,
'copyexchangeto' => $this->input->post('copyexchangeto', TRUE) ?? NULL,
'qsl_sent' => $this->input->post('qsl_sent', TRUE) ?? 'N',
'qsl_rcvd' => $this->input->post('qsl_rcvd', TRUE) ?? 'N',
'band' => $this->input->post('band', TRUE) ?? NULL,
'band_rx' => $this->input->post('band_rx', TRUE) ?? NULL,
'freq_display' => $this->input->post('freq_display', TRUE) ?? NULL,
'rst_rcvd' => $this->input->post('rst_rcvd', TRUE) ?? NULL,
'rst_sent' => $this->input->post('rst_sent', TRUE) ?? NULL,
'comment' => $this->input->post('comment', TRUE) ?? NULL,
'sat_name' => $this->input->post('sat_name', TRUE) ?? NULL,
'sat_mode' => $this->input->post('sat_mode', TRUE) ?? NULL,
'qsl_sent_method' => $this->input->post('qsl_sent_method', TRUE) ?? NULL,
'qsl_rcvd_method' => $this->input->post('qsl_rcvd_method', TRUE) ?? NULL,
'qsl_via' => $this->input->post('qsl_via', TRUE) ?? NULL,
'qslmsg' => $this->input->post('qslmsg', TRUE) ?? NULL,
'operator_callsign' => $this->input->post('operator_callsign', TRUE) ?? NULL,
'iota_ref' => $this->input->post('iota_ref', TRUE) ?? NULL,
'freq_display_rx' => $this->input->post('freq_display_rx', TRUE) ?? NULL,
'ituz' => $this->input->post('ituz', TRUE) ?? NULL,
'sota_ref' => $this->input->post('sota_ref', TRUE) ?? NULL,
'wwff_ref' => $this->input->post('wwff_ref', TRUE) ?? NULL,
'pota_ref' => $this->input->post('pota_ref', TRUE) ?? NULL,
'sig' => $this->input->post('sig', TRUE) ?? NULL,
'sig_info' => $this->input->post('sig_info', TRUE) ?? NULL,
'notes' => $this->input->post('notes', TRUE) ?? NULL,
'station_profile' => $this->input->post('station_profile', TRUE) ?? NULL,
'isSFLE' => $this->input->post('isSFLE', TRUE) ?? NULL,
'distance' => $this->input->post('distance', TRUE) ?? TRUE
];
$result = $this->logbook_model->create_qso($qso_data);
return json_encode($result, JSON_PRETTY_PRINT);
2025-02-13 14:44:53 +00:00
}
2025-02-13 14:44:53 +00:00
function winkeysettings() {
2025-10-15 14:32:48 +02:00
$this->load->model('user_options_model');
2025-10-15 14:32:48 +02:00
$cwmacros = [];
for ($i = 1; $i <= 10; $i++) {
$row = $this->user_options_model
->get_options('cwmacros', ['option_name' => "macro{$i}"])
->row();
2025-10-15 19:35:23 +02:00
$decoded = json_decode($row->option_value ?? '');
2025-10-15 14:32:48 +02:00
$name = isset($decoded->name) ? $decoded->name : '';
$macro = isset($decoded->macro) ? $decoded->macro : '';
2025-10-15 14:32:48 +02:00
$cwmacros["macro{$i}"] = [
'name' => $name,
'macro' => $macro,
];
}
2025-10-23 18:50:49 +02:00
// Check if all are empty
$allEmpty = true;
foreach ($cwmacros as $macro) {
if (!empty($macro['name']) || !empty($macro['macro'])) {
$allEmpty = false;
break;
}
}
// Apply defaults to first 5 if all are empty
if ($allEmpty) {
$cwmacros['macro1'] = ['name' => 'CQ', 'macro' => 'CQ CQ CQ DE [MYCALL] [MYCALL] K'];
$cwmacros['macro2'] = ['name' => 'REPT', 'macro' => '[CALL] DE [MYCALL] [RSTS] [RSTS] K'];
$cwmacros['macro3'] = ['name' => 'TU', 'macro' => '[CALL] TU 73 DE [MYCALL] K'];
$cwmacros['macro4'] = ['name' => 'QRZ', 'macro' => 'QRZ DE [MYCALL] K'];
$cwmacros['macro5'] = ['name' => 'TEST', 'macro' => 'TEST DE [MYCALL] K'];
}
// Load ESM (Enter Sends Message) config, fall back to sensible defaults
$esmRow = $this->user_options_model->get_options('cwmacros', ['option_name' => 'esm'])->row();
$esmDecoded = json_decode($esmRow->option_value ?? '');
$cwmacros['esm'] = [
'enabled' => isset($esmDecoded->enabled) ? (int) $esmDecoded->enabled : 0,
'cq' => isset($esmDecoded->cq) ? (int) $esmDecoded->cq : 1,
'qrz' => isset($esmDecoded->qrz) ? (int) $esmDecoded->qrz : 4,
'exchange' => isset($esmDecoded->exchange) ? (int) $esmDecoded->exchange : 2,
'tu' => isset($esmDecoded->tu) ? (int) $esmDecoded->tu : 3,
'sp' => isset($esmDecoded->sp) ? (int) $esmDecoded->sp : 4,
'sp_exch' => isset($esmDecoded->sp_exch) ? (int) $esmDecoded->sp_exch : 2,
];
New Wavelog Contesting and Basics for Wavelog Worker (#3063) * initial commit for new contesting in Wavelog * implemented cache buster to match logic from upstream dev branch * refactore data-store to idb since this will be much faster with a lot of QSOs (scale for the future) * implementation of other exchange types (wip) * updated migration version * renamed mig * renamed mig * updated mig * ESC Handler to reset form * Add Websocket * stretch maxDuration warning We can stretch the maxDuration warning to 2 seconds to allow for more network latency and processing time. The heartbeat is protected against multiple parallel requests, so we can afford to be more lenient with the duration before showing a warning. This should help reduce false positives in environments with higher latency or slower processing. * worked before warning in qso form While input the logic checks in the qso list if the callisgn already exists on the same band and mode. * translations * fix band buttons * updated migration after release 2.4.2 * Fixing create new contest, add clubstation permission check, add time check to contests * Adding attach QSO to Contest * Remove redundant clubstation checks * Fix function tip * Remove time check when launch contest * Fixing contest attach main logbook, adding detach contest qso * Enable clubstation contest QSO detach attach * add space handler for callsign input * set rst default to 599 for cw, 59 for others * add grid and refactor some ui to make a better fitting here and there * adjust tabindex * add "copy exchange to" feature * fix css for all themes * adif and cbr export * fix bug with wrong operator callsign * add exchange type "exchange+grid" * introduce new logic for exchangetypes and more flexible order selection * fix broken qso list scroll * legacy import feature * more precise wording * feat: inline editing of QSOs by double click in the QSO list * redirect cabrillo to contesting manager * Fix QSO permission check * remove unused cabrillo stuff * remove duplicate function * fix check if worked before on band change tnx to @int2001 * fix flash message for legacy importer * make dropdown searchable and place "other" at the top * add hint about "Other" contest * avoid "flash" of winkeyer settings in qso logging, it shows up if cw is set as mode * winkeyer for the new contesting need proper testing by @AndreasK79 and @phl0 since I don't have a winkeyer myself. I tested with a python simulator... * english comments * proper singular/plural * fix dropdown * preselect active station location * allow editing time aswell * fix fontsize in radio buttons * remove redundant qso count query * cache last updated value for contest qsos to reduce db load * index on user id * cache also qso count * you can now resize windows also on the edges requested by @xyz667 in https://github.com/wavelog/wavelog/pull/3063#issuecomment-4557850867 * fix oversized clock * click to prepare logging for contesting Logic: - if qso windows is open, call is sent to qso form - if contest log is open, call is sent to contest log - if both are open, call is sent to contest log - if none is open, click opens qso form and call is sent to qso form * handle exchange s prefill with data from the last qso and make exchange always uppercase * add dropdown to qso list with option to delete qsos * language fixes * english comments * center text * fix missing dropdown on new qsos * add date presets for contest session creator/editor * basic worker implementation * support multiple workers for clustering * remove unnecessary node column * simple availability check * websocket for contesting * bonus: add vip for visibilty in debug view * use vip if available * worker disabled in debug view if disabled in config * racecondition for winkeyer breaks edit/delete dropdown * racecondition for winkeyer breaks edit/delete dropdown * qso handling for worker in contesting, no high interval heartbeat anymore * clear heartbeat intervall * fix some timing bugs * remove dedicated worker controller as it's not needed * handle whitespace * trigger sync engine after processing QSO sync response and adjust last sync time to avoid spurious resyncs * fix set and setLocal * fix change detection and sync logic * fix the lost of seconds due to edit * fix table rendering * watermark handling for true delta * remove caching as it brings more problems here as benefits * make serial as default exchangetype * Add clarification on cluster setup requirements for worker URLs * add operator to qso list if this is a clubstation * fix bug for editing qsos in clubmode * remove redundant code * integers must be null in database * fix bug in tabindex if 3 exchangefields are configured (serial, exchange, grid) * nice contesting map (draft) * map autofit * add grid overlay * calculate distanz and azimuth * include rotor control via waveloggate (ws only) * implement simple dedicated callbook lookup * simpler and faster lookup * option in contest session to disabled callbook lookup and only calculate dxcc * syntax issue * fix bug in CBR export * catch empty result on contest * remove some leftovers * removed some comments * remove unused stuff * removed old todos/comments * add 4s timeout for ajax transport * you need to be admin for this * translations * add option to delete qso's aswell when deleting a session (opt in) * add quickstart button in manager * removed old todo * fix "grows to bottom" * disable "copy exchange to" if no exchange is set * clear map on qso clear aswell * same for scp * updated documentation link * calculate distanze and azimut also on grid only * better space usage in qso input * callbook db first approach, online lookup can be disabled while being cached for all users when enabled * some input validation * use only one sot for qrgtoband * basic stats component * fix html encoding * render all aswell * ascii * store locally which time frame the user wants * sync settings and warn user if something happend on the backend * dev leftover * show errors if callbook fails or callsign is invalid * easier syntax * Revert "easier syntax" - that was a mistake This reverts commit 80c75dbf1e6c832f8d0616ea56b8b55d96c36418. * fix for callbook lookup * make pathline more visible * better visibility of qsos * add classic coordinates bar to the map * fix bug in stats * stretch max duration to 4s.. just in case * simple sanity check in js to prevent unnecessary callbook lookups * simulate tab after filling the call from scp * fix poll radios with enabled worker * reset po/mo files back to upstream/dev * split contest exports, add back reg1test edi format * keep the menu item but redirect to contesting * better syntax and bugfix * break it down further and fix typo ("constraint") * use monospace font for input fields * fix: in worker driven mode we need to do the heartbeat also on not focused windows otherwise we loose this contact * fix: error handling on invalid times * wavelog Ø * more styling * band lowercase fix * Other is not a very descriptive title for a contest logger window, so we rename it to "Contest" * custom contest name * compare the whole array instead just a few settings --------- Co-authored-by: int2001 <joerg@dj7nt.de> Co-authored-by: HadleySo <71105018+HadleySo@users.noreply.github.com> Co-authored-by: DB4SCW <dev@db4scw.de>
2026-06-14 14:23:00 +02:00
$cwmacros['contest_context'] = (bool) $this->input->post('contest', true);
2025-10-15 14:32:48 +02:00
$this->load->view('qso/components/winkeysettings', $cwmacros);
}
2025-10-23 18:50:49 +02:00
2025-10-15 14:32:48 +02:00
function cwmacrosave(){
$this->load->model('user_options_model');
for ($i = 1; $i <= 10; $i++) {
$data = [
'name' => $this->input->post("function{$i}_name", TRUE),
'macro' => $this->input->post("function{$i}_macro", TRUE),
];
$this->user_options_model->set_option('cwmacros', "macro{$i}", array("macro{$i}" => json_encode($data)));
}
$esm = [
'enabled' => (int) $this->input->post('esm_enabled', TRUE),
'cq' => (int) $this->input->post('esm_cq', TRUE),
'qrz' => (int) $this->input->post('esm_qrz', TRUE),
'exchange' => (int) $this->input->post('esm_exchange', TRUE),
'tu' => (int) $this->input->post('esm_tu', TRUE),
'sp' => (int) $this->input->post('esm_sp', TRUE),
'sp_exch' => (int) $this->input->post('esm_sp_exch', TRUE),
];
$this->user_options_model->set_option('cwmacros', 'esm', array('esm' => json_encode($esm)));
2025-02-13 14:44:53 +00:00
echo "Macros Saved, Press Close and lets get sending!";
}
2023-05-17 21:19:18 +01:00
2025-02-13 14:44:53 +00:00
function cwmacros_json() {
2025-10-15 14:32:48 +02:00
$this->load->model('user_options_model');
$cwmacros = [];
for ($i = 1; $i <= 10; $i++) {
$row = $this->user_options_model
->get_options('cwmacros', ['option_name' => "macro{$i}"])
->row();
// Decode JSON stored in option_value
2025-10-23 18:50:49 +02:00
$decoded = json_decode($row->option_value ?? '');
2025-10-15 14:32:48 +02:00
// Make sure it's an object (in case it's null)
$name = isset($decoded->name) ? $decoded->name : '';
$macro = isset($decoded->macro) ? $decoded->macro : '';
$cwmacros["macro{$i}"] = [
'name' => $name,
'macro' => $macro,
];
}
// Build the JSON result structure
$result = [];
$i = 1;
foreach ($cwmacros as $macro) {
$result["function{$i}_name"] = $macro['name'];
$result["function{$i}_macro"] = $macro['macro'];
$i++;
}
2023-08-01 11:40:32 +01:00
// ESM (Enter Sends Message) config with defaults
$esmRow = $this->user_options_model->get_options('cwmacros', ['option_name' => 'esm'])->row();
$esmDecoded = json_decode($esmRow->option_value ?? '');
$result['esm_enabled'] = isset($esmDecoded->enabled) ? (int) $esmDecoded->enabled : 0;
$result['esm_cq'] = isset($esmDecoded->cq) ? (int) $esmDecoded->cq : 1;
$result['esm_qrz'] = isset($esmDecoded->qrz) ? (int) $esmDecoded->qrz : 4;
$result['esm_exchange'] = isset($esmDecoded->exchange) ? (int) $esmDecoded->exchange : 2;
$result['esm_tu'] = isset($esmDecoded->tu) ? (int) $esmDecoded->tu : 3;
$result['esm_sp'] = isset($esmDecoded->sp) ? (int) $esmDecoded->sp : 4;
$result['esm_sp_exch'] = isset($esmDecoded->sp_exch) ? (int) $esmDecoded->sp_exch : 2;
2025-10-15 14:32:48 +02:00
// Output as JSON
2025-02-13 14:44:53 +00:00
header('Content-Type: application/json; charset=utf-8');
2025-10-15 14:32:48 +02:00
echo json_encode($result, JSON_PRETTY_PRINT);
2023-08-01 11:40:32 +01:00
2025-02-13 14:44:53 +00:00
}
2023-08-01 11:40:32 +01:00
2025-02-13 14:44:53 +00:00
function edit_ajax() {
2020-08-24 20:16:06 +02:00
2025-02-13 14:44:53 +00:00
$this->load->model('logbook_model');
$this->load->model('modes');
$this->load->model('bands');
New Wavelog Contesting and Basics for Wavelog Worker (#3063) * initial commit for new contesting in Wavelog * implemented cache buster to match logic from upstream dev branch * refactore data-store to idb since this will be much faster with a lot of QSOs (scale for the future) * implementation of other exchange types (wip) * updated migration version * renamed mig * renamed mig * updated mig * ESC Handler to reset form * Add Websocket * stretch maxDuration warning We can stretch the maxDuration warning to 2 seconds to allow for more network latency and processing time. The heartbeat is protected against multiple parallel requests, so we can afford to be more lenient with the duration before showing a warning. This should help reduce false positives in environments with higher latency or slower processing. * worked before warning in qso form While input the logic checks in the qso list if the callisgn already exists on the same band and mode. * translations * fix band buttons * updated migration after release 2.4.2 * Fixing create new contest, add clubstation permission check, add time check to contests * Adding attach QSO to Contest * Remove redundant clubstation checks * Fix function tip * Remove time check when launch contest * Fixing contest attach main logbook, adding detach contest qso * Enable clubstation contest QSO detach attach * add space handler for callsign input * set rst default to 599 for cw, 59 for others * add grid and refactor some ui to make a better fitting here and there * adjust tabindex * add "copy exchange to" feature * fix css for all themes * adif and cbr export * fix bug with wrong operator callsign * add exchange type "exchange+grid" * introduce new logic for exchangetypes and more flexible order selection * fix broken qso list scroll * legacy import feature * more precise wording * feat: inline editing of QSOs by double click in the QSO list * redirect cabrillo to contesting manager * Fix QSO permission check * remove unused cabrillo stuff * remove duplicate function * fix check if worked before on band change tnx to @int2001 * fix flash message for legacy importer * make dropdown searchable and place "other" at the top * add hint about "Other" contest * avoid "flash" of winkeyer settings in qso logging, it shows up if cw is set as mode * winkeyer for the new contesting need proper testing by @AndreasK79 and @phl0 since I don't have a winkeyer myself. I tested with a python simulator... * english comments * proper singular/plural * fix dropdown * preselect active station location * allow editing time aswell * fix fontsize in radio buttons * remove redundant qso count query * cache last updated value for contest qsos to reduce db load * index on user id * cache also qso count * you can now resize windows also on the edges requested by @xyz667 in https://github.com/wavelog/wavelog/pull/3063#issuecomment-4557850867 * fix oversized clock * click to prepare logging for contesting Logic: - if qso windows is open, call is sent to qso form - if contest log is open, call is sent to contest log - if both are open, call is sent to contest log - if none is open, click opens qso form and call is sent to qso form * handle exchange s prefill with data from the last qso and make exchange always uppercase * add dropdown to qso list with option to delete qsos * language fixes * english comments * center text * fix missing dropdown on new qsos * add date presets for contest session creator/editor * basic worker implementation * support multiple workers for clustering * remove unnecessary node column * simple availability check * websocket for contesting * bonus: add vip for visibilty in debug view * use vip if available * worker disabled in debug view if disabled in config * racecondition for winkeyer breaks edit/delete dropdown * racecondition for winkeyer breaks edit/delete dropdown * qso handling for worker in contesting, no high interval heartbeat anymore * clear heartbeat intervall * fix some timing bugs * remove dedicated worker controller as it's not needed * handle whitespace * trigger sync engine after processing QSO sync response and adjust last sync time to avoid spurious resyncs * fix set and setLocal * fix change detection and sync logic * fix the lost of seconds due to edit * fix table rendering * watermark handling for true delta * remove caching as it brings more problems here as benefits * make serial as default exchangetype * Add clarification on cluster setup requirements for worker URLs * add operator to qso list if this is a clubstation * fix bug for editing qsos in clubmode * remove redundant code * integers must be null in database * fix bug in tabindex if 3 exchangefields are configured (serial, exchange, grid) * nice contesting map (draft) * map autofit * add grid overlay * calculate distanz and azimuth * include rotor control via waveloggate (ws only) * implement simple dedicated callbook lookup * simpler and faster lookup * option in contest session to disabled callbook lookup and only calculate dxcc * syntax issue * fix bug in CBR export * catch empty result on contest * remove some leftovers * removed some comments * remove unused stuff * removed old todos/comments * add 4s timeout for ajax transport * you need to be admin for this * translations * add option to delete qso's aswell when deleting a session (opt in) * add quickstart button in manager * removed old todo * fix "grows to bottom" * disable "copy exchange to" if no exchange is set * clear map on qso clear aswell * same for scp * updated documentation link * calculate distanze and azimut also on grid only * better space usage in qso input * callbook db first approach, online lookup can be disabled while being cached for all users when enabled * some input validation * use only one sot for qrgtoband * basic stats component * fix html encoding * render all aswell * ascii * store locally which time frame the user wants * sync settings and warn user if something happend on the backend * dev leftover * show errors if callbook fails or callsign is invalid * easier syntax * Revert "easier syntax" - that was a mistake This reverts commit 80c75dbf1e6c832f8d0616ea56b8b55d96c36418. * fix for callbook lookup * make pathline more visible * better visibility of qsos * add classic coordinates bar to the map * fix bug in stats * stretch max duration to 4s.. just in case * simple sanity check in js to prevent unnecessary callbook lookups * simulate tab after filling the call from scp * fix poll radios with enabled worker * reset po/mo files back to upstream/dev * split contest exports, add back reg1test edi format * keep the menu item but redirect to contesting * better syntax and bugfix * break it down further and fix typo ("constraint") * use monospace font for input fields * fix: in worker driven mode we need to do the heartbeat also on not focused windows otherwise we loose this contact * fix: error handling on invalid times * wavelog Ø * more styling * band lowercase fix * Other is not a very descriptive title for a contest logger window, so we rename it to "Contest" * custom contest name * compare the whole array instead just a few settings --------- Co-authored-by: int2001 <joerg@dj7nt.de> Co-authored-by: HadleySo <71105018+HadleySo@users.noreply.github.com> Co-authored-by: DB4SCW <dev@db4scw.de>
2026-06-14 14:23:00 +02:00
$this->load->model('contest_admin_model');
2020-08-24 20:16:06 +02:00
2025-02-13 14:44:53 +00:00
$this->load->library('form_validation');
2020-08-24 20:16:06 +02:00
2025-02-13 14:44:53 +00:00
if(!$this->user_model->authorize(2)) {
$this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard');
}
2025-02-13 14:44:53 +00:00
$id = str_replace('"', "", $this->input->post("id", TRUE));
$query = $this->logbook_model->qso_info($id);
2020-08-24 20:16:06 +02:00
2025-02-13 14:44:53 +00:00
$data['qso'] = $query->row();
$data['dxcc'] = $this->logbook_model->fetchDxcc();
$data['iota'] = $this->logbook_model->fetchIota();
$data['modes'] = $this->modes->all();
$data['bands'] = $this->bands->get_user_bands_for_qso_entry(true);
New Wavelog Contesting and Basics for Wavelog Worker (#3063) * initial commit for new contesting in Wavelog * implemented cache buster to match logic from upstream dev branch * refactore data-store to idb since this will be much faster with a lot of QSOs (scale for the future) * implementation of other exchange types (wip) * updated migration version * renamed mig * renamed mig * updated mig * ESC Handler to reset form * Add Websocket * stretch maxDuration warning We can stretch the maxDuration warning to 2 seconds to allow for more network latency and processing time. The heartbeat is protected against multiple parallel requests, so we can afford to be more lenient with the duration before showing a warning. This should help reduce false positives in environments with higher latency or slower processing. * worked before warning in qso form While input the logic checks in the qso list if the callisgn already exists on the same band and mode. * translations * fix band buttons * updated migration after release 2.4.2 * Fixing create new contest, add clubstation permission check, add time check to contests * Adding attach QSO to Contest * Remove redundant clubstation checks * Fix function tip * Remove time check when launch contest * Fixing contest attach main logbook, adding detach contest qso * Enable clubstation contest QSO detach attach * add space handler for callsign input * set rst default to 599 for cw, 59 for others * add grid and refactor some ui to make a better fitting here and there * adjust tabindex * add "copy exchange to" feature * fix css for all themes * adif and cbr export * fix bug with wrong operator callsign * add exchange type "exchange+grid" * introduce new logic for exchangetypes and more flexible order selection * fix broken qso list scroll * legacy import feature * more precise wording * feat: inline editing of QSOs by double click in the QSO list * redirect cabrillo to contesting manager * Fix QSO permission check * remove unused cabrillo stuff * remove duplicate function * fix check if worked before on band change tnx to @int2001 * fix flash message for legacy importer * make dropdown searchable and place "other" at the top * add hint about "Other" contest * avoid "flash" of winkeyer settings in qso logging, it shows up if cw is set as mode * winkeyer for the new contesting need proper testing by @AndreasK79 and @phl0 since I don't have a winkeyer myself. I tested with a python simulator... * english comments * proper singular/plural * fix dropdown * preselect active station location * allow editing time aswell * fix fontsize in radio buttons * remove redundant qso count query * cache last updated value for contest qsos to reduce db load * index on user id * cache also qso count * you can now resize windows also on the edges requested by @xyz667 in https://github.com/wavelog/wavelog/pull/3063#issuecomment-4557850867 * fix oversized clock * click to prepare logging for contesting Logic: - if qso windows is open, call is sent to qso form - if contest log is open, call is sent to contest log - if both are open, call is sent to contest log - if none is open, click opens qso form and call is sent to qso form * handle exchange s prefill with data from the last qso and make exchange always uppercase * add dropdown to qso list with option to delete qsos * language fixes * english comments * center text * fix missing dropdown on new qsos * add date presets for contest session creator/editor * basic worker implementation * support multiple workers for clustering * remove unnecessary node column * simple availability check * websocket for contesting * bonus: add vip for visibilty in debug view * use vip if available * worker disabled in debug view if disabled in config * racecondition for winkeyer breaks edit/delete dropdown * racecondition for winkeyer breaks edit/delete dropdown * qso handling for worker in contesting, no high interval heartbeat anymore * clear heartbeat intervall * fix some timing bugs * remove dedicated worker controller as it's not needed * handle whitespace * trigger sync engine after processing QSO sync response and adjust last sync time to avoid spurious resyncs * fix set and setLocal * fix change detection and sync logic * fix the lost of seconds due to edit * fix table rendering * watermark handling for true delta * remove caching as it brings more problems here as benefits * make serial as default exchangetype * Add clarification on cluster setup requirements for worker URLs * add operator to qso list if this is a clubstation * fix bug for editing qsos in clubmode * remove redundant code * integers must be null in database * fix bug in tabindex if 3 exchangefields are configured (serial, exchange, grid) * nice contesting map (draft) * map autofit * add grid overlay * calculate distanz and azimuth * include rotor control via waveloggate (ws only) * implement simple dedicated callbook lookup * simpler and faster lookup * option in contest session to disabled callbook lookup and only calculate dxcc * syntax issue * fix bug in CBR export * catch empty result on contest * remove some leftovers * removed some comments * remove unused stuff * removed old todos/comments * add 4s timeout for ajax transport * you need to be admin for this * translations * add option to delete qso's aswell when deleting a session (opt in) * add quickstart button in manager * removed old todo * fix "grows to bottom" * disable "copy exchange to" if no exchange is set * clear map on qso clear aswell * same for scp * updated documentation link * calculate distanze and azimut also on grid only * better space usage in qso input * callbook db first approach, online lookup can be disabled while being cached for all users when enabled * some input validation * use only one sot for qrgtoband * basic stats component * fix html encoding * render all aswell * ascii * store locally which time frame the user wants * sync settings and warn user if something happend on the backend * dev leftover * show errors if callbook fails or callsign is invalid * easier syntax * Revert "easier syntax" - that was a mistake This reverts commit 80c75dbf1e6c832f8d0616ea56b8b55d96c36418. * fix for callbook lookup * make pathline more visible * better visibility of qsos * add classic coordinates bar to the map * fix bug in stats * stretch max duration to 4s.. just in case * simple sanity check in js to prevent unnecessary callbook lookups * simulate tab after filling the call from scp * fix poll radios with enabled worker * reset po/mo files back to upstream/dev * split contest exports, add back reg1test edi format * keep the menu item but redirect to contesting * better syntax and bugfix * break it down further and fix typo ("constraint") * use monospace font for input fields * fix: in worker driven mode we need to do the heartbeat also on not focused windows otherwise we loose this contact * fix: error handling on invalid times * wavelog Ø * more styling * band lowercase fix * Other is not a very descriptive title for a contest logger window, so we rename it to "Contest" * custom contest name * compare the whole array instead just a few settings --------- Co-authored-by: int2001 <joerg@dj7nt.de> Co-authored-by: HadleySo <71105018+HadleySo@users.noreply.github.com> Co-authored-by: DB4SCW <dev@db4scw.de>
2026-06-14 14:23:00 +02:00
$data['contest'] = $this->contest_admin_model->getActiveContests();
2020-08-24 20:16:06 +02:00
$data['adif_propmodes'] = $this->config->item('adif_propmodes');
2026-05-07 15:39:10 +02:00
2025-02-13 14:44:53 +00:00
$this->load->view('qso/edit_ajax', $data);
}
2025-02-13 14:44:53 +00:00
function qso_save_ajax() {
$this->load->library('form_validation');
$this->load->model('logbook_model');
if(!$this->user_model->authorize(2)) {
$this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard');
}
$this->form_validation->set_rules('time_on', 'Start Date', 'required');
$this->form_validation->set_rules('time_off', 'End Date', 'required');
2025-02-14 07:52:24 +00:00
$this->form_validation->set_rules('id', 'qso ID', 'required');
if (strtoupper(trim($this->input->post('locator')) ?? '') != '') {
2026-08-12 10:53:36 +02:00
$this->form_validation->set_rules('gridsquare', 'Locator', 'callback_check_locator[grid]');
}
2026-08-12 09:41:14 +02:00
if (strtoupper(trim($this->input->post('vucc_grids')) ?? '') != '') {
2026-08-12 10:53:36 +02:00
$this->form_validation->set_rules('vucc_grids', 'VUCC Grids', 'callback_check_locator[vucc]');
2026-08-12 09:41:14 +02:00
}
$edit_result=array();
2025-02-14 07:52:24 +00:00
$edit_result['success']=false;
2025-02-13 14:44:53 +00:00
if ($this->form_validation->run()) {
2025-02-14 07:52:24 +00:00
$edit_result=$this->logbook_model->edit();
} else {
2025-02-16 11:36:57 +00:00
if (validation_errors() != '') {
$edit_result['detail']=validation_errors();
}
$edit_result['success']=false;
2025-02-13 14:44:53 +00:00
}
header('Content-Type: application/json');
2025-02-14 07:52:24 +00:00
echo json_encode($edit_result);
2025-02-13 14:44:53 +00:00
}
function qsl_rcvd($id, $method) {
2019-06-15 20:20:20 +02:00
$this->load->model('logbook_model');
2024-08-16 10:08:44 +02:00
if(!$this->user_model->authorize(2)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
2019-06-15 20:20:20 +02:00
2025-02-13 14:44:53 +00:00
// Update Logbook to Mark Paper Card Received
2025-02-13 14:44:53 +00:00
$this->logbook_model->paperqsl_update($id, $method);
2025-02-13 14:44:53 +00:00
$this->session->set_flashdata('notice', 'QSL Card: Marked as Received');
2025-02-13 14:44:53 +00:00
redirect('logbook');
2019-06-15 20:20:20 +02:00
}
2020-08-24 20:16:06 +02:00
2025-02-13 14:44:53 +00:00
function qsl_rcvd_ajax() {
$id = str_replace('"', "", $this->input->post("id", TRUE));
$method = str_replace('"', "", $this->input->post("method", TRUE));
2020-08-24 20:16:06 +02:00
2025-02-13 14:44:53 +00:00
$this->load->model('logbook_model');
2020-08-24 20:16:06 +02:00
2025-02-13 14:44:53 +00:00
header('Content-Type: application/json');
2020-08-24 20:16:06 +02:00
2025-02-13 14:44:53 +00:00
if(!$this->user_model->authorize(2)) {
echo json_encode(array('message' => 'Error'));
2020-08-24 20:16:06 +02:00
2025-02-13 14:44:53 +00:00
}
else {
// Update Logbook to Mark Paper Card Received
$this->logbook_model->paperqsl_update($id, $method);
2020-08-24 20:16:06 +02:00
2025-02-13 14:44:53 +00:00
echo json_encode(array('message' => 'OK'));
}
}
2025-02-13 14:44:53 +00:00
function qsl_sent_ajax() {
$id = str_replace('"', "", $this->input->post("id", TRUE));
$method = str_replace('"', "", $this->input->post("method", TRUE));
2025-02-13 14:44:53 +00:00
$this->load->model('logbook_model');
2025-02-13 14:44:53 +00:00
header('Content-Type: application/json');
2025-02-13 14:44:53 +00:00
if(!$this->user_model->authorize(2)) {
echo json_encode(array('message' => 'Error'));
2025-02-13 14:44:53 +00:00
}
else {
// Update Logbook to Mark Paper Card Sent
$this->logbook_model->paperqsl_update_sent($id, $method);
2025-02-13 14:44:53 +00:00
echo json_encode(array('message' => 'OK'));
}
}
2025-02-13 14:44:53 +00:00
function qsl_requested_ajax() {
$id = str_replace('"', "", $this->input->post("id", TRUE));
$method = str_replace('"', "", $this->input->post("method", TRUE));
2025-02-13 14:44:53 +00:00
$this->load->model('logbook_model');
2025-02-13 14:44:53 +00:00
header('Content-Type: application/json');
2025-02-13 14:44:53 +00:00
if(!$this->user_model->authorize(2)) {
echo json_encode(array('message' => 'Error'));
2025-02-13 14:44:53 +00:00
}
else {
// Update Logbook to Mark Paper Card Received
$this->logbook_model->paperqsl_requested($id, $method);
2025-02-13 14:44:53 +00:00
echo json_encode(array('message' => 'OK'));
}
}
function qsl_ignore_ajax() {
2025-02-13 14:44:53 +00:00
$id = str_replace('"', "", $this->input->post("id", TRUE));
$method = str_replace('"', "", $this->input->post("method", TRUE));
2025-02-13 14:44:53 +00:00
$this->load->model('logbook_model');
2025-02-13 14:44:53 +00:00
header('Content-Type: application/json');
2025-02-13 14:44:53 +00:00
if(!$this->user_model->authorize(2)) {
echo json_encode(array('message' => 'Error'));
2025-02-13 14:44:53 +00:00
}
else {
// Update Logbook to Mark Paper Card Received
$this->logbook_model->paperqsl_ignore($id, $method);
2025-02-13 14:44:53 +00:00
echo json_encode(array('message' => 'OK'));
}
}
/* Delete QSO */
function delete() {
// CSRF mitigation: reject non-POST requests
if ($this->input->method() !== 'post') {
$this->session->set_flashdata('error', __("Invalid request method"));
redirect('dashboard');
return;
}
$id = $this->input->post('id', TRUE);
if (empty($id)) {
redirect('dashboard');
return;
}
$this->load->model('logbook_model');
if ($this->logbook_model->check_qso_is_accessible($id)) {
$this->logbook_model->delete($id);
$this->session->set_flashdata('notice', 'QSO Deleted Successfully');
$data['message_title'] = "Deleted";
$data['message_contents'] = "QSO Deleted Successfully";
$this->load->view('messages/message', $data);
}
2011-04-25 16:24:01 +01:00
}
2020-08-24 20:16:06 +02:00
2025-02-13 14:44:53 +00:00
/* Delete QSO */
function delete_ajax() {
$id = str_replace('"', "", $this->input->post("id", TRUE));
$this->load->model('logbook_model');
if ($this->logbook_model->check_qso_is_accessible($id)) {
$this->logbook_model->delete($id);
header('Content-Type: application/json');
echo json_encode(array('message' => 'OK'));
} else {
header('Content-Type: application/json');
echo json_encode(array('message' => 'not allowed'));
}
return;
}
function band_to_freq($band, $mode) {
session_write_close();
2024-05-20 17:48:26 +02:00
if ($band != null and $band != 'null') {
echo $this->frequency->convert_band($band, $mode);
}
}
/*
* Function is used for autocompletion of SOTA in the QSO entry form
*/
public function get_sota() {
session_write_close();
2025-02-13 14:44:53 +00:00
$query = $this->input->get('query', TRUE) ?? FALSE;
2026-08-03 10:44:30 +02:00
$this->load->model('sota');
$json = $this->sota->search_refs($query);
header('Content-Type: application/json');
echo json_encode($json);
}
2022-08-15 16:04:33 +02:00
public function get_wwff() {
session_write_close();
2025-02-13 14:44:53 +00:00
$query = $this->input->get('query', TRUE) ?? FALSE;
$this->load->model('wwff');
$json = $this->wwff->search_refs($query);
2025-02-13 14:44:53 +00:00
header('Content-Type: application/json');
echo json_encode($json);
}
2022-10-05 17:05:53 +02:00
public function get_pota() {
session_write_close();
2025-02-13 14:44:53 +00:00
$query = $this->input->get('query', TRUE) ?? FALSE;
2026-08-03 10:39:17 +02:00
$this->load->model('pota');
$json = $this->pota->search_refs($query);
2025-02-13 14:44:53 +00:00
header('Content-Type: application/json');
echo json_encode($json);
}
/*
* Function is used for autocompletion of DOK in the QSO entry form
*/
2025-02-13 14:44:53 +00:00
public function get_dok() {
session_write_close();
2025-02-13 14:44:53 +00:00
$json = [];
$query = $this->input->get('query', TRUE) ?? FALSE;
$dok = strtoupper($query);
$file = 'updates/dok.txt';
if (is_readable($file)) {
$lines = file($file, FILE_IGNORE_NEW_LINES);
$input = preg_quote($dok, '~');
$reg = '~^'. $input .'(.*)$~';
$result = preg_grep($reg, $lines);
$json = [];
$i = 0;
foreach ($result as &$value) {
// Limit to 100 as to not slowdown browser too much
if (count($json) <= 100) {
$json[] = ["name"=>$value];
}
}
} else {
$src = 'assets/resources/dok.txt';
if (copy($src, $file)) {
$this->get_dok();
} else {
log_message('error', 'Failed to copy source file ('.$src.') to new location. Check if this path has the right permission: '.$file);
}
}
header('Content-Type: application/json');
echo json_encode($json);
}
public function get_sota_info() {
session_write_close();
2025-02-13 14:44:53 +00:00
$this->load->library('sota');
$sota = $this->input->post('sota', TRUE);
header('Content-Type: application/json');
echo $this->sota->info($sota);
}
public function get_wwff_info() {
session_write_close();
2025-02-13 14:44:53 +00:00
$this->load->library('wwff');
$wwff = $this->input->post('wwff', TRUE);
header('Content-Type: application/json');
echo $this->wwff->info($wwff);
}
public function get_pota_info() {
session_write_close();
2025-02-13 14:44:53 +00:00
$this->load->library('pota');
$pota = $this->input->post('pota', TRUE);
header('Content-Type: application/json');
echo $this->pota->info($pota);
}
public function get_station_power() {
session_write_close();
2025-02-13 14:44:53 +00:00
$this->load->model('stations');
$this->load->library('qra');
2025-02-13 14:44:53 +00:00
$stationProfile = $this->input->post('stationProfile', TRUE);
2025-10-24 16:15:44 +02:00
$result = $this->stations->get_station_power($stationProfile);
$data['station_power'] = $result['station_power'];
$data['station_callsign'] = $result['station_callsign'];
[$data['lat'], $data['lng']] = $this->qra->qra2latlong($this->stations->gridsquare_from_station($stationProfile));
2025-02-13 14:44:53 +00:00
header('Content-Type: application/json');
echo json_encode($data);
}
// Return Previous QSOs Made in the active logbook
public function component_past_contacts() {
$this->load->library('Qra');
if(!$this->user_model->authorize(2)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
$this->load->model('logbook_model');
session_write_close();
$data['query'] = $this->logbook_model->last_custom($this->session->userdata('qso_page_last_qso_count'));
// Load view
$this->load->view('qso/components/previous_contacts', $data);
}
public function get_eqsl_default_qslmsg() { // Get ONLY Default eQSL-Message with this function. This is ONLY for QSO relevant!
$return_json = array();
$option_key = $this->input->post('option_key', TRUE);
if ($option_key > 0) {
$options_object = $this->user_options_model->get_options('eqsl_default_qslmsg', array('option_name' => 'key_station_id', 'option_key' => $option_key))->result();
$return_json['eqsl_default_qslmsg'] = (isset($options_object[0]->option_value)) ? $options_object[0]->option_value : '';
}
header('Content-Type: application/json');
echo json_encode($return_json);
}
Station setup (#175) * The start of station setup * Added modals new logbook and new location * Added 1st JSON-Create Logbook (PHP) * Added 1st JSON-Create Logbook (JS) * Changed to small buttons * A bit more errorhandling (JS) * Moved collecting of params to controller and added errorhandling * Added Delete-Function (with confirmation) * Moved view to new folder and added delete Logbook * Added AJAX for setActive Book * Added AJAX for setActiveBook (JS) * Partially working reload logbook table * Dynamic loading of logbooks (PHP) * Dynamic loading of logbooks (JS) * Reload location table * Removed duplicate return statement * Fixed Zoo of params to JSON-Out * Fixed RenderBug (not yet finished) at JS * Fixed DT error * Fixed CSS * Changed setActiveStation from link to Ajax (PHP) * Changed setActiveStation from link to Ajax (JS) * Added confirmation to changeActiveStation * Changed EmptyStation to AJAX (PHP) * Changed EmptyStation to AJAX (JS) * Changed DeleteStation to AJAX (JS) * Changed DeleteStation to AJAX (PHP) * Tidy up code a little * Re-added favorites * Ajaxyfing favorite location * Fixed clicking on favorite * Fixed favorite star * Tweaked new logbook dialog * Fixed public search badge * Fix badges on first load * Reloads stations on load * Redirect to station setup * Re-added translation * Fixed more badges * Added menu item translation * Header with translated menu * Updated warning links on dashboard to go to station setup * Added missing lang lines for Polish and Czech * Changed links in Quickswitch to station setup * station setup in quickswitcher * Make station location ID a separate (and sortable) column * Added missing ID * Relocated eQSL-Thing to station_model to reduce redundancies * Removed Debug-Stuff * Moved get_options to get_default_eqsl_message within QSO-Controller * Moved generic get_options to more specific get_eqsl_default_message * Removed loading of options_model, since it is already loaded via "autload"... * dynamic quickswitcher * disabled button for active location * typo * removed empty unused function * comment * reload stationsetup list if we are on this page * don't grey out the locations * dynamic loading in both directions * rename function to make it more clear * readability --------- Co-authored-by: int2001 <joerg@dj7nt.de> Co-authored-by: Joerg (DJ7NT) <int2001@users.noreply.github.com> Co-authored-by: HB9HIL <fabian.berg@hb9hil.org> Co-authored-by: phl0 <github@florian-wolters.de>
2024-03-03 21:52:51 +01:00
2024-07-12 14:17:28 +02:00
public function unsupported_lotw_prop_modes() {
echo json_encode($this->config->item('lotw_unsupported_prop_modes'));
}
2026-08-12 10:53:36 +02:00
function check_locator($grid, $type) {
switch ($type) {
case 'grid':
$grid = $this->input->post('locator', TRUE);
if (!$this->load->is_loaded('Qra')) {
$this->load->library('Qra');
}
2026-08-12 10:53:36 +02:00
if ($this->qra->validate_grid($grid, 'grid')) {
return true;
} else {
$this->form_validation->set_message('check_locator', sprintf(__("Please check value for gridsquare (%s)"), strtoupper($grid)));
return false;
}
break;
case 'vucc':
$grid = $this->input->post('vucc_grids', TRUE);
if (!$this->load->is_loaded('Qra')) {
$this->load->library('Qra');
}
2026-08-12 10:53:36 +02:00
if ($this->qra->validate_grid($grid, 'vucc')) {
return true;
} else {
$this->form_validation->set_message('check_locator', sprintf(__("Please check value for VUCC gridsquare (%s)"), strtoupper($grid)));
return false;
}
break;
default:
if (!$this->load->is_loaded('Qra')) {
$this->load->library('Qra');
}
2026-08-12 10:53:36 +02:00
if ($this->qra->validate_grid($grid, 'any')) {
return true;
} else {
$this->form_validation->set_message('check_locator', sprintf(__("Please check value for gridsquare (%s)"), strtoupper($grid)));
return false;
}
break;
2025-02-13 14:44:53 +00:00
}
}
/**
2024-09-19 18:13:56 +02:00
* Open the API url which causes the browser to open the QSO live logging and populate the callsign with the data from the API
*
2024-09-19 18:13:56 +02:00
* Usage example:
* https://<URL to Wavelog>/index.php/qso/log_qso?callsign=4W7EST
*/
function log_qso() {
// Check if users logged in
if ($this->user_model->validate_session() == 0) {
// user is not logged in
$this->session->set_flashdata('warning', __("You have to be logged in to access this URL."));
redirect('user/login');
}
// get the data from the API
$data['callsign'] = $this->input->get('callsign', TRUE);
2025-02-13 14:44:53 +00:00
$data['page_title'] = __("Call Transfer");
2024-09-19 18:13:56 +02:00
// load the QSO redirect page
if ($data['callsign'] != "") {
$this->load->view('interface_assets/header', $data);
$this->load->view('qso/log_qso');
} else {
$this->session->set_flashdata('warning', __("No callsign provided."));
redirect('dashboard');
}
}
2024-12-26 14:01:00 +01:00
2025-02-13 14:44:53 +00:00
/**
* Easy modal Loader
2025-02-13 14:44:53 +00:00
* Used for Share Modal in QSO Details view
*/
function getShareModal() {
2024-12-26 14:01:00 +01:00
2025-02-13 14:44:53 +00:00
$data['qso'] = $this->input->post('qso_data', TRUE);
2024-12-26 14:01:00 +01:00
2025-02-13 14:44:53 +00:00
if (empty($data['qso'])) {
echo "No QSO data provided.";
return;
}
2024-12-26 14:01:00 +01:00
2025-02-13 14:44:53 +00:00
$this->load->view('qso/components/share_modal', $data, false);
}
function getAwardTabs() {
$this->load->view('qso/award_tabs');
}
2011-08-19 17:12:13 +01:00
}