From 16e90d938ed0be41a68826fed59476e74eaba1f4 Mon Sep 17 00:00:00 2001 From: Andreas <6977712+AndreasK79@users.noreply.github.com> Date: Wed, 22 Feb 2023 12:15:11 +0100 Subject: [PATCH 01/41] [Timeline] Fix for DXCC QSO details --- application/controllers/Timeline.php | 1 + 1 file changed, 1 insertion(+) diff --git a/application/controllers/Timeline.php b/application/controllers/Timeline.php index 61a3fb2e4..0cc5e3fa6 100644 --- a/application/controllers/Timeline.php +++ b/application/controllers/Timeline.php @@ -76,6 +76,7 @@ public function index() } public function details() { + $this->load->model('logbook_model'); $this->load->model('timeline_model'); $querystring = str_replace('"', "", $this->input->post("Querystring")); From 6d0e86ce55abcfaa7887e022f3ab1745775cbebe Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Fri, 24 Feb 2023 16:28:36 +0000 Subject: [PATCH 02/41] [Users] Improved CSS --- application/views/user/main.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/application/views/user/main.php b/application/views/user/main.php index da1bd291a..15d67aea7 100644 --- a/application/views/user/main.php +++ b/application/views/user/main.php @@ -1,14 +1,17 @@

-session->flashdata('notice')) { ?> -
- session->flashdata('notice'); ?> -
-

+session->flashdata('notice')) { ?> + + + + +
User List From 8e25e8dcfc985ade45baf175610777e39f102e9e Mon Sep 17 00:00:00 2001 From: abarrau Date: Wed, 1 Mar 2023 13:19:16 +0100 Subject: [PATCH 03/41] Update account_lang.php php tag not good --- application/language/french/account_lang.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/language/french/account_lang.php b/application/language/french/account_lang.php index a9da9722b..aa0d7298c 100644 --- a/application/language/french/account_lang.php +++ b/application/language/french/account_lang.php @@ -1,4 +1,4 @@ -?php + Date: Sat, 4 Mar 2023 21:36:41 +0100 Subject: [PATCH 04/41] Show cursor pointer while hovering DXCC summary header --- assets/css/general.css | 6 +++++- assets/js/sections/common.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/assets/css/general.css b/assets/css/general.css index 6d5b732c7..97718a791 100644 --- a/assets/css/general.css +++ b/assets/css/general.css @@ -415,4 +415,8 @@ div#station_logbooks_linked_table_paginate { .qso_panel .dxccsummary { margin-bottom: 10px; -} \ No newline at end of file +} + +.qso_panel .dxccsummaryheader { + cursor: pointer; +} diff --git a/assets/js/sections/common.js b/assets/js/sections/common.js index f06c65d60..92caf191f 100644 --- a/assets/js/sections/common.js +++ b/assets/js/sections/common.js @@ -443,7 +443,7 @@ function getDxccResult(dxcc, name) { }, success: function (html) { $('.dxccsummary').remove(); - $('.qsopane').append('

DXCC Summary for '+name+'
'); + $('.qsopane').append('

DXCC Summary for '+name+'
'); $('.dxccsummarybody').append(html); } }); From bc8b7bf6b07a37f0e883e4cbd3bf20332b7f1eac Mon Sep 17 00:00:00 2001 From: Andreas <6977712+AndreasK79@users.noreply.github.com> Date: Tue, 7 Mar 2023 19:36:39 +0100 Subject: [PATCH 05/41] [eQSL] Adjusted query to filter out stations without nickname set --- application/models/Logbook_model.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index 14fdc0639..479dc1f53 100755 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -2343,12 +2343,18 @@ function eqsl_dupe_check($datetime, $callsign, $band, $qsl_status) { function eqsl_not_yet_sent() { $this->db->select('station_profile.*, '.$this->config->item('table_name').'.COL_PRIMARY_KEY, '.$this->config->item('table_name').'.COL_TIME_ON, '.$this->config->item('table_name').'.COL_CALL, '.$this->config->item('table_name').'.COL_MODE, '.$this->config->item('table_name').'.COL_SUBMODE, '.$this->config->item('table_name').'.COL_BAND, '.$this->config->item('table_name').'.COL_COMMENT, '.$this->config->item('table_name').'.COL_RST_SENT, '.$this->config->item('table_name').'.COL_PROP_MODE, '.$this->config->item('table_name').'.COL_SAT_NAME, '.$this->config->item('table_name').'.COL_SAT_MODE, '.$this->config->item('table_name').'.COL_QSLMSG'); $this->db->from('station_profile'); - $this->db->join($this->config->item('table_name'),'station_profile.station_id = '.$this->config->item('table_name').'.station_id AND station_profile.eqslqthnickname != ""','right'); + $this->db->join($this->config->item('table_name'),'station_profile.station_id = '.$this->config->item('table_name').'.station_id'); + $this->db->group_start(); $this->db->where('station_profile.eqslqthnickname !=', ''); + $this->db->or_where('station_profile.eqslqthnickname is not null'); + $this->db->group_end(); $this->db->where($this->config->item('table_name').'.COL_CALL !=', ''); $this->db->where($this->config->item('table_name').'.COL_EQSL_QSL_SENT !=', 'Y'); + $this->db->group_start(); $this->db->where($this->config->item('table_name').'.COL_EQSL_QSL_SENT !=', 'I'); $this->db->or_where(array($this->config->item('table_name').'.COL_EQSL_QSL_SENT' => NULL)); + $this->db->group_end(); + return $this->db->get(); } From 363cda94bc70c393263810fafe059fb0e6855cfc Mon Sep 17 00:00:00 2001 From: Andreas <6977712+AndreasK79@users.noreply.github.com> Date: Thu, 9 Mar 2023 18:22:14 +0100 Subject: [PATCH 06/41] [eQSL] Adjusted query to filter out stations without nickname set (hopefully fixed all issues) --- application/models/Logbook_model.php | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index 479dc1f53..eb1fa4b45 100755 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -2344,15 +2344,14 @@ function eqsl_not_yet_sent() { $this->db->select('station_profile.*, '.$this->config->item('table_name').'.COL_PRIMARY_KEY, '.$this->config->item('table_name').'.COL_TIME_ON, '.$this->config->item('table_name').'.COL_CALL, '.$this->config->item('table_name').'.COL_MODE, '.$this->config->item('table_name').'.COL_SUBMODE, '.$this->config->item('table_name').'.COL_BAND, '.$this->config->item('table_name').'.COL_COMMENT, '.$this->config->item('table_name').'.COL_RST_SENT, '.$this->config->item('table_name').'.COL_PROP_MODE, '.$this->config->item('table_name').'.COL_SAT_NAME, '.$this->config->item('table_name').'.COL_SAT_MODE, '.$this->config->item('table_name').'.COL_QSLMSG'); $this->db->from('station_profile'); $this->db->join($this->config->item('table_name'),'station_profile.station_id = '.$this->config->item('table_name').'.station_id'); - $this->db->group_start(); - $this->db->where('station_profile.eqslqthnickname !=', ''); - $this->db->or_where('station_profile.eqslqthnickname is not null'); - $this->db->group_end(); + $this->db->where("coalesce(station_profile.eqslqthnickname, '') <> ''"); $this->db->where($this->config->item('table_name').'.COL_CALL !=', ''); - $this->db->where($this->config->item('table_name').'.COL_EQSL_QSL_SENT !=', 'Y'); - $this->db->group_start(); - $this->db->where($this->config->item('table_name').'.COL_EQSL_QSL_SENT !=', 'I'); - $this->db->or_where(array($this->config->item('table_name').'.COL_EQSL_QSL_SENT' => NULL)); + $this->db->group_start(); + $this->db->where($this->config->item('table_name').'.COL_EQSL_QSL_SENT is null'); + $this->db->or_where($this->config->item('table_name').'.COL_EQSL_QSL_SENT', ''); + $this->db->or_where($this->config->item('table_name').'.COL_EQSL_QSL_SENT', 'R'); + $this->db->or_where($this->config->item('table_name').'.COL_EQSL_QSL_SENT', 'Q'); + $this->db->or_where($this->config->item('table_name').'.COL_EQSL_QSL_SENT', 'N'); $this->db->group_end(); return $this->db->get(); From 050e950df5af23afb22e372ffc8ff91872efbfb9 Mon Sep 17 00:00:00 2001 From: phl0 Date: Thu, 9 Mar 2023 22:57:28 +0100 Subject: [PATCH 07/41] Add clickable API URL to API keys page --- application/views/api/help.php | 1 + application/views/interface_assets/footer.php | 10 ++++++++++ assets/css/general.css | 6 +++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/application/views/api/help.php b/application/views/api/help.php index 5ec2aa9e5..c1caf1c8f 100644 --- a/application/views/api/help.php +++ b/application/views/api/help.php @@ -16,6 +16,7 @@

The Cloudlog API (Application Programming Interface) lets third party systems access Cloudlog in a controlled way. Access to the API is managed via API keys.

You will need to generate an API key for each tool you wish to use (e.g. CloudlogCAT). Generate a read-write key if the application needs to send data to Cloudlog. Generate a read-only key if the application only needs to obtain data from Cloudlog.

+

API URL The API URL for this Cloudlog instance is:

Info It's good practice to delete a key if you are no longer using the associated application.

num_rows() > 0) { ?> diff --git a/application/views/interface_assets/footer.php b/application/views/interface_assets/footer.php index c66bc8ada..ad2126cbe 100644 --- a/application/views/interface_assets/footer.php +++ b/application/views/interface_assets/footer.php @@ -158,6 +158,16 @@ function copyApiKey(apiKey) { }); } +function copyApiUrl() { + var apiUrlField = $('#apiUrl'); + navigator.clipboard.writeText("").then(function() { + }); + apiUrlField.addClass('flash-copy') + .delay('1000').queue(function() { + apiUrlField.removeClass('flash-copy').dequeue(); + }); +} + $(function () { $('[data-toggle="tooltip"]').tooltip({'delay': { show: 500, hide: 0 }, 'placement': 'right'}); }); diff --git a/assets/css/general.css b/assets/css/general.css index 6d5b732c7..ffdeabb71 100644 --- a/assets/css/general.css +++ b/assets/css/general.css @@ -405,6 +405,10 @@ div#station_logbooks_linked_table_paginate { } } +.api-url { + font-family: Monospace; +} + .api-key { font-family: Monospace; } @@ -415,4 +419,4 @@ div#station_logbooks_linked_table_paginate { .qso_panel .dxccsummary { margin-bottom: 10px; -} \ No newline at end of file +} From 5ac115ceee15782effb9147e709442696783f030 Mon Sep 17 00:00:00 2001 From: Andreas <6977712+AndreasK79@users.noreply.github.com> Date: Wed, 15 Mar 2023 14:40:32 +0100 Subject: [PATCH 08/41] [Migrations] Fix in script 102 and 105 to check if things exists --- .../migrations/102_add_version_two_trigger_to_options.php | 7 +++++-- application/migrations/105_create_dxcc_master_tables.php | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/application/migrations/102_add_version_two_trigger_to_options.php b/application/migrations/102_add_version_two_trigger_to_options.php index 3b5307d97..e0dac7383 100644 --- a/application/migrations/102_add_version_two_trigger_to_options.php +++ b/application/migrations/102_add_version_two_trigger_to_options.php @@ -13,9 +13,12 @@ public function up() { $data = array( array('option_name' => "version2_trigger", 'option_value' => "false", 'autoload' => "yes"), - ); + ); - $this->db->insert_batch('options', $data); + $query = $this->db->select('option_name')->where('option_name', 'version2_trigger')->get('options'); + if($query->num_rows() == 0) { + $this->db->insert_batch('options', $data); + } } public function down() diff --git a/application/migrations/105_create_dxcc_master_tables.php b/application/migrations/105_create_dxcc_master_tables.php index 58aae898b..f66a9f0ab 100644 --- a/application/migrations/105_create_dxcc_master_tables.php +++ b/application/migrations/105_create_dxcc_master_tables.php @@ -4,6 +4,7 @@ class Migration_create_dxcc_master_tables extends CI_Migration { public function up() { + if (!$this->db->table_exists('dxcc_master')) { $this->db->query("CREATE TABLE `dxcc_master` ( `DXCCPrefix` varchar(6) DEFAULT NULL, `DXCCSearch` varchar(6) DEFAULT NULL, @@ -1041,6 +1042,7 @@ public function up() $this->db->query("INSERT INTO `dxcc_master` (DXCCPrefix,DXCCSearch,DXCCMap,DXCCSort,CountryCode,PrefixList,DXCCName,Location,Continent,CQZone,ITUZone,IOTA,TimeZone,Latitude,Longitude,StartDate,EndDate) VALUES ('YB','YB','YB',17,327,'YB9[L-P],YB9Z[L-P],YC9[L-P],YC9Z[L-P],YD9[L-P],YD9Z[L-P],YE9[L-P],YE9Z[L-P],YF9[L-P],YF9Z[L-P],YG9[L-P],YG9Z[L-P],YH9[L-P],YH9Z[L-P]','Indonesia','East Nusatenggara','OC','28','54',NULL,-8.0,-8.67,121.5,NULL,NULL);"); $this->db->query("INSERT INTO `dxcc_master` (DXCCPrefix,DXCCSearch,DXCCMap,DXCCSort,CountryCode,PrefixList,DXCCName,Location,Continent,CQZone,ITUZone,IOTA,TimeZone,Latitude,Longitude,StartDate,EndDate) VALUES ('CE9','CE9','CE9',29,13,'RI1ANZ','Antarctica','Progress Station (Russia)','AN','39','69','AN-016',-6.0,-69.397,76.373,NULL,NULL);"); $this->db->query("INSERT INTO `dxcc_master` (DXCCPrefix,DXCCSearch,DXCCMap,DXCCSort,CountryCode,PrefixList,DXCCName,Location,Continent,CQZone,ITUZone,IOTA,TimeZone,Latitude,Longitude,StartDate,EndDate) VALUES ('UA0','UA0','UA',1,15,'UA0','Asiatic Russia','Asiatic Russia','AS',NULL,NULL,NULL,NULL,64.0,130.0,NULL,NULL);"); + } } public function down(){ From 177a7f32667806969f0e113e2654a25e2b5c74d6 Mon Sep 17 00:00:00 2001 From: Andreas <6977712+AndreasK79@users.noreply.github.com> Date: Thu, 16 Mar 2023 11:01:08 +0100 Subject: [PATCH 09/41] [CQMap] Changed font size css to em (better for scalability) --- assets/js/sections/cqmap.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/js/sections/cqmap.js b/assets/js/sections/cqmap.js index d374f71b7..f4d4f912f 100644 --- a/assets/js/sections/cqmap.js +++ b/assets/js/sections/cqmap.js @@ -151,9 +151,9 @@ function load_cq_map2(data) { color: mapColor, strokeOpacity: 0.3, strokeWeight: 2, - }).addTo(map); + }).addTo(map); - var title = '' + (Number(i)+Number(1)) + ''; + var title = '' + (Number(i)+Number(1)) + ''; var myIcon = L.divIcon({className: 'my-div-icon', html: title}); L.marker( @@ -162,7 +162,7 @@ function load_cq_map2(data) { title: (Number(i)+Number(1)), zIndex: 1000, } - ).addTo(map).on('click', onClick); + ).addTo(map).on('click', onClick); } /*Legend specific*/ From c962a972a808fd95c504acb5df77775dda261081 Mon Sep 17 00:00:00 2001 From: Hugo Silva Date: Thu, 16 Mar 2023 18:58:48 +0000 Subject: [PATCH 10/41] Basic QO-100 Dx Club API integration --- application/config/migration.php | 2 +- .../migrations/115_add_webadif_api_export.php | 32 +++ application/models/Logbook_model.php | 190 +++++++++++++----- application/models/Stations.php | 24 ++- application/views/station_profile/create.php | 15 ++ application/views/station_profile/edit.php | 21 ++ 6 files changed, 222 insertions(+), 62 deletions(-) create mode 100644 application/migrations/115_add_webadif_api_export.php diff --git a/application/config/migration.php b/application/config/migration.php index 1c0ef7a12..9a64222f4 100644 --- a/application/config/migration.php +++ b/application/config/migration.php @@ -21,7 +21,7 @@ | be upgraded / downgraded to. | */ -$config['migration_version'] = 114; +$config['migration_version'] = 115; /* |-------------------------------------------------------------------------- diff --git a/application/migrations/115_add_webadif_api_export.php b/application/migrations/115_add_webadif_api_export.php new file mode 100644 index 000000000..cfbbcf5a9 --- /dev/null +++ b/application/migrations/115_add_webadif_api_export.php @@ -0,0 +1,32 @@ +dbforge->add_column('station_profile', $fields); + + $fields = array( + "webadif_upload_date datetime DEFAULT NULL", + "webadif_upload_status varchar(1) DEFAULT 'N'", + ); + $this->dbforge->add_column($this->config->item('table_name'), $fields); + + } + + public function down() + { + $this->dbforge->drop_column('station_profile', 'webadifapikey'); + $this->dbforge->drop_column('station_profile', 'webadifapiurl'); + $this->dbforge->drop_column('station_profile', 'webadifrealtime'); + $this->dbforge->drop_column($this->config->item('table_name'), 'webadif_upload_date'); + $this->dbforge->drop_column($this->config->item('table_name'), 'webadif_upload_status'); + } +} diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index eb1fa4b45..79b86b357 100755 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -481,6 +481,25 @@ function add_qso($data, $skipexport = false) { $this->mark_qrz_qsos_sent($last_id); } } + + $result = $this->exists_webadif_api_key($data['station_id']); + // Push qso to webadif if apikey is set, and realtime upload is enabled, and we're not importing an adif-file + if (isset($result->webadifapikey) && $result->webadifrealtime == 1) { + $CI =& get_instance(); + $CI->load->library('AdifHelper'); + $qso = $this->get_qso($last_id)->result(); + + $adif = $CI->adifhelper->getAdifLine($qso[0]); + $result = $this->push_qso_to_webadif( + $result->webadifapiurl, + $result->webadifapikey, + $adif + ); + + if ($result) { + $this->mark_webadif_qsos_sent($last_id); + } + } } } @@ -503,6 +522,25 @@ function exists_qrz_api_key($station_id) { } } + /* + * Function checks if a WebADIF API Key exists in the table with the given station id + */ + function exists_webadif_api_key($station_id) { + $sql = 'select webadifapikey, webadifapiurl, webadifrealtime from station_profile + where station_id = ' . $station_id; + + $query = $this->db->query($sql); + + $result = $query->row(); + + if ($result) { + return $result; + } + else { + return false; + } + } + /* * Function uploads a QSO to QRZ with the API given. * $adif contains a line with the QSO in the ADIF format. QSO ends with an @@ -545,6 +583,36 @@ function push_qso_to_qrz($apikey, $adif, $replaceoption = false) { curl_close($ch); } + /* + * Function uploads a QSO to WebADIF consumer with the API given. + * $adif contains a line with the QSO in the ADIF format. + */ + function push_qso_to_webadif($url, $apikey, $adif) : bool{ + + $headers = array( + 'Content-Type: text/plain', + 'X-API-Key: ' . $apikey + ); + + if (substr($url, -1) !== "/") { + $url .= "/"; + } + + $ch = curl_init( $url . "qso"); + curl_setopt( $ch, CURLOPT_POST, true); + curl_setopt( $ch, CURLOPT_POSTFIELDS, (string)$adif); + curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt( $ch, CURLOPT_HEADER, 0); + curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true); + + $content = curl_exec($ch); // TODO: better error handling + $errors = curl_error($ch); + $response = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + return $response === 200; + } + /* * Function marks QSOs as uploaded to QRZ. * $primarykey is the unique id for that QSO in the logbook @@ -562,6 +630,24 @@ function mark_qrz_qsos_sent($primarykey) { return true; } + /* + * Function marks QSOs as uploaded to WebADIF. + * $primarykey is the unique id for that QSO in the logbook + */ + function mark_webadif_qsos_sent($primarykey) + { + $data = array( + 'webadif_upload_date' => date("Y-m-d H:i:s", strtotime("now")), + 'webadif_upload_status' => 'Y', + ); + + $this->db->where('COL_PRIMARY_KEY', $primarykey); + + $this->db->update($this->config->item('table_name'), $data); + + return true; + } + function upload_amsat_status($data) { $sat_name = ''; if ($data['COL_SAT_NAME'] == 'AO-7') { @@ -1038,34 +1124,34 @@ function qso_info($id) { return $this->db->get($this->config->item('table_name')); } - - + + // Set Paper to received function paperqsl_update($qso_id, $method) { - + $data = array( 'COL_QSLRDATE' => date('Y-m-d H:i:s'), 'COL_QSL_RCVD' => 'Y', 'COL_QSL_RCVD_VIA' => $method ); - + $this->db->where('COL_PRIMARY_KEY', $qso_id); - + $this->db->update($this->config->item('table_name'), $data); } // Set Paper to sent function paperqsl_update_sent($qso_id, $method) { - + $data = array( 'COL_QSLSDATE' => date('Y-m-d H:i:s'), 'COL_QSL_SENT' => 'Y', 'COL_QSL_SENT_VIA' => $method ); - + $this->db->where('COL_PRIMARY_KEY', $qso_id); - + $this->db->update($this->config->item('table_name'), $data); } @@ -1219,10 +1305,10 @@ function get_last_qsos($num, $StationLocationsArray = null) { } else { $logbooks_locations_array = $StationLocationsArray; } - + if ($logbooks_locations_array) { $location_list = "'".implode("','",$logbooks_locations_array)."'"; - + $sql = "SELECT * FROM ( select * from " . $this->config->item('table_name'). " WHERE station_id IN(". $location_list .") order by col_time_on desc, col_primary_key desc @@ -1233,12 +1319,12 @@ function get_last_qsos($num, $StationLocationsArray = null) { order by col_time_on desc, col_primary_key desc"; $query = $this->db->query($sql); - + return $query; } else { return null; } - + } /* Get all QSOs with a valid grid for use in the KML export */ @@ -1390,7 +1476,7 @@ function todays_qsos($StationLocationsArray = null) { } else { return null; } - + } /* Return QSOs over a period of days */ @@ -1465,7 +1551,7 @@ function map_day($date) { // Return QSOs made during the current month function month_qsos($StationLocationsArray = null) { - if($StationLocationsArray == null) { + if($StationLocationsArray == null) { $CI =& get_instance(); $CI->load->model('logbooks_model'); $logbooks_locations_array = $CI->logbooks_model->list_logbook_relationships($this->session->userdata('active_station_logbook')); @@ -1617,17 +1703,17 @@ function total_continents($searchCriteria) { if ($searchCriteria['mode'] !== '') { $this->db->group_start(); - $this->db->where('COL_MODE', $searchCriteria['mode']); + $this->db->where('COL_MODE', $searchCriteria['mode']); $this->db->or_where('COL_SUBMODE', $searchCriteria['mode']); $this->db->group_end(); } if ($searchCriteria['band'] !== '') { if($searchCriteria['band'] != "SAT") { - $this->db->where('COL_BAND', $searchCriteria['band']); - $this->db->where('COL_PROP_MODE != "SAT"'); + $this->db->where('COL_BAND', $searchCriteria['band']); + $this->db->where('COL_PROP_MODE != "SAT"'); } else { - $this->db->where('COL_PROP_MODE', 'SAT'); + $this->db->where('COL_PROP_MODE', 'SAT'); } } @@ -1746,7 +1832,7 @@ function total_bands() { } function get_QSLStats($StationLocationsArray = null) { - + if($StationLocationsArray == null) { $CI =& get_instance(); $CI->load->model('logbooks_model'); @@ -1872,7 +1958,7 @@ function total_qsl_rcvd() { $query = $this->db->get($this->config->item('table_name')); $row = $query->row(); - + if($row == null) { return 0; } else { @@ -1995,7 +2081,7 @@ function total_countries() { $this->db->where('COL_COUNTRY !=', 'Invalid'); $this->db->where('COL_DXCC >', '0'); $query = $this->db->get($this->config->item('table_name')); - + return $query->num_rows(); } else { return 0; @@ -2028,7 +2114,7 @@ function total_countries_current($StationLocationsArray = null) { /* Return total number of countries confirmed with along with qsl types confirmed */ function total_countries_confirmed($StationLocationsArray = null) { - + if($StationLocationsArray == null) { $CI =& get_instance(); $CI->load->model('logbooks_model'); @@ -2040,7 +2126,7 @@ function total_countries_confirmed($StationLocationsArray = null) { if(!empty($logbooks_locations_array)) { $this->db->select('COUNT(DISTINCT COL_COUNTRY) as Countries_Worked, COUNT(DISTINCT IF(COL_QSL_RCVD = "Y", COL_COUNTRY, NULL)) as Countries_Worked_QSL, - COUNT(DISTINCT IF(COL_EQSL_QSL_RCVD = "Y", COL_COUNTRY, NULL)) as Countries_Worked_EQSL, + COUNT(DISTINCT IF(COL_EQSL_QSL_RCVD = "Y", COL_COUNTRY, NULL)) as Countries_Worked_EQSL, COUNT(DISTINCT IF(COL_LOTW_QSL_RCVD = "Y", COL_COUNTRY, NULL)) as Countries_Worked_LOTW'); $this->db->where_in('station_id', $logbooks_locations_array); $this->db->where('COL_COUNTRY !=', 'Invalid'); @@ -2346,7 +2432,7 @@ function eqsl_not_yet_sent() { $this->db->join($this->config->item('table_name'),'station_profile.station_id = '.$this->config->item('table_name').'.station_id'); $this->db->where("coalesce(station_profile.eqslqthnickname, '') <> ''"); $this->db->where($this->config->item('table_name').'.COL_CALL !=', ''); - $this->db->group_start(); + $this->db->group_start(); $this->db->where($this->config->item('table_name').'.COL_EQSL_QSL_SENT is null'); $this->db->or_where($this->config->item('table_name').'.COL_EQSL_QSL_SENT', ''); $this->db->or_where($this->config->item('table_name').'.COL_EQSL_QSL_SENT', 'R'); @@ -2858,11 +2944,11 @@ function import($record, $station_id = "0", $skipDuplicate = false, $markLotw = 'COL_WWFF_REF' => (!empty($record['wwff_ref'])) ? $record['wwff_ref'] : '', 'COL_POTA_REF' => (!empty($record['pota_ref'])) ? $record['pota_ref'] : '', 'COL_SRX' => (!empty($record['srx'])) ? (int)$record['srx'] : null, - //convert to integer to make sure no invalid entries are imported + //convert to integer to make sure no invalid entries are imported 'COL_SRX_STRING' => (!empty($record['srx_string'])) ? $record['srx_string'] : '', 'COL_STATE' => (!empty($record['state'])) ? strtoupper($record['state']) : '', 'COL_STATION_CALLSIGN' => (!empty($record['station_callsign'])) ? $record['station_callsign'] : '', - //convert to integer to make sure no invalid entries are imported + //convert to integer to make sure no invalid entries are imported 'COL_STX' => (!empty($record['stx'])) ? (int)$record['stx'] : null, 'COL_STX_STRING' => (!empty($record['stx_string'])) ? $record['stx_string'] : '', 'COL_SUBMODE' => $input_submode, @@ -2971,14 +3057,14 @@ public function check_dxcc_table($call, $date){ $callsign = $matches[3][0]; $suffix = $matches[5][0]; if ($prefix) { - $prefix = substr($prefix, 0, -1); # Remove the / at the end + $prefix = substr($prefix, 0, -1); # Remove the / at the end } if ($suffix) { $suffix = substr($suffix, 1); # Remove the / at the beginning }; if (preg_match($csadditions, $suffix)) { if ($prefix) { - $call = $prefix; + $call = $prefix; } else { $call = $callsign; } @@ -3052,7 +3138,7 @@ public function dxcc_lookup($call, $date){ $call = "3D2/C"; # will match with Conway } elseif (preg_match('/(^LZ\/)|(\/LZ[1-9]?$)/', $call)) { # LZ/ is LZ0 by DXCC but this is VP8h $call = "LZ"; - } elseif (preg_match('/(^KG4)[A-Z09]{2}/', $call)) { + } elseif (preg_match('/(^KG4)[A-Z09]{2}/', $call)) { $call = "KG4"; } elseif (preg_match('/(^KG4)[A-Z09]{1}/', $call)) { $call = "K"; @@ -3062,14 +3148,14 @@ public function dxcc_lookup($call, $date){ $callsign = $matches[3][0]; $suffix = $matches[5][0]; if ($prefix) { - $prefix = substr($prefix, 0, -1); # Remove the / at the end + $prefix = substr($prefix, 0, -1); # Remove the / at the end } if ($suffix) { $suffix = substr($suffix, 1); # Remove the / at the beginning }; if (preg_match($csadditions, $suffix)) { if ($prefix) { - $call = $prefix; + $call = $prefix; } else { $call = $callsign; } @@ -3120,38 +3206,38 @@ function wpx($testcall, $i) { $a = ''; $b = ''; $c = ''; - + $lidadditions = '/^QRP$|^LGT$/'; $csadditions = '/^P$|^R$|^A$|^M$|^LH$/'; $noneadditions = '/^MM$|^AM$/'; - + # First check if the call is in the proper format, A/B/C where A and C # are optional (prefix of guest country and P, MM, AM etc) and B is the # callsign. Only letters, figures and "/" is accepted, no further check if the # callsign "makes sense". # 23.Apr.06: Added another "/X" to the regex, for calls like RV0AL/0/P # as used by RDA-DXpeditions.... - + if (preg_match_all('/^((\d|[A-Z])+\/)?((\d|[A-Z]){3,})(\/(\d|[A-Z])+)?(\/(\d|[A-Z])+)?$/', $testcall, $matches)) { - + # Now $1 holds A (incl /), $3 holds the callsign B and $5 has C - # We save them to $a, $b and $c respectively to ensure they won't get + # We save them to $a, $b and $c respectively to ensure they won't get # lost in further Regex evaluations. $a = $matches[1][0]; $b = $matches[3][0]; $c = $matches[5][0]; - + if ($a) { - $a = substr($a, 0, -1); # Remove the / at the end + $a = substr($a, 0, -1); # Remove the / at the end } if ($c) { $c = substr($c, 1); # Remove the / at the beginning }; - + # In some cases when there is no part A but B and C, and C is longer than 2 # letters, it happens that $a and $b get the values that $b and $c should # have. This often happens with liddish callsign-additions like /QRP and - # /LGT, but also with calls like DJ1YFK/KP5. ~/.yfklog has a line called + # /LGT, but also with calls like DJ1YFK/KP5. ~/.yfklog has a line called # "lidadditions", which has QRP and LGT as defaults. This sorts out half of # the problem, but not calls like DJ1YFK/KH5. This is tested in a second # try: $a looks like a call (.\d[A-Z]) and $b doesn't (.\d), they are @@ -3167,32 +3253,32 @@ function wpx($testcall, $i) { $a = $temp; } } - + # *** Added later *** The check didn't make sure that the callsign # contains a letter. there are letter-only callsigns like RAEM, but not - # figure-only calls. - + # figure-only calls. + if (preg_match('/^[0-9]+$/', $b)) { # Callsign only consists of numbers. Bad! return null; # exit, undef } - + # Depending on these values we have to determine the prefix. # Following cases are possible: # # 1. $a and $c undef --> only callsign, subcases # 1.1 $b contains a number -> everything from start to number - # 1.2 $b contains no number -> first two letters plus 0 + # 1.2 $b contains no number -> first two letters plus 0 # 2. $a undef, subcases: # 2.1 $c is only a number -> $a with changed number - # 2.2 $c is /P,/M,/MM,/AM -> 1. + # 2.2 $c is /P,/M,/MM,/AM -> 1. # 2.3 $c is something else and will be interpreted as a Prefix - # 3. $a is defined, will be taken as PFX, regardless of $c - + # 3. $a is defined, will be taken as PFX, regardless of $c + if (($a == null) && ($c == null)) { # Case 1 if (preg_match('/\d/', $b)) { # Case 1.1, contains number preg_match('/(.+\d)[A-Z]*/', $b, $matches); # Prefix is all but the last $prefix = $matches[1]; # Letters - } else { # Case 1.2, no number + } else { # Case 1.2, no number $prefix = substr($b, 0, 2) . "0"; # first two + 0 } } elseif (($a == null) && (isset($c))) { # Case 2, CALL/X @@ -3204,12 +3290,12 @@ function wpx($testcall, $i) { # like N66A/7 -> N7 this brings the wrong result of N67, but I # think that's rather irrelevant cos such calls rarely appear # and if they do, it's very unlikely for them to have a number - # attached. You can still edit it by hand anyway.. + # attached. You can still edit it by hand anyway.. if (preg_match('/^([A-Z]\d)\d$/', $matches[1])) { # e.g. A45 $c = 0 $prefix = $matches[1] . $c; # -> A40 } else { # Otherwise cut all numbers preg_match('/(.*[A-Z])\d+/', $matches[1], $match); # Prefix w/o number in $1 - $prefix = $match[1] . $c; # Add attached number + $prefix = $match[1] . $c; # Add attached number } } elseif (preg_match($csadditions, $c)) { preg_match('/(.+\d)[A-Z]*/', $b, $matches); # Known attachment -> like Case 1.1 @@ -3241,7 +3327,7 @@ function wpx($testcall, $i) { # case, the superfluous part will be cropped. Since this, however, changes the # DXCC of the prefix, this will NOT happen when invoked from with an # extra parameter $_[1]; this will happen when invoking it from &dxcc. - + if (preg_match('/(\w+\d)[A-Z]+\d/', $prefix, $matches) && $i == null) { $prefix = $matches[1][0]; } diff --git a/application/models/Stations.php b/application/models/Stations.php index 1d6b9a967..5abab0c70 100644 --- a/application/models/Stations.php +++ b/application/models/Stations.php @@ -81,10 +81,13 @@ function add() { 'oqrs' => xss_clean($this->input->post('oqrs', true)), 'oqrs_email' => xss_clean($this->input->post('oqrsemail', true)), 'oqrs_text' => xss_clean($this->input->post('oqrstext', true)), + 'webadifapikey' => xss_clean($this->input->post('webadifapikey', true)), + 'webadifapiurl' => 'https://qo100dx.club/api', + 'webadifrealtime' => xss_clean($this->input->post('webadifrealtime', true)), ); // Insert Records - $this->db->insert('station_profile', $data); + $this->db->insert('station_profile', $data); } function edit() { @@ -111,11 +114,14 @@ function edit() { 'oqrs' => xss_clean($this->input->post('oqrs', true)), 'oqrs_email' => xss_clean($this->input->post('oqrsemail', true)), 'oqrs_text' => xss_clean($this->input->post('oqrstext', true)), + 'webadifapikey' => xss_clean($this->input->post('webadifapikey', true)), + 'webadifapiurl' => 'https://qo100dx.club/api', + 'webadifrealtime' => xss_clean($this->input->post('webadifrealtime', true)), ); $this->db->where('user_id', $this->session->userdata('user_id')); $this->db->where('station_id', xss_clean($this->input->post('station_id', true))); - $this->db->update('station_profile', $data); + $this->db->update('station_profile', $data); } function delete($id) { @@ -132,7 +138,7 @@ function delete($id) { $this->db->delete($this->config->item('table_name')); // Delete Station Profile - $this->db->delete('station_profile', array('station_id' => $clean_id)); + $this->db->delete('station_profile', array('station_id' => $clean_id)); } function deletelog($id) { @@ -145,7 +151,7 @@ function claim_user($id) { $data = array( 'user_id' => $this->session->userdata('user_id'), ); - + $this->db->where('station_id', $id); $this->db->update('station_profile', $data); } @@ -160,7 +166,7 @@ function ClaimAllStationLocations($id = NULL) { $data = array( 'user_id' => $id, ); - + $this->db->update('station_profile', $data); } @@ -192,7 +198,7 @@ function set_active($current, $new) { $this->db->where('user_id', $this->session->userdata('user_id')); $this->db->update('station_profile', $current_default); - // Deselect current default + // Deselect current default $newdefault = array( 'station_active' => 1, ); @@ -215,7 +221,7 @@ public function find_active() { return "0"; } } - + public function find_gridsquare() { $this->db->where('user_id', $this->session->userdata('user_id')); $this->db->where('station_active', 1); @@ -262,7 +268,7 @@ public function reassign($id) { ); $this->db->where('COL_STATION_CALLSIGN', $row->station_callsign); - + if($row->station_iota != "") { $this->db->where('COL_MY_IOTA', $row->station_iota); } @@ -307,7 +313,7 @@ function profile_exists() { return 1; } else { return 0; - } + } } function stations_with_qrz_api_key() { diff --git a/application/views/station_profile/create.php b/application/views/station_profile/create.php index 59bee15d0..6e4fcd65a 100644 --- a/application/views/station_profile/create.php +++ b/application/views/station_profile/create.php @@ -241,6 +241,21 @@
+
+
+ + + Create your API key on your QO-100 Dx Club's profile page +
+
+ + +
+
+
webadifapikey; } ?>"> + Create your API key on your QO-100 Dx Club's profile page +
+
+ + +
+
+
+ +
From a745cbb6e8ca8722ba08fc0cbaff4719d1a04782 Mon Sep 17 00:00:00 2001 From: Hugo Silva Date: Fri, 17 Mar 2023 08:28:08 +0000 Subject: [PATCH 11/41] Check if columns exist before adding them in migration --- .../migrations/115_add_webadif_api_export.php | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/application/migrations/115_add_webadif_api_export.php b/application/migrations/115_add_webadif_api_export.php index cfbbcf5a9..7997c83bd 100644 --- a/application/migrations/115_add_webadif_api_export.php +++ b/application/migrations/115_add_webadif_api_export.php @@ -6,20 +6,40 @@ class Migration_add_webadif_api_export extends CI_Migration { public function up() { - $fields = array( - 'webadifapikey varchar(50) DEFAULT NULL', - 'webadifapiurl varchar(256) DEFAULT NULL', - 'webadifrealtime bool DEFAULT FALSE', - ); - $this->dbforge->add_column('station_profile', $fields); + if (!$this->db->field_exists('webadifapikey', 'station_profile')) { + $fields = array( + 'webadifapikey varchar(50) DEFAULT NULL' + ); + $this->dbforge->add_column('station_profile', $fields); + } + if (!$this->db->field_exists('webadifapiurl', 'station_profile')) { + $fields = array( + 'webadifapiurl varchar(256) DEFAULT NULL' + ); + $this->dbforge->add_column('station_profile', $fields); + } + if (!$this->db->field_exists('webadifrealtime', 'station_profile')) { + $fields = array( + 'webadifrealtime bool DEFAULT FALSE' + ); + $this->dbforge->add_column('station_profile', $fields); + } - $fields = array( - "webadif_upload_date datetime DEFAULT NULL", - "webadif_upload_status varchar(1) DEFAULT 'N'", - ); - $this->dbforge->add_column($this->config->item('table_name'), $fields); + if (!$this->db->field_exists('webadif_upload_date', $this->config->item('table_name'))) { + $fields = array( + "webadif_upload_date datetime DEFAULT NULL" + ); + $this->dbforge->add_column($this->config->item('table_name'), $fields); + } - } + if (!$this->db->field_exists('webadif_upload_status', $this->config->item('table_name'))) { + $fields = array( + "webadif_upload_status varchar(1) DEFAULT 'N'" + ); + $this->dbforge->add_column($this->config->item('table_name'), $fields); + } + + } public function down() { From 179100a357279cecc6c68ba46a35577d549440e3 Mon Sep 17 00:00:00 2001 From: Michael Urspringer Date: Fri, 17 Mar 2023 11:12:22 +0100 Subject: [PATCH 12/41] Fix issue #100 Fix for https://github.com/m0urs/Cloudlog/issues/100 --- .htaccess.sample | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.htaccess.sample b/.htaccess.sample index 57dfd4433..ca6b92156 100644 --- a/.htaccess.sample +++ b/.htaccess.sample @@ -3,6 +3,10 @@ # $config['index_page'] = ''; RewriteEngine On + +RewriteCond %{REQUEST_URI} ^/backup/$ +RewriteRule ^(.*)$ /index.php?/$1 [L] + RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d -RewriteRule ^(.*)$ /index.php?/$1 [L] \ No newline at end of file +RewriteRule ^(.*)$ /index.php?/$1 [L] From d7d06c2bbc6d76912d38c73afff43c2aefdab40d Mon Sep 17 00:00:00 2001 From: Hugo Silva Date: Fri, 17 Mar 2023 13:11:04 +0000 Subject: [PATCH 13/41] Store webadif upload data in separate table --- .../migrations/115_add_webadif_api_export.php | 32 +++++++++++-------- application/models/Logbook_model.php | 8 ++--- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/application/migrations/115_add_webadif_api_export.php b/application/migrations/115_add_webadif_api_export.php index 7997c83bd..22e29007f 100644 --- a/application/migrations/115_add_webadif_api_export.php +++ b/application/migrations/115_add_webadif_api_export.php @@ -25,18 +25,25 @@ public function up() $this->dbforge->add_column('station_profile', $fields); } - if (!$this->db->field_exists('webadif_upload_date', $this->config->item('table_name'))) { - $fields = array( - "webadif_upload_date datetime DEFAULT NULL" - ); - $this->dbforge->add_column($this->config->item('table_name'), $fields); - } - if (!$this->db->field_exists('webadif_upload_status', $this->config->item('table_name'))) { - $fields = array( - "webadif_upload_status varchar(1) DEFAULT 'N'" - ); - $this->dbforge->add_column($this->config->item('table_name'), $fields); + if (!$this->db->table_exists('webadif')) { + $this->dbforge->add_field(array( + 'id' => array( + 'type' => 'INT', + 'auto_increment' => TRUE + ), + 'qso_id' => array( + 'type' => 'int', + ), + 'upload_date' => array( + 'type' => 'datetime', + ), + )); + + $this->dbforge->add_key('id', TRUE); + $this->dbforge->add_key(array('qso_id','upload_date'), FALSE); + + $this->dbforge->create_table('webadif'); } } @@ -46,7 +53,6 @@ public function down() $this->dbforge->drop_column('station_profile', 'webadifapikey'); $this->dbforge->drop_column('station_profile', 'webadifapiurl'); $this->dbforge->drop_column('station_profile', 'webadifrealtime'); - $this->dbforge->drop_column($this->config->item('table_name'), 'webadif_upload_date'); - $this->dbforge->drop_column($this->config->item('table_name'), 'webadif_upload_status'); + $this->dbforge->drop_table('webadif'); } } diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index 79b86b357..53299b8ae 100755 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -637,13 +637,11 @@ function mark_qrz_qsos_sent($primarykey) { function mark_webadif_qsos_sent($primarykey) { $data = array( - 'webadif_upload_date' => date("Y-m-d H:i:s", strtotime("now")), - 'webadif_upload_status' => 'Y', + 'upload_date' => date("Y-m-d H:i:s", strtotime("now")), + 'qso_id' => $primarykey, ); - $this->db->where('COL_PRIMARY_KEY', $primarykey); - - $this->db->update($this->config->item('table_name'), $data); + $this->db->insert('webadif', $data); return true; } From e72307c19414c197f78130d33c48569c27214fe6 Mon Sep 17 00:00:00 2001 From: phl0 Date: Fri, 17 Mar 2023 17:06:44 +0100 Subject: [PATCH 14/41] Fix frequency for CAS-4B --- assets/json/satellite_data.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/json/satellite_data.json b/assets/json/satellite_data.json index f074ef0af..8f21974b6 100644 --- a/assets/json/satellite_data.json +++ b/assets/json/satellite_data.json @@ -136,7 +136,7 @@ "U/V":[ { "Uplink_Mode":"LSB", - "Uplink_Freq":"4352800000", + "Uplink_Freq":"435280000", "Downlink_Mode":"USB", "Downlink_Freq":"145925000" } From cbf6efd19fd2c8612bc97ee1351474f678acda71 Mon Sep 17 00:00:00 2001 From: Hugo Silva Date: Fri, 17 Mar 2023 17:54:35 +0000 Subject: [PATCH 15/41] Adds export to QO-100 Dx Club menu entry and functionality --- application/controllers/Webadif.php | 156 ++++++++++++++++++ application/models/Logbook_model.php | 39 +++++ application/models/Stations.php | 29 ++++ application/views/interface_assets/footer.php | 35 ++-- application/views/interface_assets/header.php | 6 +- application/views/webadif/export.php | 93 +++++++++++ application/views/webadif/mark_webadif.php | 22 +++ assets/js/sections/webadif.js | 70 ++++++++ 8 files changed, 432 insertions(+), 18 deletions(-) create mode 100644 application/controllers/Webadif.php create mode 100644 application/views/webadif/export.php create mode 100644 application/views/webadif/mark_webadif.php create mode 100644 assets/js/sections/webadif.js diff --git a/application/controllers/Webadif.php b/application/controllers/Webadif.php new file mode 100644 index 000000000..608b67d66 --- /dev/null +++ b/application/controllers/Webadif.php @@ -0,0 +1,156 @@ +setOptions(); + + $this->load->model('logbook_model'); + + $station_ids = $this->logbook_model->get_station_id_with_webadif_api(); + + if ($station_ids) { + foreach ($station_ids as $station) { + $webadif_api_key = $station->webadifapikey; + $webadif_api_url = $station->webadifapiurl; + if ($this->mass_upload_qsos($station->station_id, $webadif_api_key, $webadif_api_url)) { + echo "QSOs have been uploaded to QO-100 Dx Club."; + log_message('info', 'QSOs have been uploaded to QO-100 Dx Club.'); + } else { + echo "No QSOs found for upload."; + log_message('info', 'No QSOs found for upload.'); + } + } + } else { + echo "No station profiles with a QO-100 Dx Club API Key found."; + log_message('error', "No station profiles with a QO-100 Dx Club API Key found."); + } + } + + function setOptions() { + $this->config->load('config'); + ini_set('memory_limit', '-1'); + ini_set('display_errors', 1); + ini_set('display_startup_errors', 1); + error_reporting(E_ALL); + } + + /* + * Function gets all QSOs from given station_id, that are not previously uploaded to webADIF consumer. + * Adif is build for each qso, and then uploaded, one at a time + */ + function mass_upload_qsos($station_id, $webadif_api_key, $webadif_api_url) { + $i = 0; + $data['qsos'] = $this->logbook_model->get_webadif_qsos($station_id); + $errormessages=array(); + + $CI =& get_instance(); + $CI->load->library('AdifHelper'); + + if ($data['qsos']) { + foreach ($data['qsos']->result() as $qso) { + $adif = $CI->adifhelper->getAdifLine($qso); + $result = $this->logbook_model->push_qso_to_webadif($webadif_api_url, $webadif_api_key, $adif); + + if ($result) { + $this->logbook_model->mark_webadif_qsos_sent($qso->COL_PRIMARY_KEY); + $i++; + } else { + $errorMessage = 'QO-100 Dx Club upload failed for qso: Call: ' . $qso->COL_CALL . ' Band: ' . $qso->COL_BAND . ' Mode: ' . $qso->COL_MODE . ' Time: ' . $qso->COL_TIME_ON; + log_message('error', $errorMessage); + $errormessages[] = $errorMessage; + } + } + $result=[]; + $result['status'] = 'OK'; + $result['count'] = $i; + $result['errormessages'] = $errormessages; + return $result; + } else { + $result=[]; + $result['status'] = 'Error'; + $result['count'] = $i; + $result['errormessages'] = $errormessages; + return $result; + } + } + + /* + * Used for displaying the uid for manually selecting log for upload to webADIF consumer + */ + public function export() { + $this->load->model('stations'); + + $data['page_title'] = "QO-100 Dx Club Upload"; + + $data['station_profiles'] = $this->stations->all_of_user(); + $data['station_profile'] = $this->stations->stations_with_webadif_api_key(); + + $this->load->view('interface_assets/header', $data); + $this->load->view('webadif/export'); + $this->load->view('interface_assets/footer'); + } + + /* + * Used for ajax-function when selecting log for upload to webADIF consumer + */ + public function upload_station() { + $this->setOptions(); + $this->load->model('stations'); + + $postData = $this->input->post(); + + $this->load->model('logbook_model'); + $result = $this->logbook_model->exists_webadif_api_key($postData['station_id']); + $webadif_api_key = $result->webadifapikey; + $webadif_api_url = $result->webadifapiurl; + header('Content-type: application/json'); + $result = $this->mass_upload_qsos($postData['station_id'], $webadif_api_key, $webadif_api_url); + if ($result['status'] == 'OK') { + $stationinfo = $this->stations->stations_with_webadif_api_key(); + $info = $stationinfo->result(); + + $data['status'] = 'OK'; + $data['info'] = $info; + $data['infomessage'] = $result['count'] . " QSOs are now uploaded to QO-100 Dx Club"; + $data['errormessages'] = $result['errormessages']; + echo json_encode($data); + } else { + $data['status'] = 'Error'; + $data['info'] = 'Error: No QSOs found to upload.'; + $data['errormessages'] = $result['errormessages']; + echo json_encode($data); + } + } + + public function mark_webadif() { + // Set memory limit to unlimited to allow heavy usage + ini_set('memory_limit', '-1'); + $data['page_title'] = "QO-100 Dx Club Upload"; + + $station_id = $this->security->xss_clean($this->input->post('station_profile')); + + $this->load->model('adif_data'); + + $data['qsos'] = $this->adif_data->export_custom($this->input->post('from'), $this->input->post('to'), $station_id); + + $this->load->model('logbook_model'); + if ($data['qsos']!==null) { + foreach ($data['qsos']->result() as $qso) { + $this->logbook_model->mark_webadif_qsos_sent($qso->COL_PRIMARY_KEY); + } + } + + $this->load->view('interface_assets/header', $data); + $this->load->view('webadif/mark_webadif', $data); + $this->load->view('interface_assets/footer'); + } +} diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index 53299b8ae..f43659224 100755 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -1275,6 +1275,25 @@ function get_qrz_qsos($station_id){ return $query; } + /* + * Function returns the QSOs from the logbook, which have not been either marked as uploaded to webADIF + */ + function get_webadif_qsos($station_id){ + $sql = " + SELECT qsos.*, station_profile.* FROM %s qsos + INNER JOIN station_profile ON qsos.station_id = station_profile.station_id + LEFT JOIN webadif ON qsos.COL_PRIMARY_KEY = webadif.qso_id + WHERE qsos.station_id = %d + AND webadif.upload_date IS NULL + "; + $sql = sprintf( + $sql, + $this->config->item('table_name'), + $station_id + ); + return $this->db->query($sql); + } + /* * Function returns all the station_id's with QRZ API Key's */ @@ -1294,6 +1313,26 @@ function get_station_id_with_qrz_api() { } } + /* + * Function returns all the station_id's with QRZ API Key's + */ + function get_station_id_with_webadif_api() { + $sql = " + SELECT station_id, webadifapikey, webadifapiurl + FROM station_profile + WHERE COALESCE(webadifapikey, '') <> '' + AND COALESCE(webadifapiurl, '') <> '' + "; + + $query = $this->db->query($sql); + $result = $query->result(); + if ($result) { + return $result; + } else { + return null; + } + } + function get_last_qsos($num, $StationLocationsArray = null) { if($StationLocationsArray == null) { diff --git a/application/models/Stations.php b/application/models/Stations.php index 5abab0c70..03a689da2 100644 --- a/application/models/Stations.php +++ b/application/models/Stations.php @@ -345,6 +345,35 @@ function stations_with_qrz_api_key() { return $query; } + function stations_with_webadif_api_key() { + $sql=" + SELECT station_profile.station_id, station_profile.station_profile_name, station_profile.station_callsign, notc.c notcount, totc.c totcount + FROM station_profile + INNER JOIN ( + SELECT qsos.station_id, COUNT(qsos.COL_PRIMARY_KEY) c + FROM %s qsos + LEFT JOIN webadif ON qsos.COL_PRIMARY_KEY = webadif.qso_id + WHERE webadif.qso_id IS NULL + GROUP BY qsos.station_id + ) notc + INNER JOIN ( + SELECT qsos.station_id, COUNT(qsos.COL_PRIMARY_KEY) c + FROM %s qsos + GROUP BY qsos.station_id + ) totc + WHERE COALESCE(station_profile.webadifapikey, '') <> '' + AND COALESCE(station_profile.webadifapiurl, '') <> '' + AND station_profile.user_id = %d + "; + $sql=sprintf( + $sql, + $this->config->item('table_name'), + $this->config->item('table_name'), + $this->session->userdata('user_id') + ); + return $this->db->query($sql); + } + /* * Function: are_eqsl_nicks_defined * Description: Returns number of station profiles with eqslnicknames diff --git a/application/views/interface_assets/footer.php b/application/views/interface_assets/footer.php index c66bc8ada..cba5abaa5 100644 --- a/application/views/interface_assets/footer.php +++ b/application/views/interface_assets/footer.php @@ -70,7 +70,7 @@ function load_was_map() { -uri->segment(1) == "adif" || $this->uri->segment(1) == "qrz") { ?> +uri->segment(1) == "adif" || $this->uri->segment(1) == "qrz" || $this->uri->segment(1) == "webadif") { ?> @@ -112,7 +112,7 @@ function getLocation() { console.log("'clicked"); if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(showPosition); - } else { + } else { console.log('Geolocation is not supported by this browser.'); } } @@ -363,7 +363,7 @@ function delete_stored_query(id) { $(".bootstrap-dialog-message").prepend('
×The stored query has been deleted!
'); $("#query_" + id).remove(); // removes query from table in dialog $("#querydropdown option[value='" + id + "']").remove(); // removes query from dropdown - if ($("#querydropdown option").length == 0) { + if ($("#querydropdown option").length == 0) { $("#btn-edit").remove(); $('.querydropdownform').remove(); }; @@ -549,7 +549,7 @@ function newpath(latlng1, latlng2, locator1, locator2) { var osmUrl='optionslib->get_option('option_map_tile_server');?>'; var osmAttrib='Map data © OpenStreetMap contributors'; - var osm = new L.TileLayer(osmUrl, {minZoom: 1, maxZoom: 9, attribution: osmAttrib}); + var osm = new L.TileLayer(osmUrl, {minZoom: 1, maxZoom: 9, attribution: osmAttrib}); var redIcon = L.icon({ iconUrl: icon_dot_url, @@ -602,7 +602,7 @@ function showActivatorsMap(call, count, grids) { var osmUrl='optionslib->get_option('option_map_tile_server');?>'; var osmAttrib='Map data © OpenStreetMap contributors'; - var osm = new L.TileLayer(osmUrl, {minZoom: 1, maxZoom: 9, attribution: osmAttrib}); + var osm = new L.TileLayer(osmUrl, {minZoom: 1, maxZoom: 9, attribution: osmAttrib}); map.addLayer(osm); } @@ -730,13 +730,13 @@ function showActivatorsMap(call, count, grids) { }); $(function () { - // hold onto the drop down menu + // hold onto the drop down menu var dropdownMenu; - // and when you show it, move it to the body + // and when you show it, move it to the body $(window).on('show.bs.dropdown', function (e) { - // grab the menu + // grab the menu dropdownMenu = $(e.target).find('.dropdown-menu'); // detach it and append it to the body @@ -753,7 +753,7 @@ function showActivatorsMap(call, count, grids) { }); }); - // and when you hide it, reattach the drop down, and hide it normally + // and when you hide it, reattach the drop down, and hide it normally $(window).on('hide.bs.dropdown', function (e) { $(e.target).append(dropdownMenu.detach()); dropdownMenu.hide(); @@ -882,7 +882,7 @@ function searchButtonPress(){ uri->segment(1) == "qso") { ?> -load->model('stations'); $active_station_id = $this->stations->find_active(); @@ -1581,6 +1581,9 @@ function onMapClick(event) { uri->segment(1) == "qrz") { ?> + uri->segment(1) == "webadif") { ?> + + -uri->segment(1) == "qsl") { +uri->segment(1) == "qsl") { // Get Date format if($this->session->userdata('user_date_format')) { // If Logged in and session exists @@ -2050,7 +2053,7 @@ function displayActivatorsContacts(call, band, leogeo) { case 'M d, Y': $usethisformat = 'MMM D, YYYY';break; case 'M d, y': $usethisformat = 'MMM D, YY';break; } - + ?> @@ -2066,8 +2069,8 @@ function displayActivatorsContacts(call, band, leogeo) { "scrollX": true, "order": [ 2, 'desc' ], }); - - + + diff --git a/application/views/interface_assets/header.php b/application/views/interface_assets/header.php index 4cb69d2d7..23c4fcf49 100644 --- a/application/views/interface_assets/header.php +++ b/application/views/interface_assets/header.php @@ -39,7 +39,7 @@ uri->segment(1) == "adif" || (isset($hasDatePicker) && $hasDatePicker)) { ?> - + '; } ?> @@ -214,7 +214,7 @@ session->userdata('user_id'); ?>" title="Account"> Account Station Logbooks - + Station Locations Bands @@ -256,6 +256,8 @@ QRZ Logbook + QO-100 Dx Club Upload + API Keys diff --git a/application/views/webadif/export.php b/application/views/webadif/export.php new file mode 100644 index 000000000..2c07640ef --- /dev/null +++ b/application/views/webadif/export.php @@ -0,0 +1,93 @@ + +
+ +

+ +
+
+ + +
+ +
+
+
+

Here you can see and upload all QSOs which have not been previously uploaded to QO-100 Dx Club.

+

You need to set a QO-100 Dx Club API key in your station profile. Only station profiles with an API Key are displayed.

+

WarningThis might take a while as QSO uploads are processed sequentially.

+ +result()) { + echo ' + + + + + + + + + + + '; + foreach ($station_profile->result() as $station) { // Fills the table with the data + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + echo '
Profile nameStation callsignTotal QSOs not uploadedTotal QSOs uploadedActions
' . $station->station_profile_name . '' . $station->station_callsign . '' . $station->notcount . '' . $station->totcount . '
'; + + } + else { + echo ''; + } + ?> + +
+
+ +
+ +

Warning If a date range is not selected then all QSOs will be marked!

+

From date:

+
+
+ +
+
+
+
+
+

To date:

+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
diff --git a/application/views/webadif/mark_webadif.php b/application/views/webadif/mark_webadif.php new file mode 100644 index 000000000..8cdd6c0da --- /dev/null +++ b/application/views/webadif/mark_webadif.php @@ -0,0 +1,22 @@ +
+
+ session->flashdata('message')) { ?> + +
+

session->flashdata('message'); ?>

+
+ + +
+
+ QSOs marked +
+
+

Yay, it's done!

+

The QSOs are marked as exported to QO-100 Dx Club.

+
+
+ + +
+ diff --git a/assets/js/sections/webadif.js b/assets/js/sections/webadif.js new file mode 100644 index 000000000..2687d11d1 --- /dev/null +++ b/assets/js/sections/webadif.js @@ -0,0 +1,70 @@ +$(function () { + $('#datetimepicker5').datetimepicker({ + format: 'DD/MM/YYYY', + }); +}); + +$(function () { + $('#datetimepicker6').datetimepicker({ + format: 'DD/MM/YYYY', + }); +}); + +$(document).ready(function(){ + $('#markWebAdifAsExported').click(function(e){ + let form = $(this).closest('form'); + let station = form.find('select[name=station_profile]'); + if (station.val() == 0) { + station.addClass('is-invalid'); + }else{ + form.submit(); + } + }) +}); + +function ExportWebADIF(station_id) { + if ($(".alert").length > 0) { + $(".alert").remove(); + } + if ($(".errormessages").length > 0) { + $(".errormessages").remove(); + } + $(".ld-ext-right").addClass('running'); + $(".ld-ext-right").prop('disabled', true); + + $.ajax({ + url: base_url + 'index.php/webadif/upload_station', + type: 'post', + data: {'station_id': station_id}, + success: function (data) { + $(".ld-ext-right").removeClass('running'); + $(".ld-ext-right").prop('disabled', false); + if (data.status == 'OK') { + $.each(data.info, function(index, value){ + $('#notcount'+value.station_id).html(value.notcount); + $('#totcount'+value.station_id).html(value.totcount); + }); + $(".card-body").append(''); + } + else { + $(".card-body").append(''); + } + + if (data.errormessages.length > 0) { + $(".card-body").append('' + + '

\n' + + ' \n' + + '

\n' + + '
\n' + + '
\n' + + '
\n' + + '
'); + $.each(data.errormessages, function(index, value) { + $(".errors").append('
  • ' + value); + }); + } + } + }); +} From 430990ab0cf9ce4cd5be3d43c92463a7b77124c5 Mon Sep 17 00:00:00 2001 From: Andreas <6977712+AndreasK79@users.noreply.github.com> Date: Sat, 18 Mar 2023 14:16:59 +0100 Subject: [PATCH 16/41] [CQ Map] Added full screen option --- application/views/interface_assets/footer.php | 1 + application/views/interface_assets/header.php | 1 + assets/js/leaflet/Control.FullScreen.css | 10 + assets/js/leaflet/Control.FullScreen.js | 345 ++++++++++++++++++ assets/js/leaflet/icon-fullscreen.svg | 1 + assets/js/sections/cqmap.js | 8 +- 6 files changed, 365 insertions(+), 1 deletion(-) create mode 100644 assets/js/leaflet/Control.FullScreen.css create mode 100644 assets/js/leaflet/Control.FullScreen.js create mode 100644 assets/js/leaflet/icon-fullscreen.svg diff --git a/application/views/interface_assets/footer.php b/application/views/interface_assets/footer.php index c66bc8ada..ade6e2dd0 100644 --- a/application/views/interface_assets/footer.php +++ b/application/views/interface_assets/footer.php @@ -16,6 +16,7 @@ + uri->segment(1) == "activators") { ?> diff --git a/application/views/interface_assets/header.php b/application/views/interface_assets/header.php index 4cb69d2d7..c3161feed 100644 --- a/application/views/interface_assets/header.php +++ b/application/views/interface_assets/header.php @@ -20,6 +20,7 @@ + uri->segment(1) == "search" && $this->uri->segment(2) == "filter") { ?> diff --git a/assets/js/leaflet/Control.FullScreen.css b/assets/js/leaflet/Control.FullScreen.css new file mode 100644 index 000000000..e07fea531 --- /dev/null +++ b/assets/js/leaflet/Control.FullScreen.css @@ -0,0 +1,10 @@ +.fullscreen-icon { background-image: url(icon-fullscreen.svg); background-size:26px 52px; } +.fullscreen-icon.leaflet-fullscreen-on { background-position:0 -26px; } +.leaflet-touch .fullscreen-icon { background-position: 2px 2px; } +.leaflet-touch .fullscreen-icon.leaflet-fullscreen-on { background-position: 2px -24px; } +/* one selector per rule as explained here : http://www.sitepoint.com/html5-full-screen-api/ */ +.leaflet-container:-webkit-full-screen { width: 100% !important; height: 100% !important; z-index: 99999; } +.leaflet-container:-ms-fullscreen { width: 100% !important; height: 100% !important; z-index: 99999; } +.leaflet-container:full-screen { width: 100% !important; height: 100% !important; z-index: 99999; } +.leaflet-container:fullscreen { width: 100% !important; height: 100% !important; z-index: 99999; } +.leaflet-pseudo-fullscreen { position: fixed !important; width: 100% !important; height: 100% !important; top: 0px !important; left: 0px !important; z-index: 99999; } \ No newline at end of file diff --git a/assets/js/leaflet/Control.FullScreen.js b/assets/js/leaflet/Control.FullScreen.js new file mode 100644 index 000000000..fe582e793 --- /dev/null +++ b/assets/js/leaflet/Control.FullScreen.js @@ -0,0 +1,345 @@ +/*! +* Based on package 'screenfull' +* v5.2.0 - 2021-11-03 +* (c) Sindre Sorhus; MIT License +* Added definition for using screenfull as an amd module +* Must be placed before the definition of leaflet.fullscreen +* as it is required by that +*/ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + define('screenfull', factory); + } else if (typeof module === 'object' && module.exports) { + module.exports.screenfull = factory(); + } else { + // Save 'screenfull' into global window variable + root.screenfull = factory(); + } +}(typeof self !== 'undefined' ? self : this, function () { + 'use strict'; + + var document = typeof window !== 'undefined' && typeof window.document !== 'undefined' ? window.document : {}; + + var fn = (function () { + var val; + + var fnMap = [ + [ + 'requestFullscreen', + 'exitFullscreen', + 'fullscreenElement', + 'fullscreenEnabled', + 'fullscreenchange', + 'fullscreenerror' + ], + // New WebKit + [ + 'webkitRequestFullscreen', + 'webkitExitFullscreen', + 'webkitFullscreenElement', + 'webkitFullscreenEnabled', + 'webkitfullscreenchange', + 'webkitfullscreenerror' + + ], + // Old WebKit + [ + 'webkitRequestFullScreen', + 'webkitCancelFullScreen', + 'webkitCurrentFullScreenElement', + 'webkitCancelFullScreen', + 'webkitfullscreenchange', + 'webkitfullscreenerror' + + ], + [ + 'mozRequestFullScreen', + 'mozCancelFullScreen', + 'mozFullScreenElement', + 'mozFullScreenEnabled', + 'mozfullscreenchange', + 'mozfullscreenerror' + ], + [ + 'msRequestFullscreen', + 'msExitFullscreen', + 'msFullscreenElement', + 'msFullscreenEnabled', + 'MSFullscreenChange', + 'MSFullscreenError' + ] + ]; + + var i = 0; + var l = fnMap.length; + var ret = {}; + + for (; i < l; i++) { + val = fnMap[i]; + if (val && val[1] in document) { + for (i = 0; i < val.length; i++) { + ret[fnMap[0][i]] = val[i]; + } + return ret; + } + } + + return false; + })(); + + var eventNameMap = { + change: fn.fullscreenchange, + error: fn.fullscreenerror + }; + + var screenfull = { + request: function (element, options) { + return new Promise(function (resolve, reject) { + var onFullScreenEntered = function () { + this.off('change', onFullScreenEntered); + resolve(); + }.bind(this); + + this.on('change', onFullScreenEntered); + + element = element || document.documentElement; + + var returnPromise = element[fn.requestFullscreen](options); + + if (returnPromise instanceof Promise) { + returnPromise.then(onFullScreenEntered).catch(reject); + } + }.bind(this)); + }, + exit: function () { + return new Promise(function (resolve, reject) { + if (!this.isFullscreen) { + resolve(); + return; + } + + var onFullScreenExit = function () { + this.off('change', onFullScreenExit); + resolve(); + }.bind(this); + + this.on('change', onFullScreenExit); + + var returnPromise = document[fn.exitFullscreen](); + + if (returnPromise instanceof Promise) { + returnPromise.then(onFullScreenExit).catch(reject); + } + }.bind(this)); + }, + toggle: function (element, options) { + return this.isFullscreen ? this.exit() : this.request(element, options); + }, + onchange: function (callback) { + this.on('change', callback); + }, + onerror: function (callback) { + this.on('error', callback); + }, + on: function (event, callback) { + var eventName = eventNameMap[event]; + if (eventName) { + document.addEventListener(eventName, callback, false); + } + }, + off: function (event, callback) { + var eventName = eventNameMap[event]; + if (eventName) { + document.removeEventListener(eventName, callback, false); + } + }, + raw: fn + }; + + if (!fn) { + return {isEnabled: false}; + } else { + Object.defineProperties(screenfull, { + isFullscreen: { + get: function () { + return Boolean(document[fn.fullscreenElement]); + } + }, + element: { + enumerable: true, + get: function () { + return document[fn.fullscreenElement]; + } + }, + isEnabled: { + enumerable: true, + get: function () { + // Coerce to boolean in case of old WebKit + return Boolean(document[fn.fullscreenEnabled]); + } + } + }); + return screenfull; + } +})); + +/*! +* leaflet.fullscreen +*/ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // define an AMD module that requires 'leaflet' and 'screenfull' + // and resolve to an object containing leaflet and screenfull + define('leafletFullScreen', ['leaflet', 'screenfull'], factory); + } else if (typeof module === 'object' && module.exports) { + // define a CommonJS module that requires 'leaflet' and 'screenfull' + module.exports = factory(require('leaflet'), require('screenfull')); + } else { + // Assume 'leaflet' and 'screenfull' are loaded into global variable already + factory(root.L, root.screenfull); + } +}(typeof self !== 'undefined' ? self : this, function (leaflet, screenfull) { + 'use strict'; + + leaflet.Control.FullScreen = leaflet.Control.extend({ + options: { + position: 'topleft', + title: 'Full Screen', + titleCancel: 'Exit Full Screen', + forceSeparateButton: false, + forcePseudoFullscreen: false, + fullscreenElement: false + }, + + _screenfull: screenfull, + + onAdd: function (map) { + var className = 'leaflet-control-zoom-fullscreen', container, content = ''; + + if (map.zoomControl && !this.options.forceSeparateButton) { + container = map.zoomControl._container; + } else { + container = leaflet.DomUtil.create('div', 'leaflet-bar'); + } + + if (this.options.content) { + content = this.options.content; + } else { + className += ' fullscreen-icon'; + } + + this._createButton(this.options.title, className, content, container, this.toggleFullScreen, this); + this._map.fullscreenControl = this; + + this._map.on('enterFullscreen exitFullscreen', this._toggleState, this); + + return container; + }, + + onRemove: function () { + leaflet.DomEvent + .off(this.link, 'click', leaflet.DomEvent.stop) + .off(this.link, 'click', this.toggleFullScreen, this); + + if (this._screenfull.isEnabled) { + leaflet.DomEvent + .off(this._container, this._screenfull.raw.fullscreenchange, leaflet.DomEvent.stop) + .off(this._container, this._screenfull.raw.fullscreenchange, this._handleFullscreenChange, this); + + leaflet.DomEvent + .off(document, this._screenfull.raw.fullscreenchange, leaflet.DomEvent.stop) + .off(document, this._screenfull.raw.fullscreenchange, this._handleFullscreenChange, this); + } + }, + + _createButton: function (title, className, content, container, fn, context) { + this.link = leaflet.DomUtil.create('a', className, container); + this.link.href = '#'; + this.link.title = title; + this.link.innerHTML = content; + + this.link.setAttribute('role', 'button'); + this.link.setAttribute('aria-label', title); + + L.DomEvent.disableClickPropagation(container); + + leaflet.DomEvent + .on(this.link, 'click', leaflet.DomEvent.stop) + .on(this.link, 'click', fn, context); + + if (this._screenfull.isEnabled) { + leaflet.DomEvent + .on(container, this._screenfull.raw.fullscreenchange, leaflet.DomEvent.stop) + .on(container, this._screenfull.raw.fullscreenchange, this._handleFullscreenChange, context); + + leaflet.DomEvent + .on(document, this._screenfull.raw.fullscreenchange, leaflet.DomEvent.stop) + .on(document, this._screenfull.raw.fullscreenchange, this._handleFullscreenChange, context); + } + + return this.link; + }, + + toggleFullScreen: function () { + var map = this._map; + map._exitFired = false; + if (map._isFullscreen) { + if (this._screenfull.isEnabled && !this.options.forcePseudoFullscreen) { + this._screenfull.exit(); + } else { + leaflet.DomUtil.removeClass(this.options.fullscreenElement ? this.options.fullscreenElement : map._container, 'leaflet-pseudo-fullscreen'); + map.invalidateSize(); + } + map.fire('exitFullscreen'); + map._exitFired = true; + map._isFullscreen = false; + } + else { + if (this._screenfull.isEnabled && !this.options.forcePseudoFullscreen) { + this._screenfull.request(this.options.fullscreenElement ? this.options.fullscreenElement : map._container); + } else { + leaflet.DomUtil.addClass(this.options.fullscreenElement ? this.options.fullscreenElement : map._container, 'leaflet-pseudo-fullscreen'); + map.invalidateSize(); + } + map.fire('enterFullscreen'); + map._isFullscreen = true; + } + }, + + _toggleState: function () { + this.link.title = this._map._isFullscreen ? this.options.title : this.options.titleCancel; + this._map._isFullscreen ? L.DomUtil.removeClass(this.link, 'leaflet-fullscreen-on') : L.DomUtil.addClass(this.link, 'leaflet-fullscreen-on'); + }, + + _handleFullscreenChange: function () { + var map = this._map; + map.invalidateSize(); + if (!this._screenfull.isFullscreen && !map._exitFired) { + map.fire('exitFullscreen'); + map._exitFired = true; + map._isFullscreen = false; + } + } + }); + + leaflet.Map.include({ + toggleFullscreen: function () { + this.fullscreenControl.toggleFullScreen(); + } + }); + + leaflet.Map.addInitHook(function () { + if (this.options.fullscreenControl) { + this.addControl(leaflet.control.fullscreen(this.options.fullscreenControlOptions)); + } + }); + + leaflet.control.fullscreen = function (options) { + return new leaflet.Control.FullScreen(options); + }; + + // must return an object containing also screenfull to make screenfull + // available outside of this package, if used as an amd module, + // as webpack cannot handle amd define with moduleid + return {leaflet: leaflet, screenfull: screenfull}; +})); diff --git a/assets/js/leaflet/icon-fullscreen.svg b/assets/js/leaflet/icon-fullscreen.svg new file mode 100644 index 000000000..6107d8c34 --- /dev/null +++ b/assets/js/leaflet/icon-fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/js/sections/cqmap.js b/assets/js/sections/cqmap.js index f4d4f912f..1e6888300 100644 --- a/assets/js/sections/cqmap.js +++ b/assets/js/sections/cqmap.js @@ -119,7 +119,13 @@ function load_cq_map2(data) { $("#cqmaptab").append('
    '); } - var map = L.map('cqmap'); + var map = new L.Map('cqmap', { + fullscreenControl: true, + fullscreenControlOptions: { + position: 'topleft' + }, + }); + L.tileLayer( osmUrl, { From 18b14171722ef4e9c8b21b60598289d8d947e7f1 Mon Sep 17 00:00:00 2001 From: Hugo Silva Date: Sun, 19 Mar 2023 10:49:58 +0000 Subject: [PATCH 17/41] Bugfix selecting stations with pending QSOs --- application/models/Stations.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/models/Stations.php b/application/models/Stations.php index 03a689da2..2bf0c9387 100644 --- a/application/models/Stations.php +++ b/application/models/Stations.php @@ -355,12 +355,12 @@ function stations_with_webadif_api_key() { LEFT JOIN webadif ON qsos.COL_PRIMARY_KEY = webadif.qso_id WHERE webadif.qso_id IS NULL GROUP BY qsos.station_id - ) notc + ) notc ON station_profile.station_id = notc.station_id INNER JOIN ( SELECT qsos.station_id, COUNT(qsos.COL_PRIMARY_KEY) c FROM %s qsos GROUP BY qsos.station_id - ) totc + ) totc ON station_profile.station_id = totc.station_id WHERE COALESCE(station_profile.webadifapikey, '') <> '' AND COALESCE(station_profile.webadifapiurl, '') <> '' AND station_profile.user_id = %d From 5011dfb4e8df5615a292c00366889476b47ae442 Mon Sep 17 00:00:00 2001 From: Andreas <6977712+AndreasK79@users.noreply.github.com> Date: Sun, 19 Mar 2023 16:58:29 +0100 Subject: [PATCH 18/41] [DXCC Map] Added full screen option --- assets/js/sections/dxccmap.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/assets/js/sections/dxccmap.js b/assets/js/sections/dxccmap.js index e117ffec3..8a8a9f8fa 100644 --- a/assets/js/sections/dxccmap.js +++ b/assets/js/sections/dxccmap.js @@ -42,7 +42,13 @@ function load_dxcc_map2(data, worked, confirmed, notworked) { $("#dxccmaptab").append('
    '); } - var map = L.map('dxccmap'); + var map = new L.Map('dxccmap', { + fullscreenControl: true, + fullscreenControlOptions: { + position: 'topleft' + }, + }); + L.tileLayer( osmUrl, { From 477f4977568a30cf2b52277eb75dea8421b76cc7 Mon Sep 17 00:00:00 2001 From: Andreas <6977712+AndreasK79@users.noreply.github.com> Date: Sun, 19 Mar 2023 16:58:41 +0100 Subject: [PATCH 19/41] [IOTA Map] Added full screen option --- assets/js/sections/iotamap.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/assets/js/sections/iotamap.js b/assets/js/sections/iotamap.js index 8a2b7f4d0..5c579387e 100644 --- a/assets/js/sections/iotamap.js +++ b/assets/js/sections/iotamap.js @@ -42,7 +42,13 @@ function load_iota_map2(data, worked, confirmed, notworked) { $("#iotamaptab").append('
    '); } - var map = L.map('iotamap'); + var map = new L.Map('iotamap', { + fullscreenControl: true, + fullscreenControlOptions: { + position: 'topleft' + }, + }); + L.tileLayer( osmUrl, { From 7d7a569e41a09c8fc39a7bbf73d718cf87747387 Mon Sep 17 00:00:00 2001 From: Andreas <6977712+AndreasK79@users.noreply.github.com> Date: Sun, 19 Mar 2023 17:13:07 +0100 Subject: [PATCH 20/41] [Map] Added full screen option to gridsquare and activator maps --- application/views/interface_assets/footer.php | 12 ++++++++++-- application/views/visitor/layout/footer.php | 6 +++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/application/views/interface_assets/footer.php b/application/views/interface_assets/footer.php index 24eab9705..f4f784736 100644 --- a/application/views/interface_assets/footer.php +++ b/application/views/interface_assets/footer.php @@ -1303,7 +1303,11 @@ function update_stats(){ layers: [layer], center: [19, 0], zoom: 2, - minZoom: 1 + minZoom: 1, + fullscreenControl: true, + fullscreenControlOptions: { + position: 'topleft' + }, }); var printer = L.easyPrint({ @@ -1446,7 +1450,11 @@ function onMapClick(event) { layers: [layer], center: [19, 0], zoom: 2, - minZoom: 1 + minZoom: 1, + fullscreenControl: true, + fullscreenControlOptions: { + position: 'topleft' + }, }); var grid_two = ; diff --git a/application/views/visitor/layout/footer.php b/application/views/visitor/layout/footer.php index fe48b51a5..23689ee26 100644 --- a/application/views/visitor/layout/footer.php +++ b/application/views/visitor/layout/footer.php @@ -69,7 +69,11 @@ var map = L.map('gridsquare_map', { layers: [layer], center: [19, 0], - zoom: 2 + zoom: 2, + fullscreenControl: true, + fullscreenControlOptions: { + position: 'topleft' + }, }); var printer = L.easyPrint({ From f4ea93ba89c76063941015183977903de8297fc1 Mon Sep 17 00:00:00 2001 From: Hugo Silva Date: Mon, 20 Mar 2023 13:39:35 +0000 Subject: [PATCH 21/41] Bugfix: don't duplicate 'Mark as Uploaded' records --- application/controllers/Webadif.php | 13 +++++++++---- application/models/Logbook_model.php | 26 ++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/application/controllers/Webadif.php b/application/controllers/Webadif.php index 608b67d66..76650b0d0 100644 --- a/application/controllers/Webadif.php +++ b/application/controllers/Webadif.php @@ -91,7 +91,7 @@ public function export() { $data['page_title'] = "QO-100 Dx Club Upload"; - $data['station_profiles'] = $this->stations->all_of_user(); + $data['station_profiles'] = $this->stations->stations_with_webadif_api_key(); $data['station_profile'] = $this->stations->stations_with_webadif_api_key(); $this->load->view('interface_assets/header', $data); @@ -137,12 +137,17 @@ public function mark_webadif() { $data['page_title'] = "QO-100 Dx Club Upload"; $station_id = $this->security->xss_clean($this->input->post('station_profile')); + $from = $this->security->xss_clean($this->input->post('from')); + $to = $this->security->xss_clean($this->input->post('to')); - $this->load->model('adif_data'); + $this->load->model('logbook_model'); - $data['qsos'] = $this->adif_data->export_custom($this->input->post('from'), $this->input->post('to'), $station_id); + $data['qsos'] = $this->logbook_model->get_webadif_qsos( + $station_id, + $from, + $to + ); - $this->load->model('logbook_model'); if ($data['qsos']!==null) { foreach ($data['qsos']->result() as $qso) { $this->logbook_model->mark_webadif_qsos_sent($qso->COL_PRIMARY_KEY); diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index f43659224..226e48956 100755 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -1278,9 +1278,10 @@ function get_qrz_qsos($station_id){ /* * Function returns the QSOs from the logbook, which have not been either marked as uploaded to webADIF */ - function get_webadif_qsos($station_id){ + function get_webadif_qsos($station_id,$from, $to){ $sql = " - SELECT qsos.*, station_profile.* FROM %s qsos + SELECT qsos.*, station_profile.* + FROM %s qsos INNER JOIN station_profile ON qsos.station_id = station_profile.station_id LEFT JOIN webadif ON qsos.COL_PRIMARY_KEY = webadif.qso_id WHERE qsos.station_id = %d @@ -1291,6 +1292,27 @@ function get_webadif_qsos($station_id){ $this->config->item('table_name'), $station_id ); + if ($from) { + $from = DateTime::createFromFormat('d/m/Y', $from); + $from = $from->format('Y-m-d'); + + $sql.=" AND qsos.COL_TIME_ON >= %s"; + $sql=sprintf( + $sql, + $this->db->escape($from) + ); + } + if ($to) { + $to = DateTime::createFromFormat('d/m/Y', $to); + $to = $to->format('Y-m-d'); + + $sql.=" AND qsos.COL_TIME_ON <= %s"; + $sql=sprintf( + $sql, + $this->db->escape($to) + ); + } + return $this->db->query($sql); } From 7ac0cc21b03fe18d6337a392c5fa9176cc41e6f0 Mon Sep 17 00:00:00 2001 From: Hugo Silva Date: Mon, 20 Mar 2023 18:49:46 +0000 Subject: [PATCH 22/41] Fix bug while uploading all qsos --- application/models/Logbook_model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index 226e48956..596498548 100755 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -1278,7 +1278,7 @@ function get_qrz_qsos($station_id){ /* * Function returns the QSOs from the logbook, which have not been either marked as uploaded to webADIF */ - function get_webadif_qsos($station_id,$from, $to){ + function get_webadif_qsos($station_id,$from = null, $to = null){ $sql = " SELECT qsos.*, station_profile.* FROM %s qsos From 4877b32304a381b897de2981870d20ce11f7ca73 Mon Sep 17 00:00:00 2001 From: Hugo Silva Date: Mon, 20 Mar 2023 19:15:05 +0000 Subject: [PATCH 23/41] Mark QSOs as uploaded in batch to improve performance --- application/controllers/Webadif.php | 11 +++++++++-- application/models/Logbook_model.php | 24 +++++++++++++----------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/application/controllers/Webadif.php b/application/controllers/Webadif.php index 76650b0d0..d5fcb1831 100644 --- a/application/controllers/Webadif.php +++ b/application/controllers/Webadif.php @@ -61,7 +61,7 @@ function mass_upload_qsos($station_id, $webadif_api_key, $webadif_api_url) { $result = $this->logbook_model->push_qso_to_webadif($webadif_api_url, $webadif_api_key, $adif); if ($result) { - $this->logbook_model->mark_webadif_qsos_sent($qso->COL_PRIMARY_KEY); + $this->logbook_model->mark_webadif_qsos_sent([$qso->COL_PRIMARY_KEY]); $i++; } else { $errorMessage = 'QO-100 Dx Club upload failed for qso: Call: ' . $qso->COL_CALL . ' Band: ' . $qso->COL_BAND . ' Mode: ' . $qso->COL_MODE . ' Time: ' . $qso->COL_TIME_ON; @@ -149,8 +149,15 @@ public function mark_webadif() { ); if ($data['qsos']!==null) { + $qsoIDs=[]; foreach ($data['qsos']->result() as $qso) { - $this->logbook_model->mark_webadif_qsos_sent($qso->COL_PRIMARY_KEY); + $qsoIDs[]=$qso->COL_PRIMARY_KEY; + } + $batchSize = 500; + while ($qsoIDs !== []) { + $slice = array_slice($qsoIDs, 0, $batchSize); + $qsoIDs = array_slice($qsoIDs, $batchSize); + $this->logbook_model->mark_webadif_qsos_sent($slice); } } diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index 226e48956..060cef711 100755 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -497,7 +497,7 @@ function add_qso($data, $skipexport = false) { ); if ($result) { - $this->mark_webadif_qsos_sent($last_id); + $this->mark_webadif_qsos_sent([$last_id]); } } } @@ -632,17 +632,19 @@ function mark_qrz_qsos_sent($primarykey) { /* * Function marks QSOs as uploaded to WebADIF. - * $primarykey is the unique id for that QSO in the logbook + * $qsoIDs is an arroy of unique id for the QSOs in the logbook */ - function mark_webadif_qsos_sent($primarykey) + function mark_webadif_qsos_sent(array $qsoIDs) { - $data = array( - 'upload_date' => date("Y-m-d H:i:s", strtotime("now")), - 'qso_id' => $primarykey, - ); - - $this->db->insert('webadif', $data); - + $data = []; + $now = date("Y-m-d H:i:s", strtotime("now")); + foreach ($qsoIDs as $qsoID) { + $data[] = [ + 'upload_date' => $now, + 'qso_id' => $qsoID, + ]; + } + $this->db->insert_batch('webadif', $data); return true; } @@ -1278,7 +1280,7 @@ function get_qrz_qsos($station_id){ /* * Function returns the QSOs from the logbook, which have not been either marked as uploaded to webADIF */ - function get_webadif_qsos($station_id,$from, $to){ + function get_webadif_qsos($station_id,$from = null, $to = null){ $sql = " SELECT qsos.*, station_profile.* FROM %s qsos From a0d02e96e91b28cc66fdea049445e17adf85308a Mon Sep 17 00:00:00 2001 From: Hugo Silva Date: Mon, 20 Mar 2023 19:47:37 +0000 Subject: [PATCH 24/41] Show nice message if the user doesn't have QO-100 Dx Club export configured --- application/views/webadif/export.php | 33 +++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/application/views/webadif/export.php b/application/views/webadif/export.php index 2c07640ef..f375784bc 100644 --- a/application/views/webadif/export.php +++ b/application/views/webadif/export.php @@ -19,12 +19,15 @@
    -

    Here you can see and upload all QSOs which have not been previously uploaded to QO-100 Dx Club.

    -

    You need to set a QO-100 Dx Club API key in your station profile. Only station profiles with an API Key are displayed.

    -

    WarningThis might take a while as QSO uploads are processed sequentially.

    +

    Here you can see and upload all QSOs which have not been previously uploaded to QO-100 Dx Club.

    + result()) { + ?> +

    You need to set a QO-100 Dx Club API key in your station profile. Only station profiles with an API Key are displayed.

    +

    WarningThis might take a while as QSO uploads are processed sequentially.

    + @@ -50,13 +53,22 @@ } else { - echo ''; + ?> +
    + None of your Logbooks are configured to export data to the QO-100 Dx Club's API.
    + To configure this feature, go to your profile page at the QO-100 Dx Club and Create an API key. + Then, navigate to your Station Locations and configure you station with the key you have created at the club. +
    +
    - +

    Here you can mark as uploaded your QSOs which have not been previously uploaded to QO-100 Dx Club.

    + result()!==[]){ + ?>