Merge pull request #2252 from int2001/dcl_frontend

DCL Connector
This commit is contained in:
Joerg (DJ7NT)
2025-09-06 12:34:28 +02:00
committed by GitHub
30 changed files with 989 additions and 25 deletions

View File

@@ -787,4 +787,13 @@ $config['max_login_attempts'] = 3;
|--------------------------------------------------------------------------
*/
$config['disable_user_stats'] = false;
$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;

View File

@@ -22,7 +22,7 @@ $config['migration_enabled'] = TRUE;
|
*/
$config['migration_version'] = 253;
$config['migration_version'] = 254;
/*
|--------------------------------------------------------------------------

View File

@@ -0,0 +1,269 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Dcl extends CI_Controller {
/* Controls who can access the controller and its functions */
function __construct() {
parent::__construct();
$this->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.")."<br>";
}
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).")<br>";
if (curl_errno($ch) == 28) { // break on timeout
echo __("Timeout reached. Stopping subsequent uploads.")."<br>";
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).")<br>";
if (curl_errno($ch) == 28) { // break on timeout
echo __("Timeout reached. Stopping subsequent uploads.")."<br>";
break;
} else {
continue;
}
} else {
echo $station_profile->station_callsign." (".$station_profile->station_profile_name."): ".__("Upload Successful")." ".count($qso_id_array)." QSOs<br>";
// 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 "<br><br>";
$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

View File

@@ -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,

View File

@@ -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);

View File

@@ -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');

View File

@@ -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";
}

View File

@@ -0,0 +1,43 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_dcl_cron extends CI_Migration {
public function up() {
if ($this->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());
}
}
}

View File

@@ -0,0 +1,92 @@
<?php
class Dcl_model extends CI_Model {
/*
|--------------------------------------------------------------------------
| Function: dcl_keys
|--------------------------------------------------------------------------
|
| Returns all dcl_keys for a selected user via the $user_id parameter
|
*/
function dcl_keys($user_id) {
$this->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;
}
}
}
?>

View File

@@ -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_ON<?';
$binding[] = $station_id;
$binding[] = date('Y-m-d', $from);
$binding[] = date('Y-m-d', $till);
$query = $this->db->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(

View File

@@ -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') {

View File

@@ -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']),

View File

@@ -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));

View File

@@ -0,0 +1,11 @@
Wavelog ADIF export
<ADIF_VER:5>3.1.5
<PROGRAMID:<?php echo strlen($this->config->item('app_name')); ?>><?php echo $this->config->item('app_name')."\r\n"; ?>
<PROGRAMVERSION:<?php echo strlen($this->optionslib->get_option('version')); ?>><?php echo $this->optionslib->get_option('version')."\r\n"; ?>
<EOH>
<?php
foreach ($qsos->result() as $qso) {
echo $this->adifhelper->getAdifLine($qso);
}

View File

@@ -0,0 +1,60 @@
<div class="container dcl">
<h2><?= __("DCL"); ?> - <?= __("ADIF Import"); ?></h2>
<?php if (isset($errormsg)) { ?>
<div class="alert alert-danger" role="alert">
<?php echo $errormsg; ?>
</div>
<?php } ?>
<div class="card">
<div class="card-header"><?= __("Import Options"); ?></div>
<div class="card-body">
<?php $this->load->view('layout/messages'); ?>
<?php echo form_open_multipart('dcl_views/import'); ?>
<?php if (!$this->config->item('disable_manual_dcl')) { ?>
<div>
<div class="form-check">
<input type="radio" name="dclimport" id="fetch" class="form-check-input" value="fetch" checked="checked" />
<label class="form-check-label" for="fetch"><?= __("Pull DCL data for me"); ?></label>
<br><br>
<p class="card-text"><?= __("From date"); ?>:</p>
<div class="row">
<div class="col-md-3">
<input name="from" id="from" type="date" class="form-control w-auto">
</div>
</div>
<br />
<div class="row">
<div class="col-md-3">
<label class="form-check-label" for="callsign"><?= __("Select callsign to pull DCL confirmations for."); ?></label>
<?php
$options = [];
foreach ($callsigns->result() as $call) {
$options[$call->callsign] = $call->callsign;
}
ksort($options);
array_unshift($options, __("All"));
echo form_dropdown('callsign', $options, 'All');
?>
</div>
</div>
<br />
<p class="form-text text-muted"><?= __("Wavelog will use the DCL Keys stored in your user profile to download a report from DCL for you. The report Wavelog downloads will have all confirmations since chosen date, or since your last DCL confirmation (fetched from your log), up until now."); ?></p>
</div>
<?php } ?>
<input class="btn btn-primary" type="submit" value="<?= __("Import DCL Matches"); ?>" />
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,91 @@
<div class="container dcl">
<br>
<h2><?= __("DCL"); ?></h2>
<!-- Card Starts -->
<div class="card">
<div class="card-header">
<a style="margin-left: 1em;" class="btn btn-outline-danger btn-sm float-end" href="<?php echo site_url('dcl/delete_key'); ?>" role="button"><i class="far fa-trash-alt"></i> <?= __("Delete Keys"); ?></a>
<a class="btn btn-outline-success btn-sm float-end" href="<?php echo site_url('/dcl/key_import'); ?>" role="button"><i class="fas fa-cloud-upload-alt"></i> <?= __("Request DCL Key"); ?></a><i class="fab fa-expeditedssl"></i> <?= __("Available DCL-Keys"); ?>
</div>
<div class="key-list">
<?php if(isset($error)) { ?>
<div class="alert alert-danger" role="alert">
<?php echo $error; ?>
</div>
<?php } ?>
<?php $this->load->view('layout/messages'); ?>
<?php if (count($dcl_keys ?? []) > 0) { ?>
<div class="table-responsive">
<table class="table table-hover">
<thead class="thead-light">
<tr>
<th scope="col"><?= __("Callsign"); ?></th>
<th scope="col"><?= __("Valid from"); ?></th>
<th scope="col"><?= __("Valid till"); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($dcl_keys as $row) {
usort($row->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));
}
?>
<tr>
<td><?php echo $dcl_call->callsign; ?></td>
<?php
$vf = date($date_format,strtotime($dcl_call->startDate));
$vt = $dcl_call->endDate;
?>
<td><?php echo $vf; ?></td>
<td><?php echo $vt; ?></td>
</tr>
<?php }} ?>
</tbody>
</table>
</div>
<?php } else { ?>
<div class="alert alert-info" role="alert">
<?= __("You need to request DCL keys to use this function."); ?>
</div>
<?php } ?>
</div>
</div>
<!-- Card Ends -->
<br>
<!-- Card Starts -->
<?php
if (!($this->config->item('disable_manual_dcl'))) { ?>
<div class="card">
<div class="card-header">
<?= __("Information"); ?>
</div>
<div class="card-body">
<?php if (($next_run ?? '') != '') { echo "<p>".__("The next automatic sync with DCL will happen at: ").$next_run."</p>"; } ?>
<button class="btn btn-outline-success" hx-on:click="document.getElementById('dcl_manual_results').innerHTML = '';" hx-get="<?php echo site_url('dcl/dcl_upload'); ?>" hx-indicator="#lotw-sync-running" hx-target="#dcl_manual_results">
<?= __("Manual Sync"); ?>
</button>
<span style="margin-left: 10px;" id="lotw-sync-running" class="htmx-indicator"> <?php echo __("running..."); ?></span>
<br/><br/>
<div id="dcl_manual_results"></div>
</div>
</div>
<?php } ?>
</div>

View File

@@ -0,0 +1,68 @@
<div class="container dcl">
<h2><?= __("DCL Key Import"); ?></h2>
<!-- Card Starts -->
<div class="card">
<div class="card-header">
<?= __("DCL Key Management"); ?>
</div>
<div class="card-body">
<div class="alert alert-info" role="alert">
<h5><?= __("Import Key"); ?></h5>
<?= __("You requested a key for DCL-Dataexchange, which was successful. The following data has been imported"); ?>
</div>
<div class="mb-3">
<?php if (($is_valid) && ($dcl_info)) { ?>
<b><?= __("Received a valid DCL-Key"); ?></b>
<br/><?= __("DOK History"); ?>:
<table class="table-sm table table-hover table-striped table-condensed dataTable">
<tr>
<th>DOK</th>
<th><?= __("Validity"); ?></th>
</tr>
<?php
usort($dcl_info->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 "<tr>";
echo "<td>".$value->dok."</td>";
echo "<td>".date($date_format,strtotime($value->startDate)).' - '.$value->endDate."</td>";
echo "</tr>";
}
?>
</table>
<br/>
<?= __("Call History"); ?>:
<table class="table-sm table table-hover table-striped table-condensed dataTable">
<tr>
<th><?= __("Call"); ?></th>
<th><?= __("Validity"); ?></th>
</tr>
<?php
usort($dcl_info->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 "<tr>";
echo "<td>".$value->callsign."</td>";
echo "<td>".date($date_format,strtotime($value->startDate)).' - '.$value->endDate."</td>";
echo "</tr>";
}
echo "</table>";
} else { ?>
<b><?= __("Received an invalid DCL-Key. Please check your DCL-Login, and try again"); ?></b>
<?php } ?>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,44 @@
<div class="container lotw">
<h2><?= __("Logbook of the World"); ?></h2>
<!-- Card Starts -->
<div class="card">
<div class="card-header">
<?= __("Upload Logbook of the World .p12 Certificate"); ?>
</div>
<div class="card-body">
<?php if($error != " ") { ?>
<div class="alert alert-danger" role="alert">
<?php echo $error; ?>
</div>
<?php } ?>
<div class="alert alert-info" role="alert">
<h5><?= __("Export .p12 File Instructions"); ?></h5>
<ul>
<li><b><?= __("Use at least version 2.7 of TQSL"); ?></b></li>
<li><?= __("Open TQSL and go to the Callsign Certificates Tab"); ?></li>
<li><?= __("Right click on desired Callsign"); ?></li>
<li><?= __("Click 'Save Callsign Certificate File'"); ?></li>
<li><b><?= __("Do not add a password"); ?></b></li>
<li><?= __("Upload File below"); ?></li>
</ul>
</div>
<?php echo form_open_multipart('lotw/do_cert_upload');?>
<div class="mb-3">
<label for="exampleFormControlFile1"><?= __("Upload Logbook of the World .p12 Certificate"); ?></label>
<input type="file" name="userfile" class="form-control" id="exampleFormControlFile1">
</div>
<button type="submit" value="upload" class="btn btn-primary"><?= __("Upload File"); ?></button>
</form>
</div>
</div>
<!-- Card Ends -->
</div>

View File

@@ -497,6 +497,10 @@
<li><a class="dropdown-item" href="<?php echo site_url('qrz/export'); ?>" title="Upload to QRZ.com logbook"><i class="fas fa-sync"></i> <?= __("QRZ Logbook"); ?></a></li>
<li><a class="dropdown-item" href="<?php echo site_url('webadif/export'); ?>" title="Upload to webADIF"><i class="fas fa-sync"></i> <?= __("QO-100 Dx Club Upload"); ?></a></li>
<li><a class="dropdown-item" href="<?php echo site_url('clublog/export'); ?>" title="Upload to Clublog"><i class="fas fa-sync"></i> <?= __("Clublog Import / Export"); ?></a></li>
<?php
if ($this->config->item('enable_dcl_interface') ?? false) { ?>
<li><a class="dropdown-item" href="<?php echo site_url('dcl'); ?>" title="Upload to DCL"><i class="fas fa-sync"></i> <?= __("DCL Export"); ?></a></li>
<?php } ?>
</ul>
</li>
<div class="dropdown-divider"></div>

View File

@@ -2,7 +2,6 @@
<form method="post" class="d-flex align-items-center">
<select id="editColumn" name="type" class="form-select form-select-sm w-auto me-2">
<option value="">-</option>
<optgroup label="<?= __("QSO details"); ?>">
<option value="band"><?= __("Band"); ?></option>
<option value="comment"><?= __("Comment"); ?></option>
@@ -33,6 +32,8 @@
<option value="sota"><?= __("SOTA"); ?></option>
<option value="state"><?= __("State"); ?></option>
<option value="wwff"><?= __("WWFF"); ?></option>
<option value="dclsent"><?= __("DCL Sent"); ?></option>
<option value="dclreceived"><?= __("DCL Received"); ?></option>
</optgroup>
<optgroup label="<?= __("QSL / LoTW / Clublog / eQSL / QRZ"); ?>">
@@ -51,7 +52,6 @@
</optgroup>
</select>
<div>&nbsp;</div>
<input style="display:none" class="form-control form-control-sm w-auto" id="editTextInput" type="text" name="editTextInput" placeholder="" aria-label="editTextInput">
@@ -174,6 +174,12 @@
<option value="I"><?= __("Invalid"); ?></option>
</select>
<select style="display:none" class="form-select w-auto form-select-sm w-auto" id="editDcl" name="dcl">
<option value="Y"><?= __("Yes"); ?></option>
<option value="N"><?= __("No"); ?></option>
<option value="I"><?= __("Invalid"); ?></option>
</select>
<select style="display:none" class="form-select w-auto form-select-sm w-auto" id="editEqsl" name="eqsl">
<option value="Y"><?= __("Yes"); ?></option>
<option value="N"><?= __("No"); ?></option>

View File

@@ -67,6 +67,7 @@
\"county\":{\"show\":\"true\"},
\"qth\":{\"show\":\"true\"},
\"frequency\":{\"show\":\"true\"},
\"dcl\":{\"show\":\"true\"},
}";
}
$current_opts = json_decode($options);
@@ -171,6 +172,10 @@
echo "\nvar o_template = { frequency: {show: 'true'}};";
echo "\nuser_options={...user_options, ...o_template};";
}
if (!isset($current_opts->dcl)) {
echo "\nvar o_template = { dcl: {show: 'true'}};";
echo "\nuser_options={...user_options, ...o_template};";
}
foreach ($mapoptions as $mo) {
@@ -735,6 +740,9 @@ $options = json_decode($options);
} ?>
<?php if (($options->qrz->show ?? "true") == "true") {
echo '<th class="qrz">' . __("QRZ") . '</th>';
} ?>
<?php if (($options->dcl->show ?? "true") == "true") {
echo '<th>' . __("DCL") . '</th>';
} ?>
<?php if (($options->qslmsgs->show ?? "false") == "true") {
echo '<th>' . __("QSL Msg (S)") . '</th>';

View File

@@ -86,6 +86,10 @@
<td><?= __("QRZ"); ?></td>
<td><div class="form-check"><input class="form-check-input" name="qrz" type="checkbox" <?php if (($options->qrz->show ?? "true") == "true") { echo 'checked'; } ?>></div></td>
</tr>
<tr>
<td><?= __("DCL"); ?></td>
<td><div class="form-check"><input class="form-check-input" name="dcl" type="checkbox" <?php if (($options->dcl->show ?? "true") == "true") { echo 'checked'; } ?>></div></td>
</tr>
<tr>
<td><?= __("QSL Msg (S)"); ?></td>
<td><div class="form-check"><input class="form-check-input" name="qslmsgs" type="checkbox" <?php if (($options->qslmsgs->show ?? "false") == "true") { echo 'checked'; } ?>></div></td>

View File

@@ -427,9 +427,12 @@
<li class="nav-item">
<a class="nav-link" id="qrz-tab" data-bs-toggle="tab" href="#qrz-edit" role="tab" aria-controls="qrz" aria-selected="false"><?= __("QRZ"); ?></a>
</li>
<li class="nav-item">
<li class="nav-item">
<a class="nav-link" id="clublog-tab" data-bs-toggle="tab" href="#clublog-edit" role="tab" aria-controls="clublog" aria-selected="false"><?= __("Clublog"); ?></a>
</li>
<li class="nav-item">
<a class="nav-link" id="dcl-tab" data-bs-toggle="tab" href="#dcl-edit" role="tab" aria-controls="dcl" aria-selected="false"><?= __("DCL"); ?></a>
</li>
</ul>
<div class="tab-content" id="qsl_edit_tabs">
<div class="tab-pane fade show active" id="qsl-edit" role="tabpanel" aria-labelledby="qsl-edit-tab">
@@ -595,7 +598,33 @@
</div>
</div>
</div>
<div class="tab-pane fade" id="clublog-edit" role="tabpanel" aria-labelledby="clublog-tab">
<div class="tab-pane fade" id="dcl-edit" role="tabpanel" aria-labelledby="dcl-tab">
<div class="mt-3 mb-3 row">
<label for="sent" class="col-sm-3 col-form-label"><?= __("Sent"); ?></label>
<div class="col-sm-9">
<select class="form-select" id="clublog_sent" name="dcl_sent">
<option value="N" <?php if ($qso->COL_DCL_QSL_SENT == "N") echo "selected=\"selected\""; ?>><?= __("No"); ?></option>
<option value="Y" <?php if ($qso->COL_DCL_QSL_SENT == "Y") echo "selected=\"selected\""; ?>><?= __("Yes"); ?></option>
<option value="R" <?php if ($qso->COL_DCL_QSL_SENT == "R") echo "selected=\"selected\""; ?>><?= __("Requested"); ?></option>
<option value="Q" <?php if ($qso->COL_DCL_QSL_SENT == "Q") echo "selected=\"selected\""; ?>><?= __("Queued"); ?></option>
<option value="I" <?php if ($qso->COL_DCL_QSL_SENT == "I") echo "selected=\"selected\""; ?>><?= __("Invalid (Ignore)"); ?></option>
</select>
</div>
</div>
<div class="mb-3 row">
<label for="sent" class="col-sm-3 col-form-label"><?= __("Received"); ?></label>
<div class="col-sm-9">
<select class="form-select" id="clublog_rcvd" name="dcl_rcvd">
<option value="N" <?php if ($qso->COL_DCL_QSL_RCVD == "N") echo "selected=\"selected\""; ?>><?= __("No"); ?></option>
<option value="Y" <?php if ($qso->COL_DCL_QSL_RCVD == "Y") echo "selected=\"selected\""; ?>><?= __("Yes"); ?></option>
<option value="R" <?php if ($qso->COL_DCL_QSL_RCVD == "R") echo "selected=\"selected\""; ?>><?= __("Requested"); ?></option>
<option value="I" <?php if ($qso->COL_DCL_QSL_RCVD == "I") echo "selected=\"selected\""; ?>><?= __("Invalid (Ignore)"); ?></option>
<option value="V" <?php if ($qso->COL_DCL_QSL_RCVD == "V") echo "selected=\"selected\""; ?>><?= __("Verified (Match)"); ?></option>
</select>
</div>
</div>
</div>
<div class="tab-pane fade" id="clublog-edit" role="tabpanel" aria-labelledby="clublog-tab">
<div class="mt-3 mb-3 row">
<label for="sent" class="col-sm-3 col-form-label"><?= __("Sent"); ?></label>
<div class="col-sm-9">
@@ -608,7 +637,6 @@
</select>
</div>
</div>
<div class="mb-3 row">
<label for="sent" class="col-sm-3 col-form-label"><?= __("Received"); ?></label>
<div class="col-sm-9">

View File

@@ -123,6 +123,9 @@ $ci =& get_instance();
<?php if($this->session->userdata('user_clublog_name') != ''){ ?>
<th><?= __("Clublog"); ?></th>
<?php } ?>
<?php if ( strpos($this->session->userdata('user_default_confirmation'),'D') !== false ) { ?>
<th><?= __("DCL"); ?></th>
<?php } ?>
<?php } ?>
<th><?= __("Station"); ?></th>
<?php if(($this->config->item('use_auth')) && ($this->session->userdata('user_type') >= 2)) { ?>
@@ -397,6 +400,42 @@ $ci =& get_instance();
echo '</td>';
} ?>
<?php if ( strpos($this->session->userdata('user_default_confirmation'),'D') !== false ) {
echo '<td style=\'text-align: center\' class="dcl">';
echo '<span ';
if ($row->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 '">&#9650;</span>';
echo '<span ';
if ($row->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 '">&#9660;</span>';
echo '</td>';
} ?>
<?php } ?>
<?php if(isset($row->station_callsign)) { ?>

View File

@@ -593,6 +593,7 @@
<option value="2" <?php if ($user_previous_qsl_type == 2) { echo " selected =\"selected\""; } ?>><?= __("eQSL"); ?></option>
<option value="4" <?php if ($user_previous_qsl_type == 4) { echo " selected =\"selected\""; } ?>><?= __("QRZ"); ?></option>
<option value="8" <?php if ($user_previous_qsl_type == 8) { echo " selected =\"selected\""; } ?>><?= __("Clublog"); ?></option>
<option value="16" <?php if ($user_previous_qsl_type == 16) { echo " selected =\"selected\""; } ?>><?= __("DCL"); ?></option>
</select>
</div>
</div>
@@ -806,6 +807,14 @@
echo '>'; ?>
<label class="form-check-label" for="user_default_confirmation_clublog"><?= __("Clublog"); ?></label>
</div>
<div class="form-check-inline">
<?php echo '<input class="form-check-input" type="checkbox" name="user_default_confirmation_dcl" id="user_default_confirmation_dcl"';
if (isset($user_default_confirmation) && strpos($user_default_confirmation, 'D') !== false) {
echo ' checked';
}
echo '>'; ?>
<label class="form-check-label" for="user_default_confirmation_dcl"><?= __("DCL"); ?></label>
</div>
</div>
</div>
</div>

View File

@@ -221,6 +221,9 @@ function echoQrbCalcLink($mygrid, $grid, $vucc, $isVisitor = false) {
<?php if ( strpos($this->session->userdata('user_default_confirmation'),'C') !== false ) { ?>
<th><?= __("Clublog"); ?></th>
<?php } ?>
<?php if ( strpos($this->session->userdata('user_default_confirmation'),'D') !== false ) { ?>
<th><?= __("DCL"); ?></th>
<?php } ?>
<?php } ?>
<th><?= __("Station"); ?></th>
<?php if(($this->config->item('use_auth')) && ($this->session->userdata('user_type') >= 2)) { ?>
@@ -464,6 +467,42 @@ function echoQrbCalcLink($mygrid, $grid, $vucc, $isVisitor = false) {
} ?> class="clublog-<?php
echo ($row->COL_CLUBLOG_QSO_DOWNLOAD_STATUS=='Y')?'green':'red'?>">&#9660;</span>
</td>
<?php }
if ( strpos($this->session->userdata('user_default_confirmation'),'D') !== false ) { ?>
<td class="dcl">
<span <?php
if ($row->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 "<br />(".__("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-<?php
if ($row->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';
} ?>">&#9650;</span>
<span <?php
if ($row->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-<?php
echo ($row->COL_DCL_QSL_RCVD=='Y')?'green':'red'?>">&#9660;</span>
</td>
<?php } ?>
<?php } ?>

View File

@@ -566,10 +566,20 @@
<p><?= __("This QSO was confirmed on"); ?> <?php $timestamp = strtotime($row->COL_CLUBLOG_QSO_DOWNLOAD_DATE); echo date($custom_date_format, $timestamp); ?>.</p>
<?php } ?>
<?php if($row->COL_CLUBLOG_QSO_DOWNLOAD_STATUS == "Y" && $row->COL_CLUBLOG_QSO_DOWNLOAD_DATE == null) { ?>
<?php if($row->COL_CLUBLOG_QSO_DOWNLOAD_STATUS == "Y" && $row->COL_CLUBLOG_QSO_DOWNLOAD_DATE == null) { ?>
<h3><?= __("Clublog"); ?></h3>
<p><?= __("This QSO is confirmed on Clublog."); ?></p>
<?php } ?>
<?php if($row->COL_DCL_QSL_RCVD == "Y" && $row->COL_DCL_QSLRDATE != null) { ?>
<h3><?= __("DCL"); ?></h3>
<p><?= __("This QSO was confirmed on"); ?> <?php $timestamp = strtotime($row->COL_DCL_QSLRDATE); echo date($custom_date_format, $timestamp); ?>.</p>
<?php } ?>
<?php if($row->COL_DCL_QSL_RCVD == "Y" && $row->COL_DCL_QSLRDATE == null) { ?>
<h3><?= __("DCL"); ?></h3>
<p><?= __("This QSO is confirmed on DCL."); ?></p>
<?php } ?>
</div>
<div class="col-md">

View File

@@ -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;

View File

@@ -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);

View File

@@ -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") {