diff --git a/application/config/config.sample.php b/application/config/config.sample.php index e696c53ee..eecdafb18 100644 --- a/application/config/config.sample.php +++ b/application/config/config.sample.php @@ -787,4 +787,13 @@ $config['max_login_attempts'] = 3; |-------------------------------------------------------------------------- */ - $config['disable_user_stats'] = false; \ No newline at end of file + $config['disable_user_stats'] = false; + +/* +|-------------------------------------------------------------------------- +| enable DCL Interface +| Set this to true if your Users and you want to connect your instance to the German DCL +|-------------------------------------------------------------------------- + */ + + $config['enable_dcl_interface'] = true; diff --git a/application/config/migration.php b/application/config/migration.php index 3cf48e933..6fd06b52e 100644 --- a/application/config/migration.php +++ b/application/config/migration.php @@ -22,7 +22,7 @@ $config['migration_enabled'] = TRUE; | */ -$config['migration_version'] = 253; +$config['migration_version'] = 254; /* |-------------------------------------------------------------------------- diff --git a/application/controllers/Dcl.php b/application/controllers/Dcl.php new file mode 100644 index 000000000..8d21502bd --- /dev/null +++ b/application/controllers/Dcl.php @@ -0,0 +1,269 @@ +load->helper(array('form', 'url')); + + if (!($this->config->item('enable_dcl_interface') ?? false)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); exit; } + $this->load->model('user_model'); + if (ENVIRONMENT == 'maintenance' && $this->session->userdata('user_id') == '') { + echo __("Maintenance Mode is active. Try again later.")."\n"; + redirect('user/login'); + } + } + + public function save_key() { + if (!$this->user_model->authorize(2) || !clubaccess_check(9)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); } + $this->load->model('Dcl_model'); + $this->Dcl_model->store_key($call); + } + public function key_import() { + if (!$this->user_model->authorize(2) || !clubaccess_check(9)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); } + $this->load->library('Permissions'); + $this->load->model('dcl_model'); + $data['date_format']=$this->session->userdata('user_date_format') ?? $this->config->item('qso_date_format'); + + $sig=($this->input->get('sig',true) ?? ''); + $token=($this->input->get('token',true) ?? ''); + if ( ($sig != '') && ($token != '')) { + $data['is_valid']=$this->dcl_model->check_dcl_sig($token,$sig); + $data['page_title'] = __("DCL Key Import"); + $data['token'] = $token; + if ($data['is_valid']) { + $data['dcl_info']=$this->dcl_model->get_dcl_info($token); + $this->dcl_model->store_key(json_encode($data['dcl_info'] ?? '')); + } else { + $data['dcl_info']=''; + } + $this->load->view('interface_assets/header', $data); + $this->load->view('dcl_views/key_import',$data); + $this->load->view('interface_assets/footer'); + } else { + redirect('https://api.dcl.darc.de/api/v1/get-token?wohin='.site_url().'/dcl/key_import'); + } + } + + public function index() { + if (!$this->user_model->authorize(2) || !clubaccess_check(9)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); } + $this->load->library('Permissions'); + $this->load->model('user_model'); + if(!$this->user_model->authorize(2) || !clubaccess_check(9)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); } + + // Load required models for page generation + $this->load->model('Dcl_model'); + $data['date_format']=$this->session->userdata('user_date_format') ?? $this->config->item('qso_date_format'); + + // Get Array of the logged in users LoTW certs. + $dclkeys=($this->Dcl_model->dcl_keys($this->session->userdata('user_id')) ?? ''); + $i=0; + foreach ($dclkeys as $dclkey) { + $data['dcl_keys'][$i] = json_decode($dclkey->option_value ?? ''); + $i++; + } + + // Set Page Title + $data['page_title'] = __("DCL"); + + $this->load->model('cron_model'); + $data['next_run'] = $this->cron_model->get_next_run("dcl_dcl_upload"); + + // Load Views + $this->load->view('interface_assets/header', $data); + $this->load->view('dcl_views/index'); + $this->load->view('interface_assets/footer'); + } + + public function dcl_sync() { + $this->dcl_upload(); + } + + public function dcl_upload() { + // Called as User: Upload for User (if manual sync isn't disabled + // Called from cron / without Session: iterate through stations, check for DCL-Key and upload + ini_set('memory_limit', '-1'); + + $this->load->model('user_model'); + $this->load->model('Dcl_model'); + + // set the last run in cron table for the correct cron id + $this->load->model('cron_model'); + $this->cron_model->set_last_run($this->router->class.'_'.$this->router->method); + + // Get Station Profile Data + $this->load->model('Stations'); + + if (!$this->load->is_loaded('AdifHelper')) { + $this->load->library('AdifHelper'); + } + + if ($this->user_model->authorize(2)) { + if (!($this->config->item('disable_manual_dcl'))) { + $station_profiles = $this->Stations->all_of_user($this->session->userdata('user_id')); + $sync_user_id=$this->session->userdata('user_id'); + } else { + echo "Manual syncing is disabled by configuration"; + redirect('dashboard'); + exit(); + } + } else { + $station_profiles = $this->Stations->all(); + $sync_user_id=null; + } + + // Array of QSO IDs being Uploaded + + $qso_id_array = array(); + + if ($station_profiles->num_rows() >= 1) { + + foreach ($station_profiles->result() as $station_profile) { + + // Get Certificate Data + $data['station_profile'] = $station_profile; + $key_info = $this->Dcl_model->find_key($station_profile->station_callsign, $station_profile->user_id); + // If Station Profile has no DCL Key continue on. + if (($key_info ?? '') == '') { + continue; + } + + $this->load->model('Logbook_model'); + + $data['qsos'] = $this->Logbook_model->get_dcl_qsos_to_upload($data['station_profile']->station_id,$key_info['vf'],$key_info['vt']); + + // Nothing to upload + if(empty($data['qsos']->result())){ + if ($this->user_model->authorize(2)) { // Only be verbose if we have a session + echo $station_profile->station_callsign." (".$station_profile->station_profile_name."): ".__("No QSOs to upload.")."
"; + } + continue; + } + + foreach ($data['qsos']->result() as $temp_qso) { + array_push($qso_id_array, $temp_qso->COL_PRIMARY_KEY); + } + + // Build File to save + $adif_to_post = $this->load->view('adif/data/dcl.php', $data, TRUE); + $data['qsos']=''; + + //The URL that accepts the file upload. + $url = 'https://api.dcl.darc.de/api/v1/adif-import'; // todo: change to final URL b4 release + + //Initiate cURL + $ch = curl_init(); + + //Set the URL + curl_setopt($ch, CURLOPT_URL, $url); + + //Set the HTTP request to POST + curl_setopt($ch, CURLOPT_POST, true); + + //Tell cURL to return the output as a string. + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); + $headers = [ + 'Content-Type: application/json', + 'Accept: application/json' + ]; + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + + $payload=[]; + $payload['key']=$key_info['token']; + $payload['adif']=$adif_to_post; + + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload, true)); + + $result = curl_exec($ch); + $adif_to_post=''; // Clean Mem + // todo: parse output from DCL (contains a lot of information within $result) + if(curl_errno($ch)){ + echo $station_profile->station_callsign." (".$station_profile->station_profile_name."): ".__("Upload Failed")." - ".curl_strerror(curl_errno($ch))." (".curl_errno($ch).")
"; + if (curl_errno($ch) == 28) { // break on timeout + echo __("Timeout reached. Stopping subsequent uploads.")."
"; + break; + } else { + continue; + } + } + + $pos = true; + + if ($pos === false) { + echo $station_profile->station_callsign." (".$station_profile->station_profile_name."): ".__("Upload Failed")." - ".curl_strerror(curl_errno($ch))." (".curl_errno($ch).")
"; + if (curl_errno($ch) == 28) { // break on timeout + echo __("Timeout reached. Stopping subsequent uploads.")."
"; + break; + } else { + continue; + } + } else { + echo $station_profile->station_callsign." (".$station_profile->station_profile_name."): ".__("Upload Successful")." ".count($qso_id_array)." QSOs
"; + // Mark QSOs as Sent + foreach ($qso_id_array as $qso_number) { + // todo: uncomment when ready + $this->Logbook_model->mark_dcl_sent($qso_number); + } + } + $qso_id_array=[]; + } + } else { + echo __("No Station Profiles found to upload to DCL"); + } + + if ($this->user_model->authorize(2)) { + echo "

"; + $sync_user_id=$this->session->userdata('user_id'); + } else { + $sync_user_id=null; + } + // echo $this->dcl_download($sync_user_id); + } + + public function delete_key() { + if (!$this->user_model->authorize(2) || !clubaccess_check(9)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); } + $this->load->model('user_model'); + if(!$this->user_model->authorize(2)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); } + $this->load->model('Dcl_model'); + $this->Dcl_model->delete_key(); + $this->session->set_flashdata('success', __("Key(s) Deleted.")); + redirect('dcl'); + } + + + /* + |-------------------------------------------------------------------------- + | Function: dcl_download + |-------------------------------------------------------------------------- + | + | Collects users with DCL tokens and runs through them + | downloading matching QSOs. + | + */ + function dcl_download($sync_user_id = null) { + $this->load->model('user_model'); + $this->load->model('logbook_model'); + $this->load->model('Stations'); + + // Needs complete refactoring. Pseudocode: + // Loop through all users with token present (find_key at Dcl_model) or only single_users if sync_user_is has been provided + // Fetch data for call from DCL (todo: which URL? Where?) + // Mark as received + // all on the fly (no tempfiles) + } + + public function import() { // Is only called via frontend. Cron uses "upload". within download the download is called + $this->load->model('user_model'); + $this->load->model('Stations'); + if(!$this->user_model->authorize(2)) { + $this->session->set_flashdata('error', __("You're not allowed to do that!")); + redirect('dashboard'); + exit(); + } + // Refactoring needed. This function provides manual download (via uploaded file into Wavelog) of DCL-Confirmations as well as triggering dcl_download + } + +} // end class diff --git a/application/controllers/Logbook.php b/application/controllers/Logbook.php index 51d10943b..137cb9cae 100644 --- a/application/controllers/Logbook.php +++ b/application/controllers/Logbook.php @@ -943,7 +943,7 @@ class Logbook extends CI_Controller { } function querydb($id) { - $this->db->select('dxcc_entities.adif, lotw_users.callsign, COL_BAND, COL_CALL, COL_CLUBLOG_QSO_DOWNLOAD_DATE, + $this->db->select('dxcc_entities.adif, lotw_users.callsign, COL_BAND, COL_CALL, COL_CLUBLOG_QSO_DOWNLOAD_DATE, COL_DCL_QSLRDATE, COL_DCL_QSLSDATE, COL_DCL_QSL_SENT, COL_DCL_QSL_RCVD, COL_CLUBLOG_QSO_DOWNLOAD_STATUS, COL_CLUBLOG_QSO_UPLOAD_DATE, COL_CLUBLOG_QSO_UPLOAD_STATUS, COL_CONTEST_ID, COL_DISTANCE, COL_EQSL_QSL_RCVD, COL_EQSL_QSLRDATE, COL_EQSL_QSLSDATE, COL_EQSL_QSL_SENT, COL_FREQ, COL_GRIDSQUARE, COL_IOTA, COL_LOTW_QSL_RCVD, COL_LOTW_QSLRDATE, COL_LOTW_QSLSDATE, diff --git a/application/controllers/Logbookadvanced.php b/application/controllers/Logbookadvanced.php index fa9950173..68dbd0e33 100644 --- a/application/controllers/Logbookadvanced.php +++ b/application/controllers/Logbookadvanced.php @@ -597,6 +597,7 @@ class Logbookadvanced extends CI_Controller { $json_string['region']['show'] = $this->def_boolean($this->input->post('region')); $json_string['qth']['show'] = $this->def_boolean($this->input->post('qth')); $json_string['frequency']['show'] = $this->def_boolean($this->input->post('frequency')); + $json_string['dcl']['show'] = $this->def_boolean($this->input->post('dcl')); $obj['column_settings']= json_encode($json_string); diff --git a/application/controllers/User.php b/application/controllers/User.php index 0a14b312b..736b149b9 100644 --- a/application/controllers/User.php +++ b/application/controllers/User.php @@ -222,7 +222,7 @@ class User extends CI_Controller { $data['user_amsat_status_upload'] = $this->input->post('user_amsat_status_upload'); $data['user_mastodon_url'] = $this->input->post('user_mastodon_url'); $data['user_default_band'] = $this->input->post('user_default_band'); - $data['user_default_confirmation'] = ($this->input->post('user_default_confirmation_qsl') !== null ? 'Q' : '').($this->input->post('user_default_confirmation_lotw') !== null ? 'L' : '').($this->input->post('user_default_confirmation_eqsl') !== null ? 'E' : '').($this->input->post('user_default_confirmation_qrz') !== null ? 'Z' : '').($this->input->post('user_default_confirmation_clublog') !== null ? 'C' : ''); + $data['user_default_confirmation'] = ($this->input->post('user_default_confirmation_qsl') !== null ? 'Q' : '').($this->input->post('user_default_confirmation_lotw') !== null ? 'L' : '').($this->input->post('user_default_confirmation_eqsl') !== null ? 'E' : '').($this->input->post('user_default_confirmation_qrz') !== null ? 'Z' : '').($this->input->post('user_default_confirmation_clublog') !== null ? 'C' : '').($this->input->post('user_default_confirmation_dcl') !== null ? 'D' : ''); $data['user_qso_end_times'] = $this->input->post('user_qso_end_times'); $data['user_quicklog'] = $this->input->post('user_quicklog'); $data['user_quicklog_enter'] = $this->input->post('user_quicklog_enter'); @@ -274,7 +274,7 @@ class User extends CI_Controller { $this->input->post('user_amsat_status_upload'), $this->input->post('user_mastodon_url'), $this->input->post('user_default_band'), - ($this->input->post('user_default_confirmation_qsl') !== null ? 'Q' : '').($this->input->post('user_default_confirmation_lotw') !== null ? 'L' : '').($this->input->post('user_default_confirmation_eqsl') !== null ? 'E' : '').($this->input->post('user_default_confirmation_qrz') !== null ? 'Z' : '').($this->input->post('user_default_confirmation_clublog') !== null ? 'C' : ''), + ($this->input->post('user_default_confirmation_qsl') !== null ? 'Q' : '').($this->input->post('user_default_confirmation_lotw') !== null ? 'L' : '').($this->input->post('user_default_confirmation_eqsl') !== null ? 'E' : '').($this->input->post('user_default_confirmation_qrz') !== null ? 'Z' : '').($this->input->post('user_default_confirmation_clublog') !== null ? 'C' : '').($this->input->post('user_default_confirmation_dcl') !== null ? 'D' : ''), $this->input->post('user_qso_end_times'), $this->input->post('user_quicklog'), $this->input->post('user_quicklog_enter'), @@ -352,7 +352,7 @@ class User extends CI_Controller { $data['user_amsat_status_upload'] = $this->input->post('user_amsat_status_upload'); $data['user_mastodon_url'] = $this->input->post('user_mastodon_url'); $data['user_default_band'] = $this->input->post('user_default_band'); - $data['user_default_confirmation'] = ($this->input->post('user_default_confirmation_qsl') !== null ? 'Q' : '').($this->input->post('user_default_confirmation_lotw') !== null ? 'L' : '').($this->input->post('user_default_confirmation_eqsl') !== null ? 'E' : '').($this->input->post('user_default_confirmation_qrz') !== null ? 'Z' : '').($this->input->post('user_default_confirmation_clublog') !== null ? 'C' : ''); + $data['user_default_confirmation'] = ($this->input->post('user_default_confirmation_qsl') !== null ? 'Q' : '').($this->input->post('user_default_confirmation_lotw') !== null ? 'L' : '').($this->input->post('user_default_confirmation_eqsl') !== null ? 'E' : '').($this->input->post('user_default_confirmation_qrz') !== null ? 'Z' : '').($this->input->post('user_default_confirmation_clublog') !== null ? 'C' : '').($this->input->post('user_default_confirmation_dcl') !== null ? 'D' : ''); $data['user_qso_end_times'] = $this->input->post('user_qso_end_times'); $data['user_quicklog'] = $this->input->post('user_quicklog'); $data['user_quicklog_enter'] = $this->input->post('user_quicklog_enter'); @@ -647,7 +647,7 @@ class User extends CI_Controller { } if($this->input->post('user_default_confirmation')) { - $data['user_default_confirmation'] = ($this->input->post('user_default_confirmation_qsl') !== null ? 'Q' : '').($this->input->post('user_default_confirmation_lotw') !== null ? 'L' : '').($this->input->post('user_default_confirmation_eqsl') !== null ? 'E' : '').($this->input->post('user_default_confirmation_qrz') !== null ? 'Z' : '').($this->input->post('user_default_confirmation_clublog') !== null ? 'C' : ''); + $data['user_default_confirmation'] = ($this->input->post('user_default_confirmation_qsl') !== null ? 'Q' : '').($this->input->post('user_default_confirmation_lotw') !== null ? 'L' : '').($this->input->post('user_default_confirmation_eqsl') !== null ? 'E' : '').($this->input->post('user_default_confirmation_qrz') !== null ? 'Z' : '').($this->input->post('user_default_confirmation_clublog') !== null ? 'C' : '').($this->input->post('user_default_confirmation_dcl') !== null ? 'D' : ''); } else { $data['user_default_confirmation'] = $q->user_default_confirmation; } @@ -959,7 +959,7 @@ class User extends CI_Controller { $data['user_amsat_status_upload'] = $this->input->post('user_amsat_status_upload'); $data['user_mastodon_url'] = $this->input->post('user_mastodon_url'); $data['user_default_band'] = $this->input->post('user_default_band'); - $data['user_default_confirmation'] = ($this->input->post('user_default_confirmation_qsl') !== null ? 'Q' : '').($this->input->post('user_default_confirmation_lotw') !== null ? 'L' : '').($this->input->post('user_default_confirmation_eqsl') !== null ? 'E' : '').($this->input->post('user_default_confirmation_qrz') !== null ? 'Z' : '').($this->input->post('user_default_confirmation_clublog') !== null ? 'C' : ''); + $data['user_default_confirmation'] = ($this->input->post('user_default_confirmation_qsl') !== null ? 'Q' : '').($this->input->post('user_default_confirmation_lotw') !== null ? 'L' : '').($this->input->post('user_default_confirmation_eqsl') !== null ? 'E' : '').($this->input->post('user_default_confirmation_qrz') !== null ? 'Z' : '').($this->input->post('user_default_confirmation_clublog') !== null ? 'C' : '').($this->input->post('user_default_confirmation_dcl') !== null ? 'D' : ''); $data['user_qso_end_times'] = $this->input->post('user_qso_end_times'); $data['user_quicklog'] = $this->input->post('user_quicklog'); $data['user_quicklog_enter'] = $this->input->post('user_quicklog_enter'); diff --git a/application/libraries/Genfunctions.php b/application/libraries/Genfunctions.php index e6ace37fa..7dbaedb45 100644 --- a/application/libraries/Genfunctions.php +++ b/application/libraries/Genfunctions.php @@ -12,6 +12,7 @@ class Genfunctions (($postdata['qrz'] ?? '') != '') || (($postdata['lotw'] ?? '') != '') || (($postdata['qsl'] ?? '') != '') || + (($postdata['dcl'] ?? '') != '') || (($postdata['eqsl'] ?? '') != '') ) { $sql .= ' and ('; if (($postdata['qsl'] ?? '') != '') { @@ -29,6 +30,9 @@ class Genfunctions if (($postdata['clublog'] ?? '') != '') { array_push($qsl, "COL_CLUBLOG_QSO_DOWNLOAD_STATUS = 'Y'"); } + if (($postdata['dcl'] ?? '') != '') { + array_push($qsl, "COL_DCL_QSL_RCVD = 'Y'"); + } if (count($qsl) > 0) { $sql .= implode(' or ', $qsl); } else { @@ -68,6 +72,9 @@ class Genfunctions if (($postdata['eqsl'] ?? '')!= '' ) { $qsl .= "E"; } + if (($postdata['dcl'] ?? '')!= '' ) { + $qsl .= "D"; + } if (($postdata['clublog'] ?? '')!= '' ) { $qsl .= "C"; } diff --git a/application/migrations/254_dcl_cron.php b/application/migrations/254_dcl_cron.php new file mode 100644 index 000000000..8fc0f053a --- /dev/null +++ b/application/migrations/254_dcl_cron.php @@ -0,0 +1,43 @@ +chk4cron('sync_dcl') == 0) { + $data = array( + array( + 'id' => 'sync_dcl', + 'enabled' => '0', + 'status' => 'disabled', + 'description' => 'Sync with DARC-DCL', + 'function' => 'index.php/dcl/dcl_sync', + 'expression' => '45 4 * * *', + 'last_run' => null, + 'next_run' => null + )); + $this->db->insert_batch('cron', $data); + } + + } + + public function down() { + if ($this->chk4cron('sync_dcl') > 0) { + $this->db->query("delete from cron where id='sync_dcl'"); + } + // No way back to tle-table + } + + function chk4cron($cronkey) { + $query = $this->db->query("select count(id) as cid from cron where id=?",$cronkey); + $row = $query->row(); + return $row->cid ?? 0; + } + + function dbtry($what) { + try { + $this->db->query($what); + } catch (Exception $e) { + log_message("error", "Something gone wrong while altering FKs: ".$e." // Executing: ".$this->db->last_query()); + } + } +} diff --git a/application/models/Dcl_model.php b/application/models/Dcl_model.php new file mode 100644 index 000000000..84d667a29 --- /dev/null +++ b/application/models/Dcl_model.php @@ -0,0 +1,92 @@ +load->model('user_options_model'); + return $this->user_options_model->get_options('dcl', array('option_name'=>'dcl_key'), $user_id)->result(); + } + + /* + |-------------------------------------------------------------------------- + | Function: find_key + |-------------------------------------------------------------------------- + | + | Returns all dcl_keys for a selected user via the $user_id parameter which match also to the $callsign + | + */ + + function find_key($callsign, $user_id) { + $this->load->model('user_options_model'); + $userkeys=$this->user_options_model->get_options('dcl', array('option_name'=>'dcl_key','option_key'=>'key'), $user_id)->result(); + foreach ($userkeys as $raw_key) { + $skey=json_decode($raw_key->option_value, true); + if (isset($skey['Callsigns']) && is_array($skey['Callsigns'])) { + foreach ($skey['Callsigns'] as $item) { + if (isset($item['callsign']) && strtoupper($item['callsign']) === strtoupper($callsign)) { + $key['token']=$skey['UserKeys']['token']; + $key['vf']=strtotime($item['startDate']); + $key['vt']=strtotime($item['endDate'] ?? '2099-12-31'); + return $key; + } + } + } + } + return ''; + } + + function store_key($key) { + $this->load->model('user_options_model'); + $this->user_options_model->set_option('dcl', 'dcl_key', array('key'=>$key)); + } + + function delete_key() { + $this->user_options_model->del_option('dcl', 'dcl_key',array('option_key' => 'key')); + } + + function get_dcl_info($token) { + if (($token ?? '') != '') { + try { + $dclUrl = 'https://api.dcl.darc.de/api/v1/get-userinfo/'.$token; + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $dclUrl); + curl_setopt($ch, CURLOPT_HEADER, array('Content-Type: application/json' , "Authorization: Bearer ".$token)); + curl_setopt($ch, CURLOPT_USERAGENT, 'Wavelog DCL Connector'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); + $rawdcldata = curl_exec($ch); + curl_close($ch); + if (strlen($rawdcldata)>100) { + $dcldata=json_decode($rawdcldata); + return $dcldata; + } + } catch (Exception $e) { + return false; + } + } else { + return false; + } + } + + function check_dcl_sig($string,$sig) { + try { + $sig = sodium_base642bin($sig, SODIUM_BASE64_VARIANT_URLSAFE); + $public_key=sodium_base642bin('nV46R47wlLDOJAkSs5RT00wzgz3z98uZFxjo3FSkxeg=',SODIUM_BASE64_VARIANT_URLSAFE); + return sodium_crypto_sign_verify_detached($sig, $string, $public_key); + } catch (Exception $e) { + log_message("Error","DCL: Error while checking signature: ".$e); + return false; + } + } +} +?> diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index 807b69894..f3f1fc19e 100644 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -1369,53 +1369,65 @@ class Logbook_model extends CI_Model { } if ($this->input->post('qsl_sent')) { - $qsl_sent = $this->input->post('qsl_sent'); + $qsl_sent = $this->input->post('qsl_sent',true); } else { $qsl_sent = 'N'; } if ($this->input->post('qsl_rcvd')) { - $qsl_rcvd = $this->input->post('qsl_rcvd'); + $qsl_rcvd = $this->input->post('qsl_rcvd',true); } else { $qsl_rcvd = 'N'; } if ($this->input->post('eqsl_sent')) { - $eqsl_sent = $this->input->post('eqsl_sent'); + $eqsl_sent = $this->input->post('eqsl_sent',true); } else { $eqsl_sent = 'N'; } if ($this->input->post('eqsl_rcvd')) { - $eqsl_rcvd = $this->input->post('eqsl_rcvd'); + $eqsl_rcvd = $this->input->post('eqsl_rcvd',true); } else { $eqsl_rcvd = 'N'; } if ($this->input->post('qrz_sent')) { - $qrz_sent = $this->input->post('qrz_sent'); + $qrz_sent = $this->input->post('qrz_sent',true); } else { $qrz_sent = 'N'; } if ($this->input->post('qrz_rcvd')) { - $qrz_rcvd = $this->input->post('qrz_rcvd'); + $qrz_rcvd = $this->input->post('qrz_rcvd',true); } else { $qrz_rcvd = 'N'; } if ($this->input->post('clublog_sent')) { - $clublog_sent = $this->input->post('clublog_sent'); + $clublog_sent = $this->input->post('clublog_sent',true); } else { $clublog_sent = 'N'; } if ($this->input->post('clublog_rcvd')) { - $clublog_rcvd = $this->input->post('clublog_rcvd'); + $clublog_rcvd = $this->input->post('clublog_rcvd',true); } else { $clublog_rcvd = 'N'; } + if ($this->input->post('dcl_sent')) { + $dcl_sent = $this->input->post('dcl_sent',true); + } else { + $dcl_sent = 'N'; + } + + if ($this->input->post('dcl_rcvd')) { + $dcl_rcvd = $this->input->post('dcl_rcvd',true); + } else { + $dcl_rcvd = 'N'; + } + if (in_array($this->input->post('prop_mode'), $this->config->item('lotw_unsupported_prop_modes'))) { $lotw_sent = 'I'; } elseif ($this->input->post('lotw_sent')) { @@ -1517,6 +1529,22 @@ class Logbook_model extends CI_Model { $clublogrdate = $qso->COL_CLUBLOG_QSO_DOWNLOAD_DATE; } + if ($dcl_sent == 'N' && $qso->COL_CLUBLOG_QSO_UPLOAD_STATUS != $dcl_sent) { + $dclsdate = null; + } elseif (!$qso->COL_DCL_QSLSDATE || $qso->COL_DCL_QSLSDATE != $dcl_sent) { + $dclsdate = date('Y-m-d H:i:s'); + } else { + $dclsdate = $qso->COL_DCL_QSLSDATE; + } + + if ($dcl_rcvd == 'N' && $qso->COL_DCL_QSLRDATE != $dcl_rcvd) { + $dclrdate = null; + } elseif (!$qso->COL_DCL_QSLRDATE || $qso->COL_DCL_QSLRDATE != $dcl_rcvd) { + $dclrdate = date('Y-m-d H:i:s'); + } else { + $dclrdate = $qso->COL_DCL_QSLRDATE; + } + if (($this->input->post('distance')) && (is_numeric($this->input->post('distance')))) { $distance = $this->input->post('distance'); } else { @@ -1589,6 +1617,10 @@ class Logbook_model extends CI_Model { 'COL_CLUBLOG_QSO_DOWNLOAD_DATE' => $clublogrdate, 'COL_CLUBLOG_QSO_DOWNLOAD_STATUS' => $clublog_rcvd, 'COL_CLUBLOG_QSO_UPLOAD_STATUS' => $clublog_sent, + 'COL_DCL_QSLSDATE' => $dclsdate, + 'COL_DCL_QSLRDATE' => $dclrdate, + 'COL_DCL_QSL_RCVD' => $dcl_rcvd, + 'COL_DCL_QSL_SENT' => $dcl_sent, 'COL_IOTA' => $this->input->post('iota_ref'), 'COL_SOTA_REF' => strtoupper(trim($this->input->post('sota_ref'))), 'COL_WWFF_REF' => strtoupper(trim($this->input->post('wwff_ref'))), @@ -5507,6 +5539,22 @@ class Logbook_model extends CI_Model { return $query->result(); } + function get_dcl_qsos_to_upload($station_id, $from, $till) { + + $sql = 'select *, dxcc_entities.name as station_country from ' . $this->config->item('table_name') . ' thcv ' . + ' left join station_profile on thcv.station_id = station_profile.station_id' . + ' left outer join dxcc_entities on thcv.col_my_dxcc = dxcc_entities.adif' . + ' where thcv.station_id = ?' . + ' and (COL_DCL_QSL_SENT not in ("Y","I") OR COL_DCL_QSL_SENT is null)'. + ' and COL_TIME_ON>? and COL_TIME_ONdb->query($sql, $binding); + return $query; + } + function get_lotw_qsos_to_upload($station_id, $start_date, $end_date) { $this->db->select('COL_PRIMARY_KEY,COL_CALL, COL_BAND, COL_BAND_RX, COL_TIME_ON, COL_RST_RCVD, COL_RST_SENT, COL_MODE, COL_SUBMODE, COL_FREQ, COL_FREQ_RX, COL_GRIDSQUARE, COL_SAT_NAME, COL_PROP_MODE, COL_LOTW_QSL_SENT, station_id'); @@ -5529,6 +5577,21 @@ class Logbook_model extends CI_Model { return $query; } + function mark_dcl_sent($qso_id) { + + $data = array( + 'COL_DCL_QSLSDATE' => date("Y-m-d H:i:s"), + 'COL_DCL_QSL_SENT' => 'Y', + ); + + + $this->db->where('COL_PRIMARY_KEY', $qso_id); + + $this->db->update($this->config->item('table_name'), $data); + + return "Updated"; + } + function mark_lotw_sent($qso_id) { $data = array( diff --git a/application/models/Logbookadvanced_model.php b/application/models/Logbookadvanced_model.php index a5f63bc7e..5161a884f 100644 --- a/application/models/Logbookadvanced_model.php +++ b/application/models/Logbookadvanced_model.php @@ -811,6 +811,8 @@ class Logbookadvanced_model extends CI_Model { case "qrzreceived": $column = 'COL_QRZCOM_QSO_DOWNLOAD_STATUS'; break; case "eqslsent": $column = 'COL_EQSL_QSL_SENT'; break; case "eqslreceived": $column = 'COL_EQSL_QSL_RCVD'; break; + case "dclsent": $column = 'COL_DCL_QSL_SENT'; break; + case "dclreceived": $column = 'COL_DCL_QSL_RCVD'; break; case "stationpower": $column = 'COL_TX_PWR'; break; case "clublogsent": $column = 'COL_CLUBLOG_QSO_UPLOAD_STATUS'; break; case "clublogreceived": $column = 'COL_CLUBLOG_QSO_DOWNLOAD_STATUS'; break; @@ -985,6 +987,20 @@ class Logbookadvanced_model extends CI_Model { " SET " . $this->config->item('table_name').".COL_LOTW_QSL_RCVD = ?, " . $this->config->item('table_name').".COL_LOTW_QSLRDATE = now()" . " WHERE " . $this->config->item('table_name').".col_primary_key in ? and station_profile.user_id = ?"; + $query = $this->db->query($sql, array($value, json_decode($ids, true), $this->session->userdata('user_id'))); + } else if ($column == 'COL_DCL_QSL_SENT') { + + $sql = "UPDATE ".$this->config->item('table_name')." JOIN station_profile ON ". $this->config->item('table_name').".station_id = station_profile.station_id" . + " SET " . $this->config->item('table_name').".COL_DCL_QSL_SENT = ?, " . $this->config->item('table_name').".COL_DCL_QSLSDATE = now()" . + " WHERE " . $this->config->item('table_name').".col_primary_key in ? and station_profile.user_id = ?"; + + $query = $this->db->query($sql, array($value, json_decode($ids, true), $this->session->userdata('user_id'))); + } else if ($column == 'COL_DCL_QSL_RCVD') { + + $sql = "UPDATE ".$this->config->item('table_name')." JOIN station_profile ON ". $this->config->item('table_name').".station_id = station_profile.station_id" . + " SET " . $this->config->item('table_name').".COL_DCL_QSL_RCVD = ?, " . $this->config->item('table_name').".COL_DCL_QSLRDATE = now()" . + " WHERE " . $this->config->item('table_name').".col_primary_key in ? and station_profile.user_id = ?"; + $query = $this->db->query($sql, array($value, json_decode($ids, true), $this->session->userdata('user_id'))); } else if ($column == 'COL_QRZCOM_QSO_UPLOAD_STATUS') { diff --git a/application/models/User_model.php b/application/models/User_model.php index f08f6daef..4bad6c5cb 100644 --- a/application/models/User_model.php +++ b/application/models/User_model.php @@ -55,6 +55,14 @@ class User_Model extends CI_Model { return $r; } + // FUNCTION: object get_all_dcl_users + // Returns all users with dcl details + function get_all_dcl_users() { + $sql="SELECT distinct user_id from user_options where option_name='dcl_key' and option_key='key' and option_value is not null"; + $resu=$this->db->query($sql); + return $resu->result(); + } + // FUNCTION: object get_by_email($email) // Retrieve a user by email address function get_by_email($email) { @@ -347,7 +355,7 @@ class User_Model extends CI_Model { 'user_amsat_status_upload' => xss_clean($fields['user_amsat_status_upload']), 'user_mastodon_url' => xss_clean($fields['user_mastodon_url']), 'user_default_band' => xss_clean($fields['user_default_band']), - 'user_default_confirmation' => (isset($fields['user_default_confirmation_qsl']) ? 'Q' : '').(isset($fields['user_default_confirmation_lotw']) ? 'L' : '').(isset($fields['user_default_confirmation_eqsl']) ? 'E' : '').(isset($fields['user_default_confirmation_qrz']) ? 'Z' : '').(isset($fields['user_default_confirmation_clublog']) ? 'C' : ''), + 'user_default_confirmation' => (isset($fields['user_default_confirmation_qsl']) ? 'Q' : '').(isset($fields['user_default_confirmation_lotw']) ? 'L' : '').(isset($fields['user_default_confirmation_eqsl']) ? 'E' : '').(isset($fields['user_default_confirmation_qrz']) ? 'Z' : '').(isset($fields['user_default_confirmation_clublog']) ? 'C' : '').(isset($fields['user_default_confirmation_dcl']) ? 'D' : ''), 'user_qso_end_times' => xss_clean($fields['user_qso_end_times']), 'user_quicklog' => xss_clean($fields['user_quicklog']), 'user_quicklog_enter' => xss_clean($fields['user_quicklog_enter']), diff --git a/application/models/User_options_model.php b/application/models/User_options_model.php index d69094d5c..a8d3e174c 100644 --- a/application/models/User_options_model.php +++ b/application/models/User_options_model.php @@ -8,8 +8,10 @@ class User_options_model extends CI_Model { return $this->db->get('user_options'); } - public function set_option($option_type, $option_name, $option_array) { - $uid=$this->session->userdata('user_id'); + public function set_option($option_type, $option_name, $option_array, $uid=null) { + if (($uid ?? '') == '') { + $uid=$this->session->userdata('user_id'); + } $sql='insert into user_options (user_id,option_type,option_name,option_key,option_value) values (?,?,?,?,?) ON DUPLICATE KEY UPDATE option_value=?'; foreach($option_array as $option_key => $option_value) { $query = $this->db->query($sql, array($uid, $option_type, $option_name, $option_key, $option_value, $option_value)); diff --git a/application/views/adif/data/dcl.php b/application/views/adif/data/dcl.php new file mode 100644 index 000000000..83076d77c --- /dev/null +++ b/application/views/adif/data/dcl.php @@ -0,0 +1,11 @@ +Wavelog ADIF export +3.1.5 +config->item('app_name')); ?>>config->item('app_name')."\r\n"; ?> +optionslib->get_option('version')); ?>>optionslib->get_option('version')."\r\n"; ?> + + +result() as $qso) { + echo $this->adifhelper->getAdifLine($qso); +} diff --git a/application/views/dcl_views/import.php b/application/views/dcl_views/import.php new file mode 100644 index 000000000..9b28f893c --- /dev/null +++ b/application/views/dcl_views/import.php @@ -0,0 +1,60 @@ +
+ +

-

+ + + + + +
+
+
+ + + load->view('layout/messages'); ?> + + + + +config->item('disable_manual_dcl')) { ?> +
+
+ + +

+

:

+
+
+ +
+
+
+
+
+ + result() as $call) { + $options[$call->callsign] = $call->callsign; + } + ksort($options); + array_unshift($options, __("All")); + echo form_dropdown('callsign', $options, 'All'); + ?> +
+
+
+ +

+
+ + + " /> + + +
+
+ +
diff --git a/application/views/dcl_views/index.php b/application/views/dcl_views/index.php new file mode 100644 index 000000000..a58bbaa42 --- /dev/null +++ b/application/views/dcl_views/index.php @@ -0,0 +1,91 @@ +
+
+

+ + +
+
+ + +
+ +
+ + + + + load->view('layout/messages'); ?> + + 0) { ?> + +
+ + + + + + + + + + + + Callsigns, fn($a, $b) => $a->startDate <=> $b->startDate); + foreach ($row->Callsigns as $dcl_call) { + if (($dcl_call->endDate ?? '') == '') { + $dcl_call->endDate='-------'; + } else { + $dcl_call->endDate=date($date_format,strtotime($dcl_call->endDate)); + } + ?> + + + startDate)); + $vt = $dcl_call->endDate; + ?> + + + + + + +
callsign; ?>
+
+ + + + + +
+
+ + +
+ + + config->item('disable_manual_dcl'))) { ?> +
+
+ +
+ +
+ ".__("The next automatic sync with DCL will happen at: ").$next_run."

"; } ?> + + +

+
+
+
+ + +
diff --git a/application/views/dcl_views/key_import.php b/application/views/dcl_views/key_import.php new file mode 100644 index 000000000..2b2a7edf4 --- /dev/null +++ b/application/views/dcl_views/key_import.php @@ -0,0 +1,68 @@ +
+ +

+ + +
+
+ +
+ +
+ + +
+ + +
: + + + + + + DOKs, fn($a, $b) => $a->startDate <=> $b->startDate); + foreach ($dcl_info->DOKs as $key => $value) { + if (($value->endDate ?? '') == '') { + $value->endDate='-------'; + } else { + $value->endDate=date($date_format,strtotime($value->endDate)); + } + echo ""; + echo ""; + echo ""; + echo ""; + } + ?> +
DOK
".$value->dok."".date($date_format,strtotime($value->startDate)).' - '.$value->endDate."
+
+ : + + + + + + Callsigns, fn($a, $b) => $a->startDate <=> $b->startDate); + foreach ($dcl_info->Callsigns as $key => $value) { + if (($value->endDate ?? '') == '') { + $value->endDate='-------'; + } else { + $value->endDate=date($date_format,strtotime($value->endDate)); + } + echo ""; + echo ""; + echo ""; + echo ""; + } + echo "
".$value->callsign."".date($date_format,strtotime($value->startDate)).' - '.$value->endDate."
"; + } else { ?> + + +
+
+
+
diff --git a/application/views/dcl_views/upload_cert.php b/application/views/dcl_views/upload_cert.php new file mode 100644 index 000000000..0466bbb12 --- /dev/null +++ b/application/views/dcl_views/upload_cert.php @@ -0,0 +1,44 @@ +
+ +

+ + +
+
+ +
+ +
+ + + + + + + +
+ + +
+ + + + +
+
+ + +
diff --git a/application/views/interface_assets/header.php b/application/views/interface_assets/header.php index affd434da..e91b8ef9d 100644 --- a/application/views/interface_assets/header.php +++ b/application/views/interface_assets/header.php @@ -497,6 +497,10 @@
  • + config->item('enable_dcl_interface') ?? false) { ?> +
  • + diff --git a/application/views/logbookadvanced/edit.php b/application/views/logbookadvanced/edit.php index 754236c9b..92289eeaf 100644 --- a/application/views/logbookadvanced/edit.php +++ b/application/views/logbookadvanced/edit.php @@ -2,7 +2,6 @@
    -
     
    @@ -174,6 +174,12 @@ + + qrz->show ?? "true") == "true") { echo 'checked'; } ?>>
    + + +
    dcl->show ?? "true") == "true") { echo 'checked'; } ?>>
    +
    qslmsgs->show ?? "false") == "true") { echo 'checked'; } ?>>
    diff --git a/application/views/qso/edit_ajax.php b/application/views/qso/edit_ajax.php index b333bd082..e93cf1cb2 100644 --- a/application/views/qso/edit_ajax.php +++ b/application/views/qso/edit_ajax.php @@ -427,9 +427,12 @@ - +
    @@ -595,7 +598,33 @@
    -
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    +
    @@ -608,7 +637,6 @@
    -
    diff --git a/application/views/search/search_result_ajax.php b/application/views/search/search_result_ajax.php index 2964092c4..43e8bfde7 100644 --- a/application/views/search/search_result_ajax.php +++ b/application/views/search/search_result_ajax.php @@ -123,6 +123,9 @@ $ci =& get_instance(); session->userdata('user_clublog_name') != ''){ ?> + session->userdata('user_default_confirmation'),'D') !== false ) { ?> + + config->item('use_auth')) && ($this->session->userdata('user_type') >= 2)) { ?> @@ -397,6 +400,42 @@ $ci =& get_instance(); echo ''; } ?> + session->userdata('user_default_confirmation'),'D') !== false ) { + echo ''; + echo 'COL_DCL_QSL_SENT == "Y") { + echo "title=\"DCL ".__("Sent"); + if ($row->COL_DCL_QSLSDATE != null) { + $timestamp = strtotime($row->COL_DCL_QSLSDATE); + echo " ".($timestamp != '' ? date($custom_date_format, $timestamp) : ''); + } + echo "\" data-bs-toggle=\"tooltip\""; + } + echo ' class="dcl-'; + if ($row->COL_DCL_QSL_SENT=='Y') { + echo "green"; + } elseif ($row->COL_DCL_QSL_SENT=='M') { + echo "yellow"; + } else { + echo "red"; + } + echo '">▲'; + + echo 'COL_DCL_QSL_RCVD == "Y") { + echo "title=\"DCL ".__("Received"); + if ($row->COL_DCL_QSLRDATE != null) { + $timestamp = strtotime($row->COL_DCL_QSLRDATE); + echo " ".($timestamp != '' ? date($custom_date_format, $timestamp) : ''); + } + echo "\" data-bs-toggle=\"tooltip\""; + } + echo ' class="dcl-'; + echo ($row->COL_DCL_QSL_RCVD=='Y')?'green':'red'; + echo '">▼'; + echo ''; + } ?> + station_callsign)) { ?> diff --git a/application/views/user/edit.php b/application/views/user/edit.php index 2b7da7d42..d74395d11 100644 --- a/application/views/user/edit.php +++ b/application/views/user/edit.php @@ -593,6 +593,7 @@ +
    @@ -806,6 +807,14 @@ echo '>'; ?>
    +
    + '; ?> + +
    diff --git a/application/views/view_log/partial/log_ajax.php b/application/views/view_log/partial/log_ajax.php index c8118c436..ca50e5794 100644 --- a/application/views/view_log/partial/log_ajax.php +++ b/application/views/view_log/partial/log_ajax.php @@ -221,6 +221,9 @@ function echoQrbCalcLink($mygrid, $grid, $vucc, $isVisitor = false) { session->userdata('user_default_confirmation'),'C') !== false ) { ?> + session->userdata('user_default_confirmation'),'D') !== false ) { ?> + + config->item('use_auth')) && ($this->session->userdata('user_type') >= 2)) { ?> @@ -464,6 +467,42 @@ function echoQrbCalcLink($mygrid, $grid, $vucc, $isVisitor = false) { } ?> class="clublog-COL_CLUBLOG_QSO_DOWNLOAD_STATUS=='Y')?'green':'red'?>">▼ + session->userdata('user_default_confirmation'),'D') !== false ) { ?> + + COL_DCL_QSL_SENT == "Y") { + echo 'title="'.__("Sent").($row->COL_DCL_QSLSDATE != null ? " ".date($custom_date_format, strtotime($row->COL_DCL_QSLSDATE)) : '').'" data-bs-toggle="tooltip"'; + } elseif ($row->COL_DCL_QSL_SENT == 'M') { + echo 'title="'.__("Modified"); + if ($row->COL_DCL_QSLSDATE != null) { + echo "
    (".__("last sent")." ".date($custom_date_format, strtotime($row->COL_DCL_QSLSDATE)).")"; + } + echo '" data-bs-toggle="tooltip" data-bs-html="true"'; + } elseif ($row->COL_DCL_QSL_SENT == 'I') { + echo 'title="'.__("Invalid (Ignore)").'" data-bs-toggle="tooltip"'; + }?> class="dcl-COL_DCL_QSL_SENT == 'Y') { + echo 'green'; + } elseif ($row->COL_DCL_QSL_SENT == 'M') { + echo 'yellow'; + } elseif ($row->COL_DCL_QSL_SENT == 'I') { + echo 'grey'; + } else { + echo 'red'; + } ?>">▲
    + COL_DCL_QSL_RCVD == "Y") { + echo "title=\"".__("Received"); + if ($row->COL_DCL_QSLRDATE != null) { + $timestamp = strtotime($row->COL_DCL_QSLRDATE); + echo " ".($timestamp!=''?date($custom_date_format, $timestamp):''); + } + echo "\" data-bs-toggle=\"tooltip\""; + } ?> class="dcl-COL_DCL_QSL_RCVD=='Y')?'green':'red'?>">▼ + diff --git a/application/views/view_log/qso.php b/application/views/view_log/qso.php index 307c12507..3090954c0 100644 --- a/application/views/view_log/qso.php +++ b/application/views/view_log/qso.php @@ -566,10 +566,20 @@

    COL_CLUBLOG_QSO_DOWNLOAD_DATE); echo date($custom_date_format, $timestamp); ?>.

    - COL_CLUBLOG_QSO_DOWNLOAD_STATUS == "Y" && $row->COL_CLUBLOG_QSO_DOWNLOAD_DATE == null) { ?> + COL_CLUBLOG_QSO_DOWNLOAD_STATUS == "Y" && $row->COL_CLUBLOG_QSO_DOWNLOAD_DATE == null) { ?>

    + + COL_DCL_QSL_RCVD == "Y" && $row->COL_DCL_QSLRDATE != null) { ?> +

    +

    COL_DCL_QSLRDATE); echo date($custom_date_format, $timestamp); ?>.

    + + + COL_DCL_QSL_RCVD == "Y" && $row->COL_DCL_QSLRDATE == null) { ?> +

    +

    +
    diff --git a/assets/css/general.css b/assets/css/general.css index 925e1be9d..94d6d99f6 100644 --- a/assets/css/general.css +++ b/assets/css/general.css @@ -365,6 +365,26 @@ TD.clublog { font-size: 1.1em; } +TD.dcl { + width: 33px; + white-space: nowrap; +} + +.dcl-green { + color: #00a000 !important; + font-size: 1.1em; +} + +.dcl-yellow { + color: #d39e00 !important; + font-size: 1.1em; +} + +.dcl-red { + color: #f00 !important; + font-size: 1.1em; +} + TD.qrz { width: 33px; white-space: nowrap; diff --git a/assets/js/sections/logbookadvanced.js b/assets/js/sections/logbookadvanced.js index f454538ce..be6f2bff5 100644 --- a/assets/js/sections/logbookadvanced.js +++ b/assets/js/sections/logbookadvanced.js @@ -89,6 +89,9 @@ function updateRow(qso) { if ((user_options.qrz.show ?? 'true') == "true"){ cells.eq(c++).html(qso.qrz); } + if ((user_options.dcl.show ?? 'true') == "true"){ + cells.eq(c++).html(qso.dcl); + } if ((user_options.qslmsgs.show ?? 'true') == "true"){ cells.eq(c++).text(qso.qslMessage); } @@ -274,6 +277,9 @@ function loadQSOTable(rows) { if ((user_options.qrz.show ?? 'true') == "true"){ data.push(qso.qrz); } + if ((user_options.dcl.show ?? 'true') == "true"){ + data.push(qso.dcl); + } if ((user_options.qslmsgs.show ?? 'true') == "true"){ data.push(qso.qslMessage); } @@ -1498,6 +1504,7 @@ function saveOptions() { nightshadow_layer: $('input[name="nightshadow"]').is(':checked') ? true : false, qth: $('input[name="qth"]').is(':checked') ? true : false, frequency: $('input[name="frequency"]').is(':checked') ? true : false, + dcl: $('input[name="dcl"]').is(':checked') ? true : false, }, success: function(data) { $('#saveButton').prop("disabled", false); diff --git a/assets/js/sections/logbookadvanced_edit.js b/assets/js/sections/logbookadvanced_edit.js index 22f60d0bd..4d73a1e1a 100644 --- a/assets/js/sections/logbookadvanced_edit.js +++ b/assets/js/sections/logbookadvanced_edit.js @@ -186,6 +186,9 @@ function saveBatchEditQsos(id_list) { if (column == 'qrzsent' || column == 'qrzreceived') { value = $("#editQrz").val(); } + if (column == 'dclsent' || column == 'dclreceived') { + value = $("#editDcl").val(); + } if (column == 'eqslsent' || column == 'eqslreceived') { value = $("#editEqsl").val(); } @@ -261,6 +264,7 @@ function changeEditType(type) { $('#editLoTW').hide(); $('#editContinent').hide(); $('#editQrz').hide(); + $('#editDcl').hide(); $('#saveButton').prop("disabled", false); $('#editEqsl').hide(); $('#editRegion').hide(); @@ -309,6 +313,8 @@ function changeEditType(type) { $('#editLoTW').show(); } else if (type == "qrzsent" || type == "qrzreceived") { $('#editQrz').show(); + } else if (type == "dclsent" || type == "dclreceived") { + $('#editDcl').show(); } else if (type == "eqslsent" || type == "eqslreceived") { $('#editEqsl').show(); } else if (type == "continent") {