Clubstations for Wavelog (#1334)

* feat[clubstations]: New DB structure

* feat[clubstations]: Add clubstationstable in user managment

* feat[clubstations]: Show last operator

* feat[clubstations]: Better solution for last operator. tnx for the hint @int2001

* feat[clubstations]: New Club Model and Controller

* feat[clubstations]: Add "Add User" and "Edit User" functionality

* docs[clubstations]: move comment

* feat[clubstations]: Add "Delete Member" functionality

* feat[clubstations]: some enhancements and javascript

* fix[clubstations]: Wrong message class for flashmessages

* feat[clubstations]: Added Switch in the Header menu (not functional yet)

* feat[clubstations]: clubswitch modal

* fix[clubstations]: Load encryption library if not already loaded

* fix[clubstations]: Prevent direct login attempts to clubstations and enhance impersonation authorization

* fix[clubstations]: Typo

* feat[clubstations]: Only show the operator dialog if there is something fishy

* fix[user]: little UI bug

* feat[impersonate]: Add source uid to session data

* fix[impersonate]: logic adjustment

* feat[clubstations]: Add manage button in header menu for club officers

* fix[clubstations]: typo in permission level check

* fix[clubstations]: Full rights for the admin

* feat[impersonate]: Custom sessiondata

* feat[impersonate]: Implement stop impersonation feature with modal confirmation; "the way back"

* fix(modal): Fix bug where modal was hidden when mouse leaved the browser content

* docs(config): Adjust config description for special callsigns and clubstations

* feat(club): Add club access check helper

* typo

* fix[impersonation]: Better text

* feat(club): Selectize for a efficient user search

* feat(clubstations): Restrict clubstations based on users permission level part 1/x

* adjustments for dev merge

* Adjusted club right for the advanced logbook

* feat[user]: Refactoring of the Action Buttons in the user table

* fix[club_permissions]: normal button instead small one for club permissions

* remove unnecessary line break in modal body

* feat[clubstations]: Add Club Mode badge to the header

* fix[clubstations]: fix maintenance mode

* allow switch back on http

* feat(simplefle): display operator input based on club_access

* small UI adjustments

* small UI adjustments

* moved api page to a index.php file and added support for clubstations

* removed unused stuff

* typo

* radios and api keys

* missed one binding

* fix qso view, even officers do just see their own radios in QSO logging

* omit the need for a relogin to see the changes as an admin

* Omit the need for relogin after club changes in general. It's a question of UX. It's better to accept a little higher DB load (if clubstations are enabled) then the need of an user to relogin. There is some room for improvement by changing user_model->get_by_id() and adding a join there. This can be done later if we see that the load is too high

* If the user is not the creator of the API key, it's likely a clubstation. In this case the callsign of the clubstation can not be the same as the callsign of the user (operator call provided by the user). If this is the case, we need to use the callsign of the creator of the API key

* remove debug messages

* better UI in header

* found a typo

* full access in clubstations for admins (if accessed via admin usertable)

* adjusted text

* adjusted text

* adjust text

* reduce required chars

* bugfix: missing the correct authentication in case the admin was not member of the club. he wasn't able to switch back

* reduce debug messages

* fixed UI bug related to tooltips

* load js in controller

* upps..

* some UI adjustments

* corrected permissions

* if user gets delete we need to remove data in club_permissions and also api keys which were created by this user

* Notify members about new memberships or changes in permission level

* add spinner to save button

* make login/logout process more bulletproof

* remove the relogin cookie after the attempt

* better strategy

* bug where switch back failed if user is no admin

* make api keys more secure

* mask not owned api keys

* removed annoying link

* if a user gets removed from a club we also should delete the corresponding api keys and cat radios

* adjusted wiki link

* Auto creation of logbook and location when new user is created

* store and display locator in uppercase

* same for callsign

* fixed a bug in user/club creation

* Revert "Auto creation of logbook and location when new user is created"
We found another solution to which will be addressed in a second PR
This reverts commit f05f4b7bf0.

* Optimized SQL for stats at userlist

* Source query for lastop "out", because mysql<9.0 can't handle Windowed functions

* adjust migration

* add new columns to users table to get created_at and modified_at

* added a partial down function

* add operator dropdown for clubstations

* fix mig version

* Add some backend restrictions in case a user wants to try something funny with the club

---------

Co-authored-by: Andreas Kristiansen <6977712+AndreasK79@users.noreply.github.com>
Co-authored-by: int2001 <joerg@dj7nt.de>
This commit is contained in:
Fabian Berg
2025-01-02 10:22:23 +01:00
committed by GitHub
parent cd9900eb06
commit c70c2ec5cd
63 changed files with 2601 additions and 750 deletions

View File

@@ -64,7 +64,7 @@ $autoload['libraries'] = array('database', 'session', 'curl', 'OptionsLib', 'Pat
| $autoload['helper'] = array('url', 'file');
*/
$autoload['helper'] = array('url', 'security', 'language');
$autoload['helper'] = array('url', 'security', 'language', 'club');
/*

View File

@@ -674,25 +674,21 @@ $config['disable_oqrs'] = false;
/*
|--------------------------------------------------------------------------
| Special Callsign Feature
| Special Callsign Feature aka. Clubstations Support
|--------------------------------------------------------------------------
|
| This config switch is meant to use for Special Callsign operations in a dedicated Wavelog Installation
| If this switch is set to true it will enable a dialog which pops up for each operator after login
| to ask for his personal callsign. This causes the QSOs to get saved with the correct operator data.
| Example: Special Callsign: DL250CDF
| Operator: DF2TG
| This config switch is meant to use for Special Callsign operations or Clubstations.
| If this switch is set to true it enables a whole bunch of features to handle Special Callsigns and Club Callsigns.
| For more Information please visit the Wiki:
| https://github.com/wavelog/wavelog/wiki/Clubstations
|
| !!! Important !!!
| $config['disable_impersonate'] has to be set to false to use this feature.
|
| It is recommend to enable also "Disable Syncing to 3rd party-Services at UI"
| More Information about this feature and how to use it, you can find here:
| https://github.com/wavelog/wavelog/wiki/Recommended-Setup-for-Special-Callsigns-and-Clubs
*/
$config['special_callsign'] = false;
// hides the usermenu; takes action only if "special_callsign" is true
$config['sc_hide_usermenu'] = true;
/*
|--------------------------------------------------------------------------
@@ -700,7 +696,7 @@ $config['sc_hide_usermenu'] = true;
|--------------------------------------------------------------------------
|
| This config switch disables the impersonate feature. This feauture is used to impersonate another user.
| Impersonate is enabled by default. To disable it, set the value to false.
| Impersonate is enabled by default. To disable it, set the value to false. Also the special_callsign feature needs this to be false.
|
*/

View File

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

View File

@@ -10,7 +10,7 @@ class adif extends CI_Controller {
$this->load->helper(array('form', 'url'));
$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'); }
if(!$this->user_model->authorize(2) || !clubaccess_check(9)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
}
public function test() {
@@ -145,6 +145,13 @@ class adif extends CI_Controller {
$data['page_title'] = __("ADIF Import / Export");
$data['max_upload'] = ini_get('upload_max_filesize');
if ($this->config->item('special_callsign') && clubaccess_check(9) && $this->session->userdata('clubstation') == 1) {
$this->load->model('club_model');
$data['club_operators'] = $this->club_model->get_club_members($this->session->userdata('user_id'));
} else {
$data['club_operators'] = false;
}
$data['station_profile'] = $this->stations->all_of_user();
$active_station_id = $this->stations->find_active();
$station_profile = $this->stations->profile($active_station_id);
@@ -185,7 +192,8 @@ class adif extends CI_Controller {
$this->load->view('interface_assets/footer');
} else {
if ($this->stations->check_station_is_accessible($this->input->post('station_profile', TRUE))) {
$contest=$this->security->xss_clean($this->input->post('contest')) ?? '';
$contest=$this->input->post('contest', true) ?? '';
$club_operator=$this->input->post('club_operator', true) ?? '';
$stopnow=false;
$fdata = array('upload_data' => $this->upload->data());
ini_set('memory_limit', '-1');
@@ -229,6 +237,9 @@ class adif extends CI_Controller {
if ($contest != '') {
$record['contest_id']=$contest;
}
if ($club_operator != '') {
$record['operator']=strtoupper($club_operator);
}
if(count($record) == 0) {
break;
};
@@ -236,7 +247,7 @@ class adif extends CI_Controller {
};
$record=''; // free memory
try {
$custom_errors = $this->logbook_model->import_bulk($alladif, $this->input->post('station_profile', TRUE), $this->input->post('skipDuplicate'), $this->input->post('markClublog'),$this->input->post('markLotw'), $this->input->post('dxccAdif'), $this->input->post('markQrz'), $this->input->post('markEqsl'), $this->input->post('markHrd'), $this->input->post('markDcl'), true, $this->input->post('operatorName'), false, $this->input->post('skipStationCheck'));
$custom_errors = $this->logbook_model->import_bulk($alladif, $this->input->post('station_profile', TRUE), $this->input->post('skipDuplicate'), $this->input->post('markClublog'),$this->input->post('markLotw'), $this->input->post('dxccAdif'), $this->input->post('markQrz'), $this->input->post('markEqsl'), $this->input->post('markHrd'), $this->input->post('markDcl'), true, $this->input->post('operatorName') ?? false, false, $this->input->post('skipStationCheck'));
} catch (Exception $e) {
log_message('error', 'Import error: '.$e->getMessage());
$data['page_title'] = __("ADIF Import failed!");

View File

@@ -2,42 +2,31 @@
class API extends CI_Controller {
// Do absolutely nothing
function index() {
echo "nothing to see";
}
function help() {
$this->load->model('user_model');
// Check if users logged in
if($this->user_model->validate_session() == 0) {
// user is not logged in
redirect('user/login');
}
if(!$this->user_model->authorize(3)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
$this->load->model('api_model');
$this->load->library('form_validation');
$data['api_keys'] = $this->api_model->keys();
$data['clubmode'] = $this->session->userdata('clubstation') == 1 ? true : false;
$data['page_title'] = __("API");
$this->load->view('interface_assets/header', $data);
$this->load->view('api/help');
$this->load->view('api/index');
$this->load->view('interface_assets/footer');
}
// legacy
function help() {
redirect('api');
}
function edit($key) {
$this->load->model('user_model');
// Check if users logged in
if($this->user_model->validate_session() == 0) {
// user is not logged in
redirect('user/login');
}
if(!$this->user_model->authorize(3)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
$this->load->model('api_model');
@@ -66,38 +55,40 @@ class API extends CI_Controller {
$this->session->set_flashdata('notice', sprintf(__("API Key %s description has been updated."), "<b>".$this->input->post('api_key')."</b>"));
redirect('api/help');
redirect('api');
}
}
function generate($rights) {
$this->load->model('user_model');
if(!$this->user_model->authorize(3)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
// Check if users logged in
if($this->user_model->validate_session() == 0) {
// user is not logged in
redirect('user/login');
if ($rights !== "r" && $rights !== "rw") {
$this->session->set_flashdata('error', __("Invalid API rights"));
redirect('api');
exit;
}
$this->load->model('api_model');
$data['api_keys'] = $this->api_model->generate_key($rights);
if ($this->session->userdata('clubstation') == 1 && $this->session->userdata('impersonate') == 1) {
$creator = $this->session->userdata('source_uid');
} else {
$creator = $this->session->userdata('user_id');
}
redirect('api/help');
if ($this->api_model->generate_key($rights, $creator)) {
$this->session->set_flashdata('success', __("API Key generated"));
} else {
$this->session->set_flashdata('error', __("API Key could not be generated"));
}
redirect('api');
}
function delete($key) {
$this->load->model('user_model');
// Check if users logged in
if($this->user_model->validate_session() == 0) {
// user is not logged in
redirect('user/login');
}
if(!$this->user_model->authorize(3)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
$this->load->model('api_model');
@@ -106,7 +97,7 @@ class API extends CI_Controller {
$this->session->set_flashdata('notice', sprintf(__("API Key %s has been deleted"), "<b>".$key."</b>" ));
redirect('api/help');
redirect('api');
}
// Example of authing
@@ -189,6 +180,25 @@ class API extends CI_Controller {
}
$userid = $this->api_model->key_userid($obj['key']);
$created_by = $this->api_model->key_created_by($obj['key']);
/**
* As the API key user could use it also for clubstations we need to do an additional check here. Only if clubstations are enabled
*
* In Detail:
* If the user is not the creator of the API key, it's likely a clubstation. In this case the callsign of the clubstation
* can not be the same as the callsign of the user (operator call provided by the user). If this is the case, we need to use the callsign of the creator of the API key
*/
if ($this->config->item('special_callsign')) {
if ($userid != $created_by) {
$this->load->model('user_model');
$real_operator = $this->user_model->get_by_id($created_by)->row()->user_callsign;
// TODO: It would be possible to check here if operator is allowed to use the clubstation, but this can be added later if needed
} else {
$real_operator = null;
}
}
$this->api_model->update_last_used(($obj['key']));
if(!isset($obj['station_profile_id']) || $this->stations->check_station_against_user($obj['station_profile_id'], $userid) == false) {
@@ -224,7 +234,13 @@ class API extends CI_Controller {
}
if(count($record) == 0) {
break;
};
}
// in case the provided op call is the same as the clubstation callsign, we need to use the creator of the API key as the operator
$recorded_operator = $record['operator'] ?? '';
if ($real_operator != null && ($record['operator'] == $record['station_callsign']) || ($recorded_operator == '')) {
$record['operator'] = $real_operator;
}
if ((key_exists('gridsquare',$record)) && (($mygrid ?? '') != '') && (($record['gridsquare'] ?? '') != '') && (!(key_exists('distance',$record)))) {
$record['distance'] = $this->qra->distance($mygrid, $record['gridsquare'], 'K');
}
@@ -573,6 +589,14 @@ class API extends CI_Controller {
$this->api_model->update_last_used($obj['key']);
$user_id = $this->api_model->key_userid($obj['key']);
$created_by = $this->api_model->key_created_by($obj['key']);
// Clubmode needs an additional check for the operator
if ($user_id != $created_by) {
$operator = $created_by;
} else {
$operator = $user_id;
}
// Special Case: Yaesu Radio's use CW-U and CW-L which aren't official ADIF Modes. We override this here to CW
if (isset($obj['mode']) && (strtoupper($obj['mode']) == 'CW-U' || strtoupper($obj['mode']) == 'CW-L')) {
@@ -580,7 +604,7 @@ class API extends CI_Controller {
}
// Store Result to Database
$this->cat->update($obj, $user_id);
$this->cat->update($obj, $user_id, $operator);
// Return Message

View File

@@ -12,7 +12,7 @@ class Band extends CI_Controller {
$this->load->helper(array('form', 'url'));
$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'); }
if(!$this->user_model->authorize(2) || !clubaccess_check(9)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
}
public function index()

View File

@@ -13,7 +13,7 @@ class Cabrillo extends CI_Controller {
parent::__construct();
$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'); }
if(!$this->user_model->authorize(2) || !clubaccess_check(9)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
}
public function index() {

View File

@@ -15,7 +15,7 @@ class Cfdexport extends CI_Controller {
$this->load->model('logbook_model');
$this->load->model('bands');
if(!$this->user_model->authorize(2)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
if(!$this->user_model->authorize(2) || !clubaccess_check(9)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
$data['page_title'] = __("CFD Export");

View File

@@ -0,0 +1,237 @@
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
class Club extends CI_Controller
{
/**
* Permission Levels:
* 9 - Officer
* 3 - Member
*
* These permission levels are independent of the codeigniter permission levels and managed in the club_permissions table!
* // TODO: Add a Wiki Link to the Permission Levels and Explainations about the Club Features
*/
/**
* @var array $permissions
*/
private $permissions = [
9 => "Club Officer",
3 => "Club Member",
];
public function index()
{
// nothing to display
redirect('dashboard');
}
public function permissions($club_id) {
$this->load->model('user_model');
$this->load->model('club_model');
$this->load->library('form_validation');
$cid = $this->security->xss_clean($club_id);
$club = $this->user_model->get_by_id($cid)->row();
if (!is_numeric($cid)) {
$this->session->set_flashdata('error', __("Invalid User ID!"));
redirect('user');
}
if(!$this->user_model->authorize(99) && !$this->club_model->club_authorize(9, $cid)) {
$this->session->set_flashdata('error', __("You're not allowed to do that!"));
redirect('dashboard');
}
if ($club->clubstation != 1) {
$this->session->set_flashdata('error', __("This user is not a club station."));
redirect('user');
}
$data['page_title'] = __("Club Permissions");
$data['club'] = $club;
$data['club_members'] = $this->club_model->get_club_members($cid);
$data['users'] = $this->user_model->users();
$data['permissions'] = $this->permissions;
$footerData = [];
$footerData['scripts'] = [
'assets/js/sections/club_permissions.js?' . filemtime(realpath(__DIR__ . "/../../assets/js/sections/club_permissions.js")),
];
$this->load->view('interface_assets/header', $data);
$this->load->view('club/permissions');
$this->load->view('interface_assets/footer', $footerData);
}
public function get_users() {
if(!clubaccess_check(9)) {
$this->session->set_flashdata('error', __("You're not allowed to do that!"));
redirect('dashboard');
}
if (!$this->load->is_loaded('user_model')) {
$this->load->model('user_model');
}
$query = (string) $this->input->post('query', true) ?? '';
if (empty($query)) {
header('Content-Type: application/json');
echo json_encode([]);
return;
}
$users = $this->user_model->search_users($query);
$result = [];
if ($users != false) {
foreach ($users->result() as $user) {
$result[] = [
'user_id' => $user->user_id,
'user_callsign' => $user->user_callsign,
'user_firstname' => $user->user_firstname,
'user_lastname' => $user->user_lastname
];
}
}
header('Content-Type: application/json');
echo json_encode($result);
}
public function alter_member() {
$this->load->model('user_model');
$this->load->model('club_model');
$club_id = $this->input->post('club_id', true);
$user_id = $this->input->post('user_id', true);
$p_level = $this->input->post('permission', true);
if (!is_numeric($club_id)) {
$this->session->set_flashdata('error', __("Invalid Club ID!"));
redirect('dashboard');
}
if(!$this->user_model->authorize(99) && !$this->club_model->club_authorize(9, $club_id)) {
$this->session->set_flashdata('error', __("You're not allowed to do that!"));
redirect('dashboard');
}
$this->club_model->alter_member($club_id, $user_id, $p_level);
if ($this->input->post('notify_user', true) == 'on') {
if (!$this->notification($user_id, $club_id, $this->input->post('notify_message', true))) {
$this->session->set_flashdata('error', __("User could not be notified. Please check your email settings."));
}
}
$this->session->set_flashdata('success', __("Club member permissions have been updated."));
redirect('club/permissions/'.$club_id);
}
public function delete_member() {
$this->load->model('user_model');
$this->load->model('club_model');
$club_id = $this->input->post('club_id', true);
$user_id = $this->input->post('user_id', true);
if (!is_numeric($club_id)) {
$this->session->set_flashdata('error', __("Invalid Club ID!"));
redirect('dashboard');
}
if(!$this->user_model->authorize(99) && !$this->club_model->club_authorize(9, $club_id)) {
$this->session->set_flashdata('error', __("You're not allowed to do that!"));
redirect('dashboard');
}
if ($this->club_model->delete_member($club_id, $user_id)) {
$this->session->set_flashdata('success', __("User removed from club."));
} else {
$this->session->set_flashdata('error', __("User could not be removed from club."));
}
redirect('club/permissions/'.$club_id);
}
public function switch_modal() {
$this->load->model('user_model');
$this->load->model('club_model');
$this->load->library('encryption');
$cid = $this->input->post('club_id', true);
$data['club_callsign'] = $this->input->post('club_callsign', true);
$user_id = $this->session->userdata('user_id');
if (!is_numeric($cid)) {
$this->session->set_flashdata('error', __("Invalid Club ID!"));
redirect('dashboard');
}
if(!$this->club_model->club_authorize(3, $cid)) {
$this->session->set_flashdata('error', __("You're not allowed to do that!"));
redirect('dashboard');
}
$data['impersonate_hash'] = $this->encryption->encrypt($user_id . '/' . $cid . '/' . time());
$this->load->view('club/clubswitch_modal', $data);
}
private function notification($user_id, $club_id, $message) {
$this->load->library('email');
$this->load->model('club_model');
switch ($message) {
case 'new_member':
$view = 'email/club/new_member';
break;
case 'modified_member':
$view = 'email/club/modified_member';
break;
default:
log_message('error', "Club Notification; Can't notify user - Invalid message type.");
$this->session->set_flashdata('error', __("Invalid message type."));
redirect('club/permissions/'.$club_id);
}
if($this->optionslib->get_option('emailProtocol') == "smtp") {
$config = [
'protocol' => $this->optionslib->get_option('emailProtocol'),
'smtp_crypto' => $this->optionslib->get_option('smtpEncryption'),
'smtp_host' => $this->optionslib->get_option('smtpHost'),
'smtp_port' => $this->optionslib->get_option('smtpPort'),
'smtp_user' => $this->optionslib->get_option('smtpUsername'),
'smtp_pass' => $this->optionslib->get_option('smtpPassword'),
'crlf' => "\r\n",
'newline' => "\r\n"
];
$this->email->initialize($config);
} else {
log_message('error', "Club Notification; Can't notify user - Email settings not configured.");
$this->session->set_flashdata('error', __("Email settings not configured."));
redirect('club/permissions/'.$club_id);
}
$user = $this->user_model->get_by_id($user_id)->row();
$club = $this->user_model->get_by_id($club_id)->row();
$permission = $this->club_model->get_permission($club_id, $user_id);
$permission_level = $this->permissions[$permission] ?? __("Unknown");
$mail_data['user_callsign'] = $user->user_callsign;
$mail_data['club_callsign'] = $club->user_callsign;
$mail_data['permission_level'] = $permission_level;
$message = $this->email->load($view, $mail_data, $user->user_language);
$this->email->from($this->optionslib->get_option('emailAddress'), $this->optionslib->get_option('emailSenderName'));
$this->email->to($user->user_email);
$this->email->subject($message['subject']);
$this->email->message($message['body']);
return $this->email->send();
}
}

View File

@@ -11,7 +11,7 @@ class Contesting extends CI_Controller {
parent::__construct();
$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'); }
if(!$this->user_model->authorize(2) || !clubaccess_check(99)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
}
public function index() {

View File

@@ -5,7 +5,7 @@ class Csv extends CI_Controller {
public function index() {
$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'); }
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('modes');
$this->load->model('logbook_model');

View File

@@ -4,7 +4,7 @@ class Dxatlas extends CI_Controller {
public function index() {
$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'); }
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('modes');
$this->load->model('logbook_model');

View File

@@ -43,7 +43,7 @@ class eqsl extends CI_Controller {
public function import() {
$this->load->model('user_model');
if (!$this->user_model->authorize(2)) {
if (!$this->user_model->authorize(2) || !clubaccess_check(9)) {
$this->session->set_flashdata('error', __("You're not allowed to do that!"));
redirect('dashboard');
}

View File

@@ -32,6 +32,9 @@ class Hrdlog extends CI_Controller {
* Used for displaying the uid for manually selecting log for upload to hrdlog
*/
public function export() {
$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'); }
$this->load->model('stations');
$data['page_title'] = "HRDlog.net Logbook";

View File

@@ -15,7 +15,7 @@ class Kmlexport extends CI_Controller {
$this->load->model('logbook_model');
$this->load->model('bands');
if(!$this->user_model->authorize(2)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
if(!$this->user_model->authorize(2) || !clubaccess_check(9)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
$data['worked_bands'] = $this->bands->get_worked_bands(); // Used in the view for band select
$data['modes'] = $this->modes->active(); // Used in the view for mode select

View File

@@ -23,7 +23,7 @@ class Labels extends CI_Controller {
$this->load->helper(array('form', 'url', 'psr4_autoloader'));
$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'); }
if(!$this->user_model->authorize(2) || !clubaccess_check(9)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
}

View File

@@ -158,6 +158,8 @@ class Logbookadvanced extends CI_Controller {
}
public function updateFromCallbook() {
if(!clubaccess_check(9)) return;
$this->load->model('logbook_model');
$this->load->model('logbookadvanced_model');
@@ -183,6 +185,8 @@ class Logbookadvanced extends CI_Controller {
}
function export_to_adif() {
if(!clubaccess_check(9)) return;
ini_set('memory_limit', '-1');
set_time_limit(0);
$this->load->model('logbookadvanced_model');
@@ -197,6 +201,8 @@ class Logbookadvanced extends CI_Controller {
}
function export_to_adif_params() {
if(!clubaccess_check(9)) return;
ini_set('memory_limit', '-1');
set_time_limit(0);
$this->load->model('logbookadvanced_model');
@@ -211,6 +217,8 @@ class Logbookadvanced extends CI_Controller {
}
function update_qsl() {
if(!clubaccess_check(9)) return;
$this->load->model('logbookadvanced_model');
$ids = xss_clean($this->input->post('id'));
@@ -239,6 +247,8 @@ class Logbookadvanced extends CI_Controller {
}
function update_qsl_received() {
if(!clubaccess_check(9)) return;
$this->load->model('logbookadvanced_model');
$ids = xss_clean($this->input->post('id'));
@@ -504,6 +514,8 @@ class Logbookadvanced extends CI_Controller {
}
public function userOptions() {
if(!clubaccess_check(9)) return;
$this->load->model('user_options_model');
$userOptions = $this->user_options_model->get_options('LogbookAdvanced')->result();
if (isset($userOptions[0])) {
@@ -524,6 +536,8 @@ class Logbookadvanced extends CI_Controller {
}
public function setUserOptions() {
if(!clubaccess_check(9)) return;
$json_string['datetime']['show'] = $this->input->post('datetime');
$json_string['de']['show'] = $this->input->post('de');
$json_string['dx']['show'] = $this->input->post('dx');
@@ -575,6 +589,8 @@ class Logbookadvanced extends CI_Controller {
}
public function editDialog() {
if(!clubaccess_check(9)) return;
$this->load->model('bands');
$this->load->model('modes');
$this->load->model('logbookadvanced_model');
@@ -589,6 +605,8 @@ class Logbookadvanced extends CI_Controller {
}
public function saveBatchEditQsos() {
if(!clubaccess_check(9)) return;
$ids = xss_clean($this->input->post('ids'));
$column = xss_clean($this->input->post('column'));
$value = xss_clean($this->input->post('value'));
@@ -625,6 +643,8 @@ class Logbookadvanced extends CI_Controller {
}
public function batchDeleteQsos() {
if(!clubaccess_check(9)) return;
$ids = xss_clean($this->input->post('ids'));
$this->load->model('logbookadvanced_model');

View File

@@ -41,7 +41,7 @@ class Lotw extends CI_Controller {
public function index() {
$this->load->library('Permissions');
$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'); }
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('Lotw_model');

View File

@@ -132,6 +132,8 @@ class Oqrs extends CI_Controller {
public function requests() {
$data['page_title'] = __("OQRS Requests");
$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'); }
$this->load->model('logbooks_model');
$logbooks_locations_array = $this->logbooks_model->list_logbook_relationships($this->session->userdata('active_station_logbook'));

View File

@@ -196,6 +196,9 @@ class Qrz extends CI_Controller {
* Used for displaying the uid for manually selecting log for upload to qrz
*/
public function export() {
$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'); }
$this->load->model('stations');
$data['page_title'] = __("QRZ Logbook");

View File

@@ -19,14 +19,10 @@ class QSLPrint extends CI_Controller {
public function index($station_id = 'All')
{
$this->load->model('user_model');
// Check if users logged in
$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'); }
if($this->user_model->validate_session() == 0) {
// user is not logged in
redirect('user/login');
}
$this->load->model('stations');
$data['station_id'] = $this->security->xss_clean($station_id);
$data['station_profile'] = $this->stations->all_of_user();

View File

@@ -32,7 +32,7 @@ class QSO extends CI_Controller {
$data['notice'] = false;
$data['stations'] = $this->stations->all_of_user();
$data['radios'] = $this->cat->radios();
$data['radios'] = $this->cat->radios(true);
$data['radio_last_updated'] = $this->cat->last_updated()->row();
$data['query'] = $this->logbook_model->last_custom('5');
$data['dxcc'] = $this->logbook_model->fetchDxcc();
@@ -130,7 +130,7 @@ class QSO extends CI_Controller {
'prop_mode' => $this->input->post('prop_mode', TRUE),
'radio' => $this->input->post('radio', TRUE),
'station_profile_id' => $this->input->post('station_profile', TRUE),
'operator_callsign' => $this->input->post('operator_callsign', TRUE),
'operator_callsign' => $this->input->post('operator_callsign', TRUE) ?? $this->session->userdata('operator_callsign'),
'transmit_power' => $this->input->post('transmit_power', TRUE)
);
// ];

View File

@@ -28,15 +28,9 @@ class Radio extends CI_Controller {
function status() {
// Check Auth
$this->load->model('user_model');
if(!$this->user_model->authorize(3)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
// Check if users logged in
if ($this->user_model->validate_session() == 0) {
// user is not logged in
redirect('user/login');
}
session_write_close();
$this->load->model('cat');
@@ -45,10 +39,13 @@ class Radio extends CI_Controller {
if ($query->num_rows() > 0) {
echo "<thead><tr>";
echo "<th>" . __("Radio") . "</th>";
if ($this->session->userdata('clubstation') == 1 && clubaccess_check(9)) {
echo "<th>" . __("Operator") . "</th>";
}
echo "<th>" . __("Frequency") . "</th>";
echo "<th>" . __("Mode") . "</th>";
echo "<th>" . __("Timestamp") . "</th>";
echo "<th></th>";
echo "<th> </th>";
echo "<th>" . __("Options") . "</th>";
echo "<th>" . __("Settings") . "</th>";
echo "<th></th>";
@@ -57,6 +54,16 @@ class Radio extends CI_Controller {
echo "<tr>";
echo "<td>" . $row->radio . "</td>";
$this->load->model('user_model');
if ($this->session->userdata('clubstation') == 1 && clubaccess_check(9)) {
$operator = $this->user_model->get_by_id($row->operator)->row();
if ($operator) {
echo "<td>" . $operator->user_callsign . "</td>";
} else {
echo "<td>" . __("UNKNOWN") . "</td>";
}
}
if (empty($row->frequency) || $row->frequency == "0") {
echo "<td>- / -</td>";
} elseif (empty($row->frequency_rx) || $row->frequency_rx == "0") {
@@ -93,14 +100,16 @@ class Radio extends CI_Controller {
echo '<td></td>';
}
$defaul_user_radio = $this->user_options_model->get_options('cat', array('option_name' => 'default_radio'))->row()->option_value ?? NULL;
if (!$defaul_user_radio) {
echo '<td><button id="default_radio_btn_' . $row->id . '" class="btn btn-sm btn-outline-primary ld-ext-right" onclick="set_default_radio(' . $row->id . ')">' . __("Set as default radio") . '<div class="ld ld-ring ld-spin"></div></button</td>';
} else {
if ($defaul_user_radio !== $row->id) {
if ($this->session->userdata('clubstation') != 1) {
$defaul_user_radio = $this->user_options_model->get_options('cat', array('option_name' => 'default_radio'))->row()->option_value ?? NULL;
if (!$defaul_user_radio) {
echo '<td><button id="default_radio_btn_' . $row->id . '" class="btn btn-sm btn-outline-primary ld-ext-right" onclick="set_default_radio(' . $row->id . ')">' . __("Set as default radio") . '<div class="ld ld-ring ld-spin"></div></button</td>';
} else {
echo '<td><button id="default_radio_btn_' . $row->id . '" class="btn btn-sm btn-primary ld-ext-right" onclick="release_default_radio(' . $row->id . ')">' . __("Default (click to release)") . '<div class="ld ld-ring ld-spin"></div></button</td>';
if ($defaul_user_radio !== $row->id) {
echo '<td><button id="default_radio_btn_' . $row->id . '" class="btn btn-sm btn-outline-primary ld-ext-right" onclick="set_default_radio(' . $row->id . ')">' . __("Set as default radio") . '<div class="ld ld-ring ld-spin"></div></button</td>';
} else {
echo '<td><button id="default_radio_btn_' . $row->id . '" class="btn btn-sm btn-primary ld-ext-right" onclick="release_default_radio(' . $row->id . ')">' . __("Default (click to release)") . '<div class="ld ld-ring ld-spin"></div></button</td>';
}
}
}
echo "<td><button id='edit_cat_settings_".$row->id."' \" class=\"editCatSettings btn btn-sm btn-primary\"> " . __("Edit") . "</button></td>";

View File

@@ -14,10 +14,7 @@ class Reg1test extends CI_Controller {
// do authorization check
$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');
}
if(!$this->user_model->authorize(2) || !clubaccess_check(9)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
}
public function index() {

View File

@@ -12,7 +12,7 @@ class Stationsetup extends CI_Controller {
$this->load->helper(array('form', 'url'));
$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'); }
if(!$this->user_model->authorize(2) || !clubaccess_check(9)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
}
public function index() {

View File

@@ -5,6 +5,7 @@ class User extends CI_Controller {
public function index()
{
$this->load->model('user_model');
$this->load->library('form_validation');
if (!$this->load->is_loaded('encryption')) {
$this->load->library('encryption');
@@ -13,6 +14,8 @@ class User extends CI_Controller {
if(!$this->user_model->authorize(99)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
$data['results'] = $this->user_model->users();
$data['clubs'] = $this->user_model->users('is_club');
$data['clubmode'] = $this->config->item('special_callsign');
$data['session_uid'] = $this->session->userdata('user_id');
// Check if impersonating is disabled in the config
@@ -33,11 +36,77 @@ class User extends CI_Controller {
$data['has_flossie'] = ($this->config->item('encryption_key') == 'flossie1234555541') ? true : false;
$footerData = [];
$footerData['scripts'] = [
'assets/js/sections/user.js?' . filemtime(realpath(__DIR__ . "/../../assets/js/sections/user.js")),
];
$data['page_title'] = __("User Accounts");
$this->load->view('interface_assets/header', $data);
$this->load->view('user/index');
$this->load->view('interface_assets/footer');
$this->load->view('interface_assets/footer', $footerData);
}
public function actions_modal() {
$this->load->model('user_model');
$this->load->library('encryption');
if(!$this->user_model->authorize(99)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
$data['user_id'] = $this->input->post('user_id', true) ?? '';
$modal = $this->input->post('modal', true) ?? '';
if($this->session->userdata('user_date_format')) {
$custom_date_format = $this->session->userdata('user_date_format');
} else {
$custom_date_format = $this->config->item('qso_date_format');
}
if ($this->user_model->exists_by_id($data['user_id']) && $modal != '') {
$user = $this->user_model->get_by_id($data['user_id'])->row();
$gettext = new Gettext;
$data['user_name'] = $user->user_name;
$data['user_callsign'] = $user->user_callsign;
$data['user_email'] = $user->user_email;
$data['user_firstname'] = $user->user_firstname;
$data['user_lastname'] = $user->user_lastname;
$data['user_language'] = $gettext->find_by('folder', $user->user_language)['name_en'];
$data['is_clubstation'] = $user->clubstation == 1 ? true : false;
$data['last_seen'] = $user->last_seen;
$data['custom_date_format'] = $custom_date_format;
$data['has_flossie'] = ($this->config->item('encryption_key') == 'flossie1234555541') ? true : false;
$this->load->view('user/modals/'.$modal.'_modal', $data);
} else {
$this->session->set_flashdata('error', __("Invalid User ID or missing modal!"));
redirect('user');
}
}
public function convert() {
$this->load->model('user_model');
if(!$this->user_model->authorize(99)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
$user_id = $this->input->post('user_id', true) ?? '';
$convert_to = $this->input->post('convert_to', true) ?? '';
if ($convert_to != '0' && $convert_to != '1') {
$this->session->set_flashdata('error', __("Invalid Parameter!"));
redirect('dashboard');
}
if ($this->user_model->exists_by_id($user_id)) {
if ($this->user_model->convert($user_id, $convert_to)) {
echo json_encode(true);
} else {
echo json_encode(false);
}
} else {
log_message('error', 'User Conversion - User ID not found: '.$user_id);
echo json_encode(false);
}
}
function add() {
@@ -54,8 +123,8 @@ class User extends CI_Controller {
$this->form_validation->set_rules('user_email', 'E-mail', 'required');
$this->form_validation->set_rules('user_password', 'Password', 'required');
$this->form_validation->set_rules('user_type', 'Type', 'required');
$this->form_validation->set_rules('user_firstname', 'First name', 'required');
$this->form_validation->set_rules('user_lastname', 'Last name', 'required');
// $this->form_validation->set_rules('user_firstname', 'First name', 'required');
// $this->form_validation->set_rules('user_lastname', 'Last name', 'required');
$this->form_validation->set_rules('user_callsign', 'Callsign', 'required');
$this->form_validation->set_rules('user_locator', 'Locator', 'required');
$this->form_validation->set_rules('user_locator', 'Locator', 'callback_check_locator');
@@ -65,9 +134,16 @@ class User extends CI_Controller {
$data['user_form_action'] = site_url('user/add');
$data['bands'] = $this->bands->get_user_bands();
$data['clubstation'] = ($this->input->get('club') ?? '') == '1' ? true : false;
// Get themes list
$data['themes'] = $this->user_model->getThemes();
$footerData = [];
$footerData['scripts'] = [
'assets/js/sections/user.js?' . filemtime(realpath(__DIR__ . "/../../assets/js/sections/user.js")),
];
// Get timezones
$data['timezones'] = $this->user_model->timezones();
$data['user_language'] = 'english';
@@ -81,9 +157,9 @@ class User extends CI_Controller {
$data['user_name'] = $this->input->post('user_name');
$data['user_email'] = $this->input->post('user_email');
$data['user_password'] = $this->input->post('user_password');
$data['user_type'] = $this->input->post('user_type');
$data['user_firstname'] = $this->input->post('user_firstname');
$data['user_lastname'] = $this->input->post('user_lastname');
$data['user_type'] = $data['clubstation'] == true ? '3' : $this->input->post('user_type');
$data['user_firstname'] = $this->input->post('user_firstname') ?? '';
$data['user_lastname'] = $this->input->post('user_lastname') ?? '';
$data['user_callsign'] = $this->input->post('user_callsign');
$data['user_locator'] = $this->input->post('user_locator');
$data['user_timezone'] = $this->input->post('user_timezone');
@@ -121,14 +197,14 @@ class User extends CI_Controller {
} else {
$this->load->view('user/edit', $data);
}
$this->load->view('interface_assets/footer');
$this->load->view('interface_assets/footer', $footerData);
} else {
switch($this->user_model->add($this->input->post('user_name'),
$this->input->post('user_password'),
$this->input->post('user_email'),
$this->input->post('user_type'),
$this->input->post('user_firstname'),
$this->input->post('user_lastname'),
$this->input->post('user_firstname') ?? '',
$this->input->post('user_lastname') ?? '',
$this->input->post('user_callsign'),
$this->input->post('user_locator'),
$this->input->post('user_timezone'),
@@ -169,21 +245,22 @@ class User extends CI_Controller {
$this->input->post('user_eqsl_password'),
$this->input->post('user_clublog_name'),
$this->input->post('user_clublog_password'),
$this->input->post('user_winkey')
$this->input->post('user_winkey'),
$this->input->post('clubstation') == '1' ? true : false
)) {
// Check for errors
case EUSERNAMEEXISTS:
$data['username_error'] = 'Username <b>'.$this->input->post('user_name').'</b> already in use!';
$data['username_error'] = sprintf(__("Username %s already in use!"), '<b>' . $this->input->post('user_name') . '</b>');
break;
case EEMAILEXISTS:
$data['email_error'] = 'E-mail address <b>'.$this->input->post('user_email').'</b> already in use!';
$data['email_error'] = sprintf(__("E-mail %s already in use!"), '<b>' . $this->input->post('user_email') . '</b>');
break;
case EPASSWORDINVALID:
$data['password_error'] = 'Invalid password!';
$data['password_error'] = __("Invalid Password!");
break;
// All okay, return to user screen
case OK:
$this->session->set_flashdata('notice', 'User '.$this->input->post('user_name').' added');
$this->session->set_flashdata('notice', sprintf(__("User %s added!"), '<b>' . $this->input->post('user_name') . '</b>'));
redirect('user');
return;
}
@@ -194,8 +271,8 @@ class User extends CI_Controller {
$data['user_email'] = $this->input->post('user_email');
$data['user_password'] = $this->input->post('user_password');
$data['user_type'] = $this->input->post('user_type');
$data['user_firstname'] = $this->input->post('user_firstname');
$data['user_lastname'] = $this->input->post('user_lastname');
$data['user_firstname'] = $this->input->post('user_firstname') ?? '';
$data['user_lastname'] = $this->input->post('user_lastname') ?? '';
$data['user_callsign'] = $this->input->post('user_callsign');
$data['user_locator'] = $this->input->post('user_locator');
$data['user_measurement_base'] = $this->input->post('user_measurement_base');
@@ -221,7 +298,7 @@ class User extends CI_Controller {
$data['user_quicklog_enter'] = $this->input->post('user_quicklog_enter');
$data['user_language'] = $this->input->post('user_language');
$this->load->view('user/edit', $data);
$this->load->view('interface_assets/footer');
$this->load->view('interface_assets/footer', $footerData);
}
}
@@ -250,12 +327,18 @@ class User extends CI_Controller {
$this->form_validation->set_rules('user_locator', 'Locator', 'callback_check_locator');
$this->form_validation->set_rules('user_timezone', 'Timezone', 'required');
$data['user_form_action'] = site_url('user/edit')."/".$this->uri->segment(3);;
$data['user_form_action'] = site_url('user/edit')."/".$this->uri->segment(3);
$data['clubstation'] = ($query->row()->clubstation == 1) ? true : false;
$data['bands'] = $this->bands->get_user_bands();
// Get themes list
$data['themes'] = $this->user_model->getThemes();
$footerData = [];
$footerData['scripts'] = [
'assets/js/sections/user.js?' . filemtime(realpath(__DIR__ . "/../../assets/js/sections/user.js")),
];
// Get timezones
$data['timezones'] = $this->user_model->timezones();
@@ -640,7 +723,7 @@ class User extends CI_Controller {
$this->load->view('interface_assets/header', $data);
$this->load->view('user/edit', $data);
$this->load->view('interface_assets/footer');
$this->load->view('interface_assets/footer', $footerData);
} else {
unset($data);
switch($this->user_model->edit($this->input->post())) {
@@ -790,6 +873,14 @@ class User extends CI_Controller {
}
function login($firstlogin = false) {
// Due the fact there was a new session generated, we need to get flash messages from a temporary cookie
$tmpdata = json_decode($this->input->cookie(config_item('cookie_prefix') . 'tmp_msg') ?? '') ?? false;
if ($tmpdata) {
$this->session->set_flashdata($tmpdata[0], $tmpdata[1]);
$this->input->set_cookie('tmp_msg', '', -3600, '');
}
// Check our version and run any migrations
if (!$this->load->is_loaded('Migration')) {
$this->load->library('Migration');
@@ -814,7 +905,7 @@ class User extends CI_Controller {
$data['user'] = $query->row();
// Read the cookie keep_login and allow the login
if ($this->input->cookie(config_item('cookie_prefix') . 'keep_login')) {
if ($this->input->cookie(config_item('cookie_prefix') . 'keep_login') || $this->input->cookie(config_item('cookie_prefix') . 're_login')) {
try {
@@ -823,7 +914,7 @@ class User extends CI_Controller {
}
// process the incoming string
$incoming_string = $this->input->cookie(config_item('cookie_prefix') . 'keep_login');
$incoming_string = $this->input->cookie(config_item('cookie_prefix') . 'keep_login') ?? $this->input->cookie(config_item('cookie_prefix') . 're_login');
$i_str_parts_a = explode(base64_encode($this->config->item('base_url')), $incoming_string);
$uid = base64_decode($i_str_parts_a[1]);
$a = $i_str_parts_a[0];
@@ -836,6 +927,13 @@ class User extends CI_Controller {
$user = $this->user_model->get_by_id($uid)->row();
$user_type = $user->user_type;
// direct login to clubstations are not allowed, especially not with a keeplogin cookie
if ($user->clubstation == 1) {
log_message('debug', "User ID: [$uid] Login rejected because of a external clubstation login attempt with a modified cookie. Attack?");
$this->session->set_flashdata('error', __("This is not allowed!"));
redirect('user/login');
}
// compare both strings the hard way and log in if they match
if ($this->user_model->check_keep_hash($a, $b)) {
@@ -845,7 +943,8 @@ class User extends CI_Controller {
// if everything is fine we can log in the user
$this->user_model->update_session($uid);
$this->user_model->set_last_seen($uid);
log_message('debug', "User ID: [$uid] logged in successfully with 'Keep Login'.");
log_message('info', "User ID: [$uid] logged in successfully with 'Keep Login'.");
$this->input->set_cookie('re_login', '', -3600, ''); // delete re_login cookie in case this was a re-login from a clubstation or impersonated user
redirect('dashboard');
} else {
@@ -855,6 +954,7 @@ class User extends CI_Controller {
// Delete keep_login cookie
$this->input->set_cookie('keep_login', '', -3600, '');
$this->input->set_cookie('re_login', '', -3600, '');
redirect('user/login');
}
@@ -864,6 +964,7 @@ class User extends CI_Controller {
// Delete keep_login cookie
$this->input->set_cookie('keep_login', '', -3600, '');
$this->input->set_cookie('re_login', '', -3600, '');
$this->session->set_flashdata('error', __("Login failed. Try again."));
redirect('user/login');
}
@@ -873,6 +974,7 @@ class User extends CI_Controller {
// Delete keep_login cookie
$this->input->set_cookie('keep_login', '', -3600, '');
$this->input->set_cookie('re_login', '', -3600, '');
$this->session->set_flashdata('error', __("Login failed. Try again."));
redirect('user/login');
@@ -888,7 +990,8 @@ class User extends CI_Controller {
$this->load->view('interface_assets/footer');
} else {
if($this->user_model->login() == 1) {
$login_attempt = $this->user_model->login();
if($login_attempt === 1) {
$this->user_model->update_session($data['user']->user_id);
$cookie= array(
@@ -916,7 +1019,10 @@ class User extends CI_Controller {
}
$this->user_model->set_last_seen($data['user']->user_id);
redirect('dashboard');
} else if ($login_attempt === 2) {
$this->session->set_flashdata('warning', __("You can't login to a clubstation directly. Use your personal account instead."));
redirect('user/login');
} else {
if(ENVIRONMENT == 'maintenance') {
$this->session->set_flashdata('notice', __("Sorry. This instance is currently in maintenance mode. If this message appears unexpectedly or keeps showing up, please contact an administrator. Only administrators are currently allowed to log in."));
@@ -929,17 +1035,25 @@ class User extends CI_Controller {
}
}
function logout() {
function logout($custom_message = null, $hard_logout = true) {
$this->load->model('user_model');
$user_name = $this->session->userdata('user_name');
// Delete keep_login cookie
$this->input->set_cookie('keep_login', '', -3600, '');
if ($hard_logout) {
$this->input->set_cookie('re_login', '', -3600, '');
$this->input->set_cookie('keep_login', '', -3600, '');
}
$this->user_model->clear_session();
$this->session->set_flashdata('notice', sprintf(__("User %s logged out."), $user_name));
if ($custom_message != null && is_array($custom_message)) {
$this->input->set_cookie('tmp_msg', json_encode([$custom_message[0], $custom_message[1]]), 10, '');
} else {
$this->input->set_cookie('tmp_msg', json_encode(['notice', sprintf(__("User %s logged out."), $user_name)]), 10, '');
}
redirect('user/login');
}
@@ -1185,13 +1299,15 @@ class User extends CI_Controller {
// Check if impersonating is disabled in the config
if ($this->config->item('disable_impersonate')) {
show_404();
$this->session->set_flashdata('error', sprintf(__("You currently can't impersonate another user. You need to set %s to %s in your config.php!"), "'disable_impersonate'", "'false'"));
redirect('dashboard');
}
// Load the encryption library
if (!$this->load->is_loaded('encryption')) {
$this->load->library('encryption');
}
// Load the user model
$this->load->model('user_model');
@@ -1241,13 +1357,38 @@ class User extends CI_Controller {
redirect('dashboard');
}
// before we can impersonate a user, we need to make sure the current user is an admin
// TODO: authorize from additional datatable 'impersonators' to allow other user types to impersonate
// before we can impersonate a user, we need to make sure the current user is allowed to do so
$clubswitch = $this->input->post('clubswitch', TRUE) ?? '';
$custom_sessiondata = [];
$source_user = $this->user_model->get_by_id($source_uid)->row();
if(!$source_user || !$this->user_model->authorize(99)) {
$this->session->set_flashdata('error', __("You're not allowed to do that!"));
redirect('dashboard');
if ($clubswitch == 1) {
$this->load->model('club_model');
if (!$this->club_model->club_authorize(3, $target_uid, $source_uid) || !$this->user_model->authorize(3)) {
$this->session->set_flashdata('error', __("You're not allowed to do that!"));
redirect('dashboard');
} else {
$targetclub = array_filter($this->session->userdata('available_clubstations'), function($club) use ($target_uid) {
return $club->user_id == $target_uid;
});
$p_level = !empty($targetclub) ? reset($targetclub)->p_level : null;
if ($p_level != null) {
$custom_sessiondata['p_level'] = $p_level;
} else {
$this->session->set_flashdata('error', __("Could not determine the correct permission level for the clubstation. Try again after re-login."));
redirect('dashboard');
}
}
} else {
if(!$source_user || !$this->user_model->authorize(99)) {
$this->session->set_flashdata('error', __("You're not allowed to do that!"));
redirect('dashboard');
} else {
$custom_sessiondata['p_level'] = 99; // if the user is an admin he also should have full rights in the clubstations
}
}
$custom_sessiondata['src_call'] = $source_user->user_callsign;
$custom_sessiondata['src_user_type'] = $source_user->user_type;
$custom_sessiondata['src_hash'] = $this->input->post('hash', TRUE) ?? '';
/**
* Impersonate the user
@@ -1255,9 +1396,87 @@ class User extends CI_Controller {
// Update the session with the new user_id
// TODO: Find a solution for sessiondata 'radio', so a user would be able to use e.g. his own radio while impersonating another user
// Due the fact that the user is now impersonating another user, he can't use his default radio anymore
$this->user_model->update_session($target_uid, null, $impersonate = true);
$this->session->set_userdata('source_uid', $source_uid);
$this->user_model->update_session($target_uid, null, true, $custom_sessiondata);
// Redirect to the dashboard, the user should now be logged in as the other user
redirect('dashboard');
}
public function stop_impersonate_modal() {
// Load the user model
$this->load->model('user_model');
if(!$this->user_model->authorize(3)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); }
$this->load->view('user/modals/stop_impersonate_modal');
}
public function stop_impersonate() {
// Load the user model
$this->load->model('user_model');
// there is no source_uid, there is probably something fishy going on. So we clear the session at this point
$source_uid = $this->session->userdata('source_uid') ?? false;
$post_chk = $this->input->post('stopImpersonate', TRUE) ?? false;
if (!$source_uid || $post_chk != 1) {
$this->logout(['error', __("Ups.. Something went wrong. Try to log back in.")]);
exit;
}
// is the current user a clubstation we need to check if the source user was allowed to impersonate the clubstation
$club = $this->user_model->get_by_id($this->session->userdata('user_id'))->row();
$current_is_club = $club->clubstation == 1 ? true : false;
$source_user = $this->user_model->get_by_id($source_uid)->row();
if ($current_is_club) {
$this->load->model('club_model');
if (!$this->club_model->club_authorize(3, $this->session->userdata('user_id'), $source_uid)) {
$this->logout(['error', __("Ups.. Something went wrong. Try to log back in.")]);
exit;
}
} else {
// if the current user is not a clubstation, we need to check if the source user was allowed to impersonate the current user (has to be an admin)
if($source_user->user_type != 99) {
$this->logout(['error', __("Ups.. Something went wrong. Try to log back in.")]);
exit;
}
}
// Validate the impersonate hash
$this->load->library('encryption');
$raw_hash = $this->encryption->decrypt($this->session->userdata('cd_src_hash') ?? false);
if (!$raw_hash) {
$this->logout(['error', __("Ups.. Something went wrong. Try to log back in.")]);
exit;
}
$hash_parts = explode('/', $raw_hash);
$src_in_hash = $hash_parts[0];
$tgt_in_hash = $hash_parts[1];
$timestamp = $hash_parts[2];
if ($src_in_hash != $source_uid || $tgt_in_hash != $this->session->userdata('user_id')) {
$this->logout(['error', __("Ups.. Something went wrong. Try to log back in.")]);
exit;
}
// The timestamp can't be older then 2 hours
if (time() - $timestamp > 7200) {
$this->logout(['notice', __("The ability to return quickly has been disabled after the security hash expired. Please log in again.")]);
exit;
}
// Create a keep login cookie which will be used to log back in as the source user
$encrypted_string = $this->user_model->keep_cookie_hash($source_uid);
$cookie = array(
'name' => 're_login', // we use a different cookie name to avoid conflicts with the regular keep_login cookie
'value' => $encrypted_string,
'expire' => 20, // seconds should be enough
'secure' => FALSE,
'httponly' => TRUE
);
$this->input->set_cookie($cookie);
// log out on the regular way
$msg = ['notice', sprintf(__("You have been logged out of the clubstation %s. Welcome back, %s, to your personal account!"), $club->user_callsign, $source_user->user_callsign)];
$this->logout($msg, false);
}
}

View File

@@ -87,6 +87,9 @@ class Webadif extends CI_Controller {
* Used for displaying the uid for manually selecting log for upload to webADIF consumer
*/
public function export() {
$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'); }
$this->load->model('stations');
$data['page_title'] = __("QO-100 Dx Club Upload");

View File

@@ -0,0 +1,49 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Small Helper functions for the Clubstations feature
*/
/**
* Access Check in the UI for impersonated clubstations
*
* To see available permission levels, check out 'application/controllers/Club.php'
*/
if (!function_exists('clubaccess_check')) {
function clubaccess_check($required_level, $qso_id = 0) {
$CI =& get_instance();
if (!$CI->load->is_loaded('session')) {
$CI->load->library('session');
}
$clubmode = $CI->config->item('special_callsign') ?? false;
$clubstation = $CI->session->userdata('clubstation') ?? 0;
if ($clubmode && $clubstation == 1) {
// check if the user has the required level
if ($CI->session->userdata('cd_p_level') >= $required_level) {
if ($qso_id != 0) {
// check if the QSO belongs to the user
$CI->load->model('logbook_model');
$qso = $CI->logbook_model->get_qso($qso_id)->row();
if ($qso->COL_OPERATOR == $CI->session->userdata('operator_callsign') || $CI->session->userdata('cd_p_level') >= 9) {
return true;
} else {
return false;
}
} else {
return true;
}
} else {
return false;
}
} else {
// return always true if the special callsign mode is disabled, so there is no change in behaviour
return true;
}
}
}

View File

@@ -0,0 +1,64 @@
<?php
class Migration_clubstations extends CI_Migration {
public function up() {
// Add new columns to 'users' table
$this->add_column_if_not_exists('users', 'clubstation', 'TINYINT(1) DEFAULT 0 AFTER user_type');
$this->add_column_if_not_exists('users', 'created_at', 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP');
$this->add_column_if_not_exists('users', 'modified_at', 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
// Create a new table for the club permissions
if (!$this->db->table_exists('club_permissions')) {
try {
$this->db->query("CREATE TABLE `club_permissions` (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
club_id INT(6) UNSIGNED NOT NULL,
user_id INT(6) UNSIGNED NOT NULL,
p_level INT(2) UNSIGNED NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
modified_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE RESTRICT,
UNIQUE (user_id, club_id)
);");
} catch (Exception $e) {
log_message('error', 'Mig 230 - Error creating table "club_permissions": ' . $e->getMessage());
}
}
// Add 'created_by' column to 'api' table
$this->add_column_if_not_exists('api', 'created_by', 'INT(6) NOT NULL DEFAULT 0');
$this->db->query("UPDATE `api` SET created_by = user_id;");
$this->db->query("ALTER TABLE `api` MODIFY created_by INT(6) NOT NULL;");
// Add 'created_at' column to 'api' table
$this->add_column_if_not_exists('api', 'created_at', 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP');
// Add 'operator' column to 'cat' table
$this->add_column_if_not_exists('cat', 'operator', 'INT(6) NOT NULL DEFAULT 0');
$this->db->query("UPDATE `cat` SET operator = user_id;");
$this->db->query("ALTER TABLE `cat` MODIFY operator INT(6) NOT NULL;");
}
public function down() {
// Due the risk of data loss we can't drop any new created columns or tables
// But we can drop some of the timestamp columns
$this->db->query("ALTER TABLE `users` DROP COLUMN created_at;");
$this->db->query("ALTER TABLE `users` DROP COLUMN modified_at;");
$this->db->query("ALTER TABLE `api` DROP COLUMN created_at;");
}
private function add_column_if_not_exists($table, $column, $definition) {
$col_check = $this->db->query("SHOW COLUMNS FROM `$table` LIKE '$column';")->num_rows() > 0;
if (!$col_check) {
try {
$this->db->query("ALTER TABLE `$table` ADD COLUMN $column $definition;");
log_message('info', "Mig 230 - Column '$column' added to table '$table'.");
} catch (Exception $e) {
log_message('error', "Mig 230 - Error adding column '$column' to table '$table': " . $e->getMessage());
}
} else {
log_message('info', "Mig 230 - Column '$column' already exists in table '$table', skipping ALTER TABLE.");
}
}
}

View File

@@ -8,299 +8,137 @@
class API_Model extends CI_Model {
// GET API Keys
function keys() {
$this->db->where('user_id', $this->session->userdata('user_id'));
return $this->db->get('api');
}
// GET API Keys
function keys() {
$binding = [];
$user_id = $this->session->userdata('user_id');
$clubstation = $this->session->userdata('clubstation');
$impersonate = $this->session->userdata('impersonate');
function CountKeysWithNoUserID() {
$this->db->where('user_id =', NULL);
if ($clubstation == 1 && $impersonate == 1) {
$sql = "SELECT api.*, users.user_callsign
FROM api
JOIN users ON api.created_by = users.user_id
WHERE api.user_id = ?";
$binding[] = $user_id;
if (!clubaccess_check(9)) {
$sql .= " AND api.created_by = ?";
$binding[] = $this->session->userdata('source_uid');
}
} else {
$sql = "SELECT * FROM api WHERE user_id = ?";
$binding[] = $user_id;
}
return $this->db->query($sql, $binding);
}
function key_description($key) {
$this->db->where('user_id', $this->session->userdata('user_id'));
$this->db->where('key', $key);
$query = $this->db->get('api');
return $query->num_rows();
}
function key_description($key) {
$this->db->where('user_id', $this->session->userdata('user_id'));
$this->db->where('key', $key);
$query = $this->db->get('api');
return $query->result_array()[0];
}
return $query->result_array()[0];
}
function key_userid($key) {
$this->db->where('key', $key);
$query = $this->db->get('api');
$this->db->where('key', $key);
$query = $this->db->get('api');
return $query->result_array()[0]['user_id'];
}
return $query->result_array()[0]['user_id'];
}
function update_key_description($key, $description) {
function key_created_by($key) {
$this->db->where('key', $key);
$query = $this->db->get('api');
$data = array(
'description' => xss_clean($description),
return $query->result_array()[0]['created_by'];
}
function update_key_description($key, $description) {
$data = array(
'description' => xss_clean($description),
);
$this->db->where('key', xss_clean($key));
$this->db->where('user_id', $this->session->userdata('user_id'));
$this->db->update('api', xss_clean($data));
}
}
function delete_key($key) {
function delete_key($key) {
$this->db->where('user_id', $this->session->userdata('user_id'));
$this->db->where('key', xss_clean($key));
$this->db->where('key', xss_clean($key));
$this->db->delete('api');
}
// Generate API Key
function generate_key($rights) {
}
// Expects either rw (Read, Write) or r (read only)
// Generate API Key
function generate_key($rights, $creator = NULL) {
// Generate Unique Key
$data['key'] = uniqid("wl");
// Generate Unique Key
$data['key'] = "wl" . substr(md5(uniqid(rand(), true)), 19);
$data['rights'] = $rights;
$data['rights'] = $rights;
// Set API key to active
$data['status'] = "active";
// Set API key to active
$data['status'] = "active";
$data['user_id'] = $this->session->userdata('user_id');
$data['created_by'] = $creator != NULL ? $creator : $this->session->userdata('user_id');
$this->db->insert('api', $data);
}
function access($key) {
// No key = no access, mate
if(!$key) {
return $status = "No Key Found";
}
// Check that the key is valid
$this->db->where('key', $key);
$query = $this->db->get('api');
if ($query->num_rows() > 0)
{
foreach ($query->result() as $row)
{
if($row->status == "active") {
return $status = $row->rights;
} else {
return $status = "Key Disabled";
}
}
} else {
return $status = "No Key Found";
}
}
function authorize($key) {
$r = $this->access($key);
if($r == "rw") {
return 2;
} else if($r == "r") {
return 1;
} else {
return 0;
}
}
function update_last_used($key) {
$this->db->set('last_used', 'NOW()', FALSE);
$this->db->where('key', xss_clean($key));
$this->db->update('api');
// Also update last_seen in user table
$user_id = $this->key_userid($key);
$this->load->model('user_model');
$this->user_model->set_last_seen($user_id);
}
// FUNCTION: string name(string $column)
// Converts a MySQL column name to a more friendly name
function name($col)
{
if($this->_columnName[$col])
{
return $this->_columnName[$col]['Name'];
if ($this->db->insert('api', $data)) {
return true;
} else {
return false;
}
else
{
}
function access($key) {
// No key = no access, mate
if (!$key) {
return $status = "No Key Found";
}
// Check that the key is valid
$this->db->where('key', $key);
$query = $this->db->get('api');
if ($query->num_rows() > 0) {
foreach ($query->result() as $row) {
if ($row->status == "active") {
return $status = $row->rights;
} else {
return $status = "Key Disabled";
}
}
} else {
return $status = "No Key Found";
}
}
function authorize($key) {
$r = $this->access($key);
if ($r == "rw") {
return 2;
} else if ($r == "r") {
return 1;
} else {
return 0;
}
}
// FUNCTION: string description(string $column)
// Returns the description for a MySQL column name
function description($col)
{
if($this->_columnName[$col])
{
if($this->_columnName[$col]['Description'] != "")
{
return $this->_columnName[$col]['Description'];
}
else
{
return "No description available";
}
}
else
{
return 0;
}
function update_last_used($key) {
$this->db->set('last_used', 'NOW()', FALSE);
$this->db->where('key', xss_clean($key));
$this->db->update('api');
// Also update last_seen in user table
$user_id = $this->key_userid($key);
$this->load->model('user_model');
$this->user_model->set_last_seen($user_id);
}
// FUNCTION: string name(string $name)
// Converts a friendly name to a MySQL column name
function column($name)
{
while ($column = current($this->_columnName))
{
if($this->_columnName[key($this->_columnName)]['Name'] == $name)
{
$a = key($this->_columnName);
reset($this->_columnName);
return $a;
}
next($this->_columnName);
}
reset($this->_columnName);
return 0;
}
// ARRAY: $_columnName
// An array matching MySQL column names to friendly names, descriptions and types
private $_columnName = array(
'COL_PRIMARY_KEY' => array('Name' => 'ID', 'Description' => 'Unique QSO ID', 'Type' => 'I'),
'COL_ADDRESS' => array('Name' => 'Address', 'Description' => 'Operator\'s address', 'Type' => 'S'),
'COL_AGE' => array('Name' => 'Age', 'Description' => 'Operator\'s age', 'Type' => 'I'),
'COL_A_INDEX' => array('Name' => 'AIndex', 'Description' => 'Solar A Index', 'Type' => 'I'),
'COL_ANT_AZ' => array('Name' => 'AntennaAzimuth', 'Description' => 'Antenna azimuth', 'Type' => 'I'),
'COL_ANT_EL' => array('Name' => 'AntennaElevation', 'Description' => 'Antenna elevation', 'Type' => 'I'),
'COL_ANT_PATH' => array('Name' => 'AntennaPath', 'Description' => 'Antenna path', 'Type' => ''),
'COL_ARRL_SECT' => array('Name' => 'ARRLSection', 'Description' => 'ARRL Section', 'Type' => ''),
'COL_BAND' => array('Name' => 'Band', 'Description' => 'Band', 'Type' => ''),
'COL_BAND_RX' => array('Name' => 'BandRX', 'Description' => '', 'Type' => ''),
'COL_BIOGRAPHY' => array('Name' => 'Biography', 'Description' => '', 'Type' => ''),
'COL_CALL' => array('Name' => 'Call', 'Description' => '', 'Type' => ''),
'COL_CHECK' => array('Name' => 'UNK_CHECK', 'Description' => '', 'Type' => ''),
'COL_CLASS' => array('Name' => 'Class', 'Description' => '', 'Type' => ''),
'COL_CNTY' => array('Name' => 'County', 'Description' => '', 'Type' => ''),
'COL_COMMENT' => array('Name' => 'Comment', 'Description' => '', 'Type' => ''),
'COL_CONT' => array('Name' => 'Continent', 'Description' => '', 'Type' => ''),
'COL_CONTACTED_OP' => array('Name' => 'UNK_CONTACTED_OP', 'Description' => '', 'Type' => ''),
'COL_CONTEST_ID' => array('Name' => 'ContestID', 'Description' => '', 'Type' => ''),
'COL_COUNTRY' => array('Name' => 'Country', 'Description' => '', 'Type' => ''),
'COL_CQZ' => array('Name' => 'CQZone', 'Description' => '', 'Type' => ''),
'COL_DARC_DOK' => array('Name' => 'DOK', 'Description' => '', 'Type' => ''),
'COL_DISTANCE' => array('Name' => 'Distance', 'Description' => '', 'Type' => ''),
'COL_DXCC' => array('Name' => 'DXCC', 'Description' => '', 'Type' => ''),
'COL_EMAIL' => array('Name' => 'EMail', 'Description' => '', 'Type' => ''),
'COL_EQ_CALL' => array('Name' => 'UNK_EQ_CALL', 'Description' => '', 'Type' => ''),
'COL_EQSL_QSLRDATE' => array('Name' => 'EQSLRecievedDate', 'Description' => '', 'Type' => ''),
'COL_EQSL_QSLSDATE' => array('Name' => 'EQSLSentDate', 'Description' => '', 'Type' => ''),
'COL_EQSL_QSL_RCVD' => array('Name' => 'EQSLRecieved', 'Description' => '', 'Type' => ''),
'COL_EQSL_QSL_SENT' => array('Name' => 'EQSLSent', 'Description' => '', 'Type' => ''),
'COL_EQSL_STATUS' => array('Name' => 'EQSLStatus', 'Description' => '', 'Type' => ''),
'COL_FORCE_INIT' => array('Name' => 'UNK_FORCE_INIT', 'Description' => '', 'Type' => ''),
'COL_FREQ' => array('Name' => 'Frequency', 'Description' => '', 'Type' => ''),
'COL_FREQ_RX' => array('Name' => 'FrequencyRX', 'Description' => '', 'Type' => ''),
'COL_GRIDSQUARE' => array('Name' => 'Locator', 'Description' => '', 'Type' => ''),
'COL_HEADING' => array('Name' => 'Heading', 'Description' => '', 'Type' => ''),
'COL_IOTA' => array('Name' => 'IOTA', 'Description' => '', 'Type' => ''),
'COL_ITUZ' => array('Name' => 'ITUZone', 'Description' => '', 'Type' => ''),
'COL_K_INDEX' => array('Name' => 'KIndex', 'Description' => '', 'Type' => ''),
'COL_LAT' => array('Name' => 'Latitude', 'Description' => '', 'Type' => ''),
'COL_LON' => array('Name' => 'Longitude', 'Description' => '', 'Type' => ''),
'COL_LOTW_QSLRDATE' => array('Name' => 'LOTWRecievedDate', 'Description' => '', 'Type' => ''),
'COL_LOTW_QSLSDATE' => array('Name' => 'LOTWSentDate', 'Description' => '', 'Type' => ''),
'COL_LOTW_QSL_RCVD' => array('Name' => 'LOTWRecieved', 'Description' => '', 'Type' => ''),
'COL_LOTW_QSL_SENT' => array('Name' => 'LOTWSent', 'Description' => '', 'Type' => ''),
'COL_LOTW_STATUS' => array('Name' => 'LOTWStatus', 'Description' => '', 'Type' => ''),
'COL_MAX_BURSTS' => array('Name' => 'MaxBursts', 'Description' => '', 'Type' => ''),
'COL_MODE' => array('Name' => 'Mode', 'Description' => '', 'Type' => ''),
'COL_MS_SHOWER' => array('Name' => 'MSShower', 'Description' => '', 'Type' => ''),
'COL_MY_CITY' => array('Name' => 'MyCity', 'Description' => '', 'Type' => ''),
'COL_MY_CNTY' => array('Name' => 'MyCounty', 'Description' => '', 'Type' => ''),
'COL_MY_COUNTRY' => array('Name' => 'MyCountry', 'Description' => '', 'Type' => ''),
'COL_MY_CQ_ZONE' => array('Name' => 'MyCQZone', 'Description' => '', 'Type' => ''),
'COL_MY_GRIDSQUARE' => array('Name' => 'MyLocator', 'Description' => '', 'Type' => ''),
'COL_MY_IOTA' => array('Name' => 'MyIOTA', 'Description' => '', 'Type' => ''),
'COL_MY_ITU_ZONE' => array('Name' => 'MyITUZone', 'Description' => '', 'Type' => ''),
'COL_MY_LAT' => array('Name' => 'MyLatitude', 'Description' => '', 'Type' => ''),
'COL_MY_LON' => array('Name' => 'MyLongitude', 'Description' => '', 'Type' => ''),
'COL_MY_NAME' => array('Name' => 'MyName', 'Description' => '', 'Type' => ''),
'COL_MY_POSTAL_CODE' => array('Name' => 'MyPostalCode', 'Description' => '', 'Type' => ''),
'COL_MY_RIG' => array('Name' => 'MyRig', 'Description' => '', 'Type' => ''),
'COL_MY_SIG' => array('Name' => 'MySig', 'Description' => '', 'Type' => ''),
'COL_MY_SIG_INFO' => array('Name' => 'MySigInfo', 'Description' => '', 'Type' => ''),
'COL_MY_STATE' => array('Name' => 'MyState', 'Description' => '', 'Type' => ''),
'COL_MY_STREET' => array('Name' => 'MyStreet', 'Description' => '', 'Type' => ''),
'COL_NAME' => array('Name' => 'Name', 'Description' => '', 'Type' => ''),
'COL_NOTES' => array('Name' => 'Notes', 'Description' => '', 'Type' => ''),
'COL_NR_BURSTS' => array('Name' => 'NumBursts', 'Description' => '', 'Type' => ''),
'COL_NR_PINGS' => array('Name' => 'NumPings', 'Description' => '', 'Type' => ''),
'COL_OPERATOR' => array('Name' => 'Operator', 'Description' => '', 'Type' => ''),
'COL_OWNER_CALLSIGN' => array('Name' => 'OwnerCallsign', 'Description' => '', 'Type' => ''),
'COL_PFX' => array('Name' => 'Prefix', 'Description' => '', 'Type' => ''),
'COL_PRECEDENCE' => array('Name' => 'Precedence', 'Description' => '', 'Type' => ''),
'COL_PROP_MODE' => array('Name' => 'PropMode', 'Description' => '', 'Type' => ''),
'COL_PUBLIC_KEY' => array('Name' => 'PublicKey', 'Description' => '', 'Type' => ''),
'COL_QSLMSG' => array('Name' => 'QSLMessage', 'Description' => '', 'Type' => ''),
'COL_QSLRDATE' => array('Name' => 'QSLRecievedDate', 'Description' => '', 'Type' => ''),
'COL_QSLSDATE' => array('Name' => 'QSLSentDate', 'Description' => '', 'Type' => ''),
'COL_QSL_RCVD' => array('Name' => 'QSLRecieved', 'Description' => '', 'Type' => ''),
'COL_QSL_RCVD_VIA' => array('Name' => 'QSLRecievedVia', 'Description' => '', 'Type' => ''),
'COL_QSL_SENT' => array('Name' => 'QSLSent', 'Description' => '', 'Type' => ''),
'COL_QSL_SENT_VIA' => array('Name' => 'QSLSentVia', 'Description' => '', 'Type' => ''),
'COL_QSL_VIA' => array('Name' => 'QSLVia', 'Description' => '', 'Type' => ''),
'COL_QSO_COMPLETE' => array('Name' => 'QSOComplete', 'Description' => '', 'Type' => ''),
'COL_QSO_RANDOM' => array('Name' => 'QSORandom', 'Description' => '', 'Type' => ''),
'COL_QTH' => array('Name' => 'QTH', 'Description' => '', 'Type' => ''),
'COL_RIG' => array('Name' => 'Rig', 'Description' => '', 'Type' => ''),
'COL_RST_RCVD' => array('Name' => 'ReportRecieved', 'Description' => '', 'Type' => ''),
'COL_RST_SENT' => array('Name' => 'ReportSent', 'Description' => '', 'Type' => ''),
'COL_RX_PWR' => array('Name' => 'RXPower', 'Description' => '', 'Type' => ''),
'COL_SAT_MODE' => array('Name' => 'SatMode', 'Description' => '', 'Type' => ''),
'COL_SAT_NAME' => array('Name' => 'SatName', 'Description' => '', 'Type' => ''),
'COL_SFI' => array('Name' => 'SFI', 'Description' => '', 'Type' => ''),
'COL_SIG' => array('Name' => 'Sig', 'Description' => '', 'Type' => ''),
'COL_SIG_INFO' => array('Name' => 'SigInfo', 'Description' => '', 'Type' => ''),
'COL_SRX' => array('Name' => 'UNK_SRX', 'Description' => '', 'Type' => ''),
'COL_STX' => array('Name' => 'UNK_STX', 'Description' => '', 'Type' => ''),
'COL_SRX_STRING' => array('Name' => 'UNK_SRX_STRING', 'Description' => '', 'Type' => ''),
'COL_STX_STRING' => array('Name' => 'UNK_STX_STRING', 'Description' => '', 'Type' => ''),
'COL_STATE' => array('Name' => 'State', 'Description' => '', 'Type' => ''),
'COL_STATION_CALLSIGN' => array('Name' => 'StationCall', 'Description' => '', 'Type' => ''),
'COL_SWL' => array('Name' => 'SWL', 'Description' => '', 'Type' => ''),
'COL_TEN_TEN' => array('Name' => 'TenTen', 'Description' => '', 'Type' => ''),
'COL_TIME_OFF' => array('Name' => 'TimeOff', 'Description' => '', 'Type' => ''),
'COL_TIME_ON' => array('Name' => 'TimeOn', 'Description' => '', 'Type' => ''),
'COL_TX_PWR' => array('Name' => 'TXPower', 'Description' => '', 'Type' => ''),
'COL_WEB' => array('Name' => 'Website', 'Description' => '', 'Type' => ''),
'COL_USER_DEFINED_0' => array('Name' => 'UNK_USER_DEFINED_0', 'Description' => '', 'Type' => ''),
'COL_USER_DEFINED_1' => array('Name' => 'UNK_USER_DEFINED_1', 'Description' => '', 'Type' => ''),
'COL_USER_DEFINED_2' => array('Name' => 'UNK_USER_DEFINED_2', 'Description' => '', 'Type' => ''),
'COL_USER_DEFINED_3' => array('Name' => 'UNK_USER_DEFINED_3', 'Description' => '', 'Type' => ''),
'COL_USER_DEFINED_4' => array('Name' => 'UNK_USER_DEFINED_4', 'Description' => '', 'Type' => ''),
'COL_USER_DEFINED_5' => array('Name' => 'UNK_USER_DEFINED_5', 'Description' => '', 'Type' => ''),
'COL_USER_DEFINED_6' => array('Name' => 'UNK_USER_DEFINED_6', 'Description' => '', 'Type' => ''),
'COL_USER_DEFINED_7' => array('Name' => 'UNK_USER_DEFINED_7', 'Description' => '', 'Type' => ''),
'COL_USER_DEFINED_8' => array('Name' => 'UNK_USER_DEFINED_8', 'Description' => '', 'Type' => ''),
'COL_USER_DEFINED_9' => array('Name' => 'UNK_USER_DEFINED_9', 'Description' => '', 'Type' => ''),
'COL_CREDIT_GRANTED' => array('Name' => 'UNK_CREDIT_GRANTED', 'Description' => '', 'Type' => ''),
'COL_CREDIT_SUBMITTED' => array('Name' => 'UNK_CREDIT_SUBMITTED', 'Description' => '', 'Type' => ''),
);
}
?>

View File

@@ -2,7 +2,7 @@
class Cat extends CI_Model {
function update($result, $user_id) {
function update($result, $user_id, $operator) {
$timestamp = gmdate("Y-m-d H:i:s");
@@ -16,6 +16,7 @@
}
$this->db->where('radio', $result['radio']);
$this->db->where('operator', $operator);
$this->db->where('user_id', $user_id);
$query = $this->db->get('cat');
@@ -74,6 +75,7 @@
// Add a new record
$data['radio'] = $result['radio'];
$data['user_id'] = $user_id;
$data['operator'] = $operator;
$this->db->insert('cat', $data);
}
@@ -82,6 +84,9 @@
function status() {
//$this->db->where('radio', $result['radio']);
$this->db->where('user_id', $this->session->userdata('user_id'));
if ($this->session->userdata('clubstation') == 1 && !clubaccess_check(9)) {
$this->db->where('operator', $this->session->userdata('source_uid'));
}
$query = $this->db->get('cat');
return $query;
@@ -89,6 +94,9 @@
function recent_status() {
$this->db->where('user_id', $this->session->userdata('user_id'));
if ($this->session->userdata('clubstation') == 1 && !clubaccess_check(9)) {
$this->db->where('operator', $this->session->userdata('source_uid'));
}
$this->db->where("timestamp > date_sub(UTC_TIMESTAMP(), interval 15 minute)", NULL, FALSE);
$query = $this->db->get('cat');
@@ -96,22 +104,39 @@
}
/* Return list of radios */
function radios() {
function radios($only_operator = false) {
$this->db->select('id, radio');
$this->db->where('user_id', $this->session->userdata('user_id'));
if ($only_operator || ($this->session->userdata('clubstation') == 1 && !clubaccess_check(9))) {
$this->db->where('operator', $this->session->userdata('source_uid'));
}
$query = $this->db->get('cat');
return $query;
}
function radio_status($id) {
$binding = [];
$sql = 'SELECT * FROM `cat` WHERE id = ? AND user_id = ?';
return $this->db->query($sql, array($id, $this->session->userdata('user_id')));
$binding[] = $id;
$binding[] = $this->session->userdata('user_id');
if ($this->session->userdata('clubstation') == 1 && !clubaccess_check(9)) {
$sql .= ' AND operator = ?';
$binding[] = $this->session->userdata('source_uid');
}
return $this->db->query($sql, $binding);
}
function last_updated() {
$sql = 'SELECT * FROM cat WHERE user_id = ? ORDER BY timestamp DESC LIMIT 1';
return $this->db->query($sql, $this->session->userdata('user_id'));
$binding = [];
$sql = 'SELECT * FROM cat WHERE user_id = ?';
$binding[] = $this->session->userdata('user_id');
if ($this->session->userdata('clubstation') == 1 && !clubaccess_check(9)) {
$sql .= ' AND operator = ?';
$binding[] = $this->session->userdata('source_uid');
}
$sql .= ' ORDER BY timestamp DESC LIMIT 1';
return $this->db->query($sql, $binding);
}
function delete($id) {

View File

@@ -0,0 +1,205 @@
<?php
class Club_model extends CI_Model {
/**
* Authorization for Club Features
*
* @param int $level
* @param int $club_id
* @param int $user_id (optional)
*
* @return boolean
*/
function club_authorize($level, $club_id, $user_id = NULL) {
if ($level == 0 || !is_numeric($level)) {
log_message('error', 'Club Authorization Level not set!');
return false;
}
if ($club_id == 0 || !is_numeric($club_id)) {
$this->session->set_flashdata('error', __("Invalid Club ID!"));
redirect('dashboard');
}
// admin is always allowed
$this->load->model('user_model');
if ($user_id != NULL) {
if ($this->user_model->get_by_id($user_id)->row()->user_type == 99) {
return true;
}
}
if ($user_id == NULL || !is_numeric($user_id)) {
$user_id = $this->session->userdata('user_id');
} else {
$user_id = xss_clean($user_id);
}
// Now we can check the database for permissions
$binding = [];
$sql = 'SELECT * FROM `club_permissions` WHERE user_id = ? AND club_id = ? AND p_level >= ?';
$binding[] = $user_id;
$binding[] = $club_id;
$binding[] = $level;
$query = $this->db->query($sql, $binding);
if ($query->num_rows() > 0) {
return true;
} else {
$this->session->set_flashdata('error', __("You're not allowed to do that!"));
redirect('dashboard');
}
return false;
}
/**
* Get Permissionlevel for User in Club
*
* @param int $club_id
* @param int $user_id
*
* @return int
*/
function get_permission($club_id, $user_id) {
if ($club_id == 0 || !is_numeric($club_id)) {
$this->session->set_flashdata('error', __("Invalid Club ID!"));
redirect('dashboard');
}
if ($user_id == 0 || !is_numeric($user_id)) {
$this->session->set_flashdata('error', __("Invalid User ID!"));
redirect('dashboard');
}
$binding = [];
$sql = 'SELECT p_level FROM `club_permissions` WHERE user_id = ? AND club_id = ?';
$binding[] = $user_id;
$binding[] = $club_id;
$query = $this->db->query($sql, $binding);
if ($query->num_rows() > 0) {
return $query->row()->p_level;
} else {
return 0;
}
}
/**
* Get Club Members
*
* @param int $club_id
*
* @return array
*/
function get_club_members($club_id) {
$sql = 'SELECT users.user_id, users.user_type, users.user_callsign, users.user_name, users.user_firstname, users.user_lastname, users.user_email, club_permissions.p_level
FROM club_permissions
JOIN users ON club_permissions.user_id = users.user_id
WHERE club_permissions.club_id = ?;';
$members = $this->db->query($sql, [$club_id])->result();
return $members;
}
/**
* Get available Clubstations per User
*
* @param int $user_id
*
* @return array
*/
function get_clubstations($user_id) {
$sql = 'SELECT users.user_id, users.user_callsign, club_permissions.p_level
FROM club_permissions
JOIN users ON club_permissions.club_id = users.user_id
WHERE club_permissions.user_id = ?;';
$clubs = $this->db->query($sql, [$user_id])->result();
return $clubs;
}
/**
*
* Add Club Member
*
* @param int $club_id
* @param int $user_id
* @param int $p_level
*
* @return boolean
*/
function alter_member($club_id, $user_id, $p_level) {
if ($club_id == 0 || !is_numeric($club_id)) {
$this->session->set_flashdata('error', __("Invalid Club ID!"));
redirect('dashboard');
}
if ($user_id == 0 || !is_numeric($user_id)) {
$this->session->set_flashdata('error', __("Invalid User ID!"));
redirect('dashboard');
}
if ($p_level == 0 || !is_numeric($p_level)) {
$this->session->set_flashdata('error', __("Invalid Permission Level!"));
redirect('dashboard');
}
$binding = [];
$sql = "INSERT INTO club_permissions (club_id, user_id, p_level)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE p_level = VALUES(p_level)";
$binding[] = $club_id;
$binding[] = $user_id;
$binding[] = $p_level;
if ($this->db->query($sql, $binding)) {
return true;
} else {
$this->session->set_flashdata('error', __("Error adding Club Member!"));
redirect('club/permissions/' . $club_id);
}
}
/**
*
* Delete Club Member
*
* @param int $club_id
* @param int $user_id
*
* @return boolean
*/
function delete_member($club_id, $user_id) {
if ($club_id == 0 || !is_numeric($club_id)) {
$this->session->set_flashdata('error', __("Invalid Club ID!"));
redirect('dashboard');
}
if ($user_id == 0 || !is_numeric($user_id)) {
$this->session->set_flashdata('error', __("Invalid User ID!"));
redirect('dashboard');
}
try {
$this->db->query('DELETE FROM club_permissions WHERE club_id = ? AND user_id = ?', [$club_id, $user_id]);
$this->db->query('DELETE FROM api WHERE user_id = ? AND created_by = ?', [$club_id, $user_id]);
$this->db->query('DELETE FROM cat WHERE user_id = ? AND operator = ?', [$club_id, $user_id]);
return true;
} catch (Exception $e) {
log_message('error', 'Error deleting Club Member: ' . $e->getMessage());
return false;
}
}
}

View File

@@ -1490,7 +1490,7 @@ class Logbook_model extends CI_Model {
'COL_ANT_EL' => $this->input->post('ant_el') != '' ? $this->input->post('ant_el') : null,
'station_id' => $stationId,
'COL_STATION_CALLSIGN' => $stationCallsign,
'COL_OPERATOR' => $this->input->post('operator_callsign'),
'COL_OPERATOR' => $this->input->post('operator_callsign') ?? $qso->COL_OPERATOR,
'COL_STATE' => $this->input->post('input_state_edit'),
'COL_CNTY' => $uscounty,
'COL_MY_IOTA' => $iotaRef,
@@ -4293,7 +4293,7 @@ class Logbook_model extends CI_Model {
if ($operatorName != false) {
$operatorName = $this->session->userdata('user_callsign');
$operatorName = $this->session->userdata('operator_callsign');
} else {
$operatorName = (!empty($record['operator'])) ? $record['operator'] : '';
}

View File

@@ -641,7 +641,7 @@ class Staticmap_model extends CI_Model {
$cacheDir = $cachepath . "staticmap_images/";
if (!is_dir($cacheDir)) {
log_message('debug', "Cache directory '" . $cacheDir . "' does not exist. Therefore no static map images to remove...");
log_message('info', "Cache directory '" . $cacheDir . "' does not exist. Therefore no static map images to remove...");
return true;
}
@@ -660,7 +660,7 @@ class Staticmap_model extends CI_Model {
foreach ($linked_logbooks as $logbook_id) {
$slug = $this->stationsetup_model->get_slug($logbook_id);
if ($slug == false) {
log_message('debug', "No slug found for logbook ID " . $logbook_id . ". Continue...");
log_message('info', "No slug found for logbook ID " . $logbook_id . ". Continue...");
continue;
}

View File

@@ -154,6 +154,29 @@ class User_Model extends CI_Model {
}
}
// FUNCTION: array search_users($query)
// Search for users by parts of their callsign
function search_users($query, $clubstations = false) {
if (strlen($query) < 2) {
return false;
}
$this->db->select('user_id, user_callsign, user_firstname, user_lastname');
if (!$clubstations) {
$this->db->where('clubstation', 0);
}
$this->db->group_start();
$this->db->like('user_callsign', $query);
$this->db->or_like('user_firstname', $query);
$this->db->or_like('user_lastname', $query);
$this->db->group_end();
$this->db->limit(100);
$r = $this->db->get($this->config->item('auth_table'));
return $r;
}
// FUNCTION: bool add($username, $password, $email, $type)
// Add a user
function add($username, $password, $email, $type, $firstname, $lastname, $callsign, $locator, $timezone,
@@ -164,7 +187,7 @@ class User_Model extends CI_Model {
$user_language, $user_hamsat_key, $user_hamsat_workable_only, $user_iota_to_qso_tab, $user_sota_to_qso_tab,
$user_wwff_to_qso_tab, $user_pota_to_qso_tab, $user_sig_to_qso_tab, $user_dok_to_qso_tab,
$user_lotw_name, $user_lotw_password, $user_eqsl_name, $user_eqsl_password, $user_clublog_name, $user_clublog_password,
$user_winkey) {
$user_winkey, $clubstation = 0) {
// Check that the user isn't already used
if(!$this->exists($username)) {
$data = array(
@@ -172,10 +195,10 @@ class User_Model extends CI_Model {
'user_password' => $this->_hash($password),
'user_email' => xss_clean($email),
'user_type' => xss_clean($type),
'user_firstname' => xss_clean($firstname),
'user_lastname' => xss_clean($lastname),
'user_callsign' => xss_clean($callsign),
'user_locator' => xss_clean($locator),
'user_firstname' => xss_clean($firstname) ?? '',
'user_lastname' => xss_clean($lastname) ?? '',
'user_callsign' => strtoupper(xss_clean($callsign)),
'user_locator' => strtoupper(xss_clean($locator)),
'user_timezone' => xss_clean($timezone),
'user_measurement_base' => xss_clean($measurement),
'user_date_format' => xss_clean($user_date_format),
@@ -206,7 +229,8 @@ class User_Model extends CI_Model {
'user_eqsl_password' => xss_clean($user_eqsl_password),
'user_clublog_name' => xss_clean($user_clublog_name),
'user_clublog_password' => xss_clean($user_clublog_password),
'winkey' => xss_clean($user_winkey)
'winkey' => xss_clean($user_winkey),
'clubstation' => $clubstation,
);
// Check the password is valid
@@ -254,8 +278,8 @@ class User_Model extends CI_Model {
$data = array(
'user_name' => xss_clean($fields['user_name']),
'user_email' => xss_clean($fields['user_email']),
'user_callsign' => xss_clean($fields['user_callsign']),
'user_locator' => xss_clean($fields['user_locator']),
'user_callsign' => strtoupper(xss_clean($fields['user_callsign'])),
'user_locator' => strtoupper(xss_clean($fields['user_locator'])),
'user_firstname' => xss_clean($fields['user_firstname']),
'user_lastname' => xss_clean($fields['user_lastname']),
'user_timezone' => xss_clean($fields['user_timezone']),
@@ -388,7 +412,8 @@ class User_Model extends CI_Model {
}
// Delete QSOs from $this->config->item('table_name')
$this->db->query("DELETE FROM bandxuser WHERE userid = ?",$user_id);
$this->db->query("DELETE FROM api WHERE user_id = ?",$user_id);
$this->db->query("DELETE FROM api WHERE user_id = ? OR created_by = ?", [$user_id, $user_id]);
$this->db->query("DELETE FROM club_permissions WHERE user_id = ? OR club_id = ?", [$user_id, $user_id]);
$this->db->query("DELETE FROM cat WHERE user_id = ?",$user_id);
$this->db->query("DELETE FROM lotw_certs WHERE user_id = ?",$user_id);
$this->db->query("DELETE FROM notes WHERE user_id = ?",$user_id);
@@ -397,7 +422,7 @@ class User_Model extends CI_Model {
$this->db->query("DELETE FROM queries WHERE userid = ?",$user_id);
$this->db->query("DELETE FROM station_profile WHERE user_id = ?",$user_id);
$this->db->query("DELETE FROM station_logbooks WHERE user_id = ?",$user_id);
$this->db->query("delete from user_options where user_id=?",$user_id);
$this->db->query("DELETE FROM user_options WHERE user_id=?",$user_id);
$this->db->query("DELETE FROM ".$this->config->item('auth_table')." WHERE user_id = ?",$user_id);
return 1;
} else {
@@ -428,7 +453,7 @@ class User_Model extends CI_Model {
// FUNCTION: void update_session()
// Updates a user's login session after they've logged in
// TODO: This should return bool TRUE/FALSE or 0/1
function update_session($id, $u = null, $impersonate = false) {
function update_session($id, $u = null, $impersonate = false, $custom_data = null) {
if ($u == null) {
$u = $this->get_by_id($id);
@@ -475,8 +500,14 @@ class User_Model extends CI_Model {
'isWinkeyEnabled' => $u->row()->winkey,
'hasQrzKey' => $this->hasQrzKey($u->row()->user_id),
'impersonate' => $this->session->userdata('impersonate') ?? false,
'clubstation' => $u->row()->clubstation,
'source_uid' => $this->session->userdata('source_uid') ?? ''
);
if ($this->config->item('special_callsign')) {
$userdata['available_clubstations'] = $this->get_clubstations($u->row()->user_id) ?? 'none';
}
foreach (array_keys($this->frequency->defaultFrequencies) as $band) {
$qrg_unit = $this->session->userdata("qrgunit_$band") ?? ($this->user_options_model->get_options('frequency', array('option_name' => 'unit', 'option_key' => $band), $u->row()->user_id)->row()->option_value ?? '');
if ($qrg_unit !== '') {
@@ -486,8 +517,25 @@ class User_Model extends CI_Model {
}
}
// Restore custom data in impersonation mode
foreach ($this->session->userdata() as $key => $value) {
if (substr($key, 0, 3) == 'cd_') {
$userdata[$key] = $value;
}
}
// Overrides
if ($impersonate) {
$userdata['impersonate'] = true;
$userdata['available_clubstations'] = $this->get_clubstations($u->row()->user_id);
}
if ($userdata['clubstation'] == 1) {
$userdata['available_clubstations'] = 'none';
}
if (isset($custom_data)) {
foreach ($custom_data as $key => $value) {
$userdata['cd_' . $key] = $value;
}
}
$this->session->set_userdata($userdata);
@@ -502,6 +550,7 @@ class User_Model extends CI_Model {
{
$user_id = $this->session->userdata('user_id');
$user_type = $this->session->userdata('user_type');
$src_user_type = $this->session->userdata('cd_src_user_type');
$user_hash = $this->session->userdata('user_hash');
$impersonate = $this->session->userdata('impersonate');
@@ -515,7 +564,7 @@ class User_Model extends CI_Model {
return 0;
}
} else { // handle the maintenance mode and kick out user on page reload if not an admin
if($user_type == '99' || $impersonate === true) {
if($user_type == '99' || $src_user_type === '99') {
if($this->_auth($user_id."-".$user_type, $user_hash)) {
// Freshen the session
$this->update_session($user_id, $u);
@@ -538,8 +587,14 @@ class User_Model extends CI_Model {
// Authenticate a user against the users table
function authenticate($username, $password) {
$u = $this->get($username);
if($u->num_rows() != 0)
{
if($u->num_rows() != 0) {
// direct login to clubstations are not allowed
if ($u->row()->clubstation == 1) {
$uid = $u->row()->user_id;
log_message('debug', "User ID: [$uid] Login rejected because of a external clubstation login attempt.");
return 2;
}
if($this->_auth($password, $u->row()->user_password)) {
if (ENVIRONMENT != "maintenance") {
return 1;
@@ -582,7 +637,7 @@ class User_Model extends CI_Model {
$this->set_last_seen($u->row()->user_id);
}
return 1;
return 1;
} else {
return 0;
}
@@ -590,22 +645,43 @@ class User_Model extends CI_Model {
// FUNCTION: object users()
// Returns a list of users with additional counts
function users() {
$this->db->select('(SELECT COUNT(*) FROM station_profile WHERE user_id = users.user_id) as stationcount');
$this->db->select('(SELECT COUNT(*) FROM station_logbooks WHERE user_id = users.user_id) as logbookcount');
$this->db->select('(SELECT COUNT(*) FROM ' . $this->config->item('table_name') . ' WHERE station_id IN (SELECT station_id from station_profile WHERE user_id = users.user_id)) as qsocount');
$this->db->select('
(SELECT COUNT(*) FROM ' . $this->config->item('table_name') . ' WHERE station_id IN (SELECT station_id FROM station_profile WHERE user_id = users.user_id)) as qsocount,
(SELECT MAX(COL_TIME_ON) FROM ' . $this->config->item('table_name') . ' WHERE station_id IN (SELECT station_id FROM station_profile WHERE user_id = users.user_id)) as lastqso
');
$this->db->select('users.*');
$this->db->from('users');
$result = $this->db->get();
function users($club = '') {
$sql=" SELECT COUNT(distinct sp.station_id) AS stationcount, count(distinct sl.logbook_id) AS logbookcount, count(distinct log.col_primary_key) AS qsocount,
MAX(COL_TIME_ON) AS lastqso,
u.*
FROM users u
LEFT OUTER JOIN station_profile sp ON (sp.user_id = u.user_id)
LEFT OUTER JOIN station_logbooks sl ON (sl.user_id = u.user_id)
LEFT OUTER JOIN ". $this->config->item('table_name') ." log on (log.station_id=sp.station_id)";
if ($this->config->item('special_callsign')) {
if ($club == 'is_club') {
$sql.=' WHERE clubstation=1';
} else {
$sql.=' WHERE clubstation!=1';
}
}
$sql.=" GROUP BY u.user_id";
$result = $this->db->query($sql);
if ($this->config->item('special_callsign')) {
if ($club == 'is_club') {
foreach ($result->result() as &$row) {
$row->lastoperator=$this->get_last_op($row->user_id,$row->lastqso);
}
} else {
foreach ($result->result() as &$row) {
$row->lastoperator='';
}
}
}
return $result;
}
function get_last_op($userid,$lastqso) {
$sql="SELECT log.COL_OPERATOR FROM ". $this->config->item('table_name') ." log INNER JOIN station_profile sp ON (log.station_id=sp.station_id) where sp.user_id=? AND col_time_on=? ORDER BY col_time_on DESC LIMIT 1";
$resu=$this->db->query($sql,array($userid,$lastqso));
return $resu->result()[0]->COL_OPERATOR ?? '';
}
// FUNCTION: array timezones()
// Returns a list of timezones
function timezones() {
@@ -741,6 +817,38 @@ class User_Model extends CI_Model {
}
}
function get_clubstations($user_id) {
$this->load->model('club_model');
$clubstations = $this->club_model->get_clubstations($user_id);
return $clubstations;
}
function convert($user_id, $clubstation) {
$clubstation_value = ($clubstation == true) ? 1 : 0;
$sql = "UPDATE users SET clubstation = ? WHERE user_id = ?;";
$this->db->trans_start();
if (!$this->db->query($sql, [$clubstation_value, $user_id])) {
$this->db->trans_rollback();
return false;
}
if ($clubstation) {
$delete_sql = "DELETE FROM club_permissions WHERE club_id = ?;";
if (!$this->db->query($delete_sql, [$user_id])) {
$this->db->trans_rollback();
return false;
}
}
$this->db->trans_complete();
return $this->db->trans_status();
}
}
?>

View File

@@ -66,6 +66,22 @@
} ?>><?= __("Callsign") . ": " ?><?php echo $station->station_callsign; ?> (<?php echo $station->station_profile_name; ?>)</option>
<?php } ?>
</select>
<?php
$show_operator_question = true;
if ($this->config->item('special_callsign') && $club_operators != false) {
$show_operator_question = false; ?>
<div class="small form-text text-muted"><?= __("Select the operator of the imported QSOs") ?></div>
<select name="club_operator" class="form-select mb-2 me-sm-2 w-50 w-lg-100">
<?php foreach ($club_operators as $operator) { ?>
<option value="<?php echo $operator->user_callsign; ?>"
<?php if ($operator->user_callsign == $this->session->userdata('cd_src_call')) {
echo ' selected="selected"';
} ?>>
<?php echo $operator->user_callsign; ?>
</option>
<?php } ?>
</select>
<?php } ?>
<div class="small form-text text-muted"><?= __("Add QSOs to Contest") ?></div>
<select name="contest" id="contest" class="form-select mb-2 me-sm-2 w-50 w-lg-100">
<option value="" selected><?= __("No Contest"); ?></option>
@@ -156,6 +172,7 @@
</div>
</div>
<?php if ($show_operator_question) { ?>
<div class="mb-3 row">
<div class="col-md-10">
<div class="form-check-inline">
@@ -164,6 +181,7 @@
</div>
</div>
</div>
<?php } ?>
<div class="mb-3 row">
<div class="col-md-10">

View File

@@ -1,83 +0,0 @@
<div class="container">
<br>
<?php if($this->session->flashdata('notice')) { ?>
<div class="alert alert-success" role="alert">
<?php echo $this->session->flashdata('notice'); ?>
</div>
<?php } ?>
<h2><?php echo $page_title; ?></h2>
<div class="card">
<div class="card-header">
<?= __("API Keys"); ?>
</div>
<div class="card-body">
<p class="card-text"><?= __("The Wavelog API (Application Programming Interface) lets third party systems access Wavelog in a controlled way. Access to the API is managed via API keys."); ?></p>
<p class="card-text"><?= __("You will need to generate an API key for each tool you wish to use (e.g. WLgate). Generate a read-write key if the application needs to send data to Wavelog. Generate a read-only key if the application only needs to obtain data from Wavelog."); ?></p>
<p class="card-text"><span class="badge text-bg-warning"><?= __("API URL"); ?></span> <?= __("The API URL for this Wavelog instance is"); ?>: <span class="api-url" id="apiUrl"><a target="_blank" href="<?php echo base_url(); ?>"><?php echo base_url(); ?></a></span><span data-bs-toggle="tooltip" title="<?= __("Copy to clipboard"); ?>" onClick='copyApiUrl()'><i class="copy-icon fas fa-copy"></i></span></p>
<p class="card-text"><span class="badge text-bg-info"><?= __("Info"); ?></span> <?= __("It's good practice to delete a key if you are no longer using the associated application."); ?></p>
<?php if ($api_keys->num_rows() > 0) { ?>
<table class="table table-striped">
<thead>
<tr>
<th scope="col"><?= __("API Key"); ?></th>
<th scope="col"><?= __("Description"); ?></th>
<th scope="col"><?= __("Last Used"); ?></th>
<th scope="col"><?= __("Permissions"); ?></th>
<th scope="col"><?= __("Status"); ?></th>
<th scope="col"><?= __("Actions"); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($api_keys->result() as $row) { ?>
<tr>
<td><i class="fas fa-key"></i> <span class="api-key" id="<?php echo $row->key; ?>"><?php echo $row->key; ?></span> <span data-bs-toggle="tooltip" title="<?= __("Copy to clipboard"); ?>" onclick='copyApiKey("<?php echo $row->key; ?>")'><i class="copy-icon fas fa-copy"></span></td>
<td><?php echo $row->description; ?></td>
<td><?php echo $row->last_used; ?></td>
<td>
<?php
if($row->rights == "rw") {
echo "<span class=\"badge bg-warning\">" . __("Read & Write") . "</span>";
} elseif($row->rights == "r") {
echo "<span class=\"badge bg-success\">" . __("Read-Only") . "</span>";
} else {
echo "<span class=\"badge bg-dark\">" . __("Unknown") . "</span>";
}
?>
</td>
<td><span class="badge rounded-pill text-bg-success"><?php echo ucfirst($row->status); ?></span></td>
<td>
<a href="<?php echo site_url('api/edit'); ?>/<?php echo $row->key; ?>" class="btn btn-outline-primary btn-sm"><?= __("Edit"); ?></a>
<a href="<?php echo site_url('api/auth/'.$row->key); ?>" target="_blank" class="btn btn-primary btn-sm"><?= __("Test"); ?></a>
<a href="<?php echo site_url('api/delete/'.$row->key); ?>" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure you want delete API Key <?php echo $row->key; ?>?');"><?= __("Delete"); ?></a>
</td>
</tr>
<?php } ?>
</table>
<?php } else { ?>
<p><?= __("You have no API Keys."); ?></p>
<?php } ?>
<p>
<a href="<?php echo site_url('api/generate/rw'); ?>" class="btn btn-primary "><i class="fas fa-plus"></i> <?= __("Create a read & write key"); ?></a>
<a href="<?php echo site_url('api/generate/r'); ?>" class="btn btn-primary"><i class="fas fa-plus"></i> <?= __("Create a read-only key"); ?></a>
</p>
</div>
</div>
</div>

View File

@@ -0,0 +1,97 @@
<div class="container">
<br>
<?php $this->load->view('layout/messages'); ?>
<h2><?php echo $page_title; ?></h2>
<div class="card">
<div class="card-header">
<?= __("API Keys"); ?>
</div>
<div class="card-body">
<p class="card-text"><?= __("The Wavelog API (Application Programming Interface) lets third party systems access Wavelog in a controlled way. Access to the API is managed via API keys."); ?></p>
<p class="card-text"><?= __("You will need to generate an API key for each tool you wish to use (e.g. WLgate). Generate a read-write key if the application needs to send data to Wavelog. Generate a read-only key if the application only needs to obtain data from Wavelog."); ?></p>
<p class="card-text"><span class="badge text-bg-warning"><?= __("API URL"); ?></span> <?= __("The API URL for this Wavelog instance is"); ?>: <span class="api-url" id="apiUrl"><code class="ms-3 me-3"><?php echo base_url(); ?></code></span><span data-bs-toggle="tooltip" title="<?= __("Copy to clipboard"); ?>" onClick='copyApiUrl()'><i class="copy-icon fas fa-copy"></i></span></p>
<p class="card-text"><span class="badge text-bg-info"><?= __("Info"); ?></span> <?= __("It's good practice to delete a key if you are no longer using the associated application."); ?></p>
<?php if ($clubmode) { ?>
<p class="card-text"><span class="badge text-bg-danger"><?= __("Important"); ?></span> <?= __("On Clubstations the API Keys are personal and not shared. Clubstation users can only see their own keys."); ?></p>
<?php } ?>
<?php if ($api_keys->num_rows() > 0) { ?>
<table class="table table-striped">
<thead>
<tr>
<th scope="col"><?= __("API Key"); ?></th>
<th scope="col"><?= __("Description"); ?></th>
<th scope="col"><?= __("Last Used"); ?></th>
<?php if ($clubmode) { ?>
<th scope="col"><?= __("Created By"); ?></th>
<?php } ?>
<th scope="col"><?= __("Permissions"); ?></th>
<th scope="col"><?= __("Status"); ?></th>
<th scope="col"><?= __("Actions"); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($api_keys->result() as $row) { ?>
<tr>
<?php if ($clubmode && $row->user_callsign !== $this->session->userdata('cd_src_call')) {
$api_key = substr($row->key, 0, 2) . str_repeat('*', strlen($row->key) - 6) . substr($row->key, -4);
$masked = true;
} else {
$api_key = $row->key;
$masked = false;
} ?>
<td>
<i class="fas fa-key"></i> <span class="api-key" id="<?php echo $api_key; ?>"><?php echo $api_key; ?></span>
<?php if (!$masked) { ?>
<span data-bs-toggle="tooltip" title="<?= __("Copy to clipboard"); ?>" onclick='copyApiKey("<?php echo $api_key; ?>")'><i class="copy-icon fas fa-copy"></i></span>
<?php } ?>
</td>
<td><?php echo $row->description; ?></td>
<td><?php echo $row->last_used; ?></td>
<?php if ($clubmode) { ?>
<td><?php echo $row->user_callsign; ?></td>
<?php } ?>
<td>
<?php if ($row->rights == "rw") {
echo "<span class=\"badge bg-warning\">" . __("Read & Write") . "</span>";
} elseif ($row->rights == "r") {
echo "<span class=\"badge bg-success\">" . __("Read-Only") . "</span>";
} else {
echo "<span class=\"badge bg-dark\">" . __("Unknown") . "</span>";
} ?>
</td>
<td><span class="badge rounded-pill text-bg-success"><?php echo ucfirst($row->status); ?></span></td>
<td>
<?php if (!$masked) { ?>
<a href="<?php echo site_url('api/edit'); ?>/<?php echo $api_key; ?>" class="btn btn-outline-primary btn-sm"><?= __("Edit"); ?></a>
<a href="<?php echo site_url('api/auth/' . $api_key); ?>" target="_blank" class="btn btn-primary btn-sm"><?= __("Test"); ?></a>
<?php
$cfnm_delete = sprintf(__("Are you sure you want delete the API Key %s?"), '&quot;'.($row->description ?? '<noname>').'&quot;');
?>
<a href="<?php echo site_url('api/delete/' . $api_key); ?>" class="btn btn-danger btn-sm" onclick="return confirm('<?php echo $cfnm_delete; ?>');"><?= __("Delete"); ?></a>
<?php } ?>
</td>
</tr>
<?php } ?>
</table>
<?php } else { ?>
<p><?= __("You have no API Keys."); ?></p>
<?php } ?>
<p>
<a href="<?php echo site_url('api/generate/rw'); ?>" class="btn btn-primary "><i class="fas fa-plus"></i> <?= __("Create a read & write key"); ?></a>
<a href="<?php echo site_url('api/generate/r'); ?>" class="btn btn-primary"><i class="fas fa-plus"></i> <?= __("Create a read-only key"); ?></a>
</p>
</div>
</div>
</div>

View File

@@ -0,0 +1,20 @@
<div class="modal fade bg-black bg-opacity-50" id="clubswitchModal" tabindex="-1" aria-labelledby="clubswitchLabel" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog modal-dialog-centered modal-md">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="clubswitchLabel"><?= __("Switch to a Clubstation") ?></h5>
</div>
<form action="<?= site_url('user/impersonate'); ?>" method="post">
<div class="modal-body" style="text-align: center !important;">
<p><?= sprintf(__("Are you sure you want to switch to %s?"), $club_callsign); ?></p>
<input type="hidden" name="hash" value="<?php echo $impersonate_hash; ?>">
<input type="hidden" name="clubswitch" value="1">
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-success"><?= __("Yes, switch over!"); ?></button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= __("Cancel"); ?></button>
</div>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,342 @@
<div class="container">
<br>
<h2><?= sprintf(__("Club Permissions for %s"), $club->user_callsign); ?></h2>
<!-- <a class="btn btn-primary" href="<?= site_url('user'); ?>"><i class="fas fa-arrow-left"></i> <?= __("Go back"); ?></a> -->
<?php $this->load->view('layout/messages'); ?>
<div class="card mt-3">
<div class="card-header">
<?= __("Club Permissions"); ?>
</div>
<div class="card-body">
<p><?= __("In order for users to log QSOs with this club/special callsign, they need appropriate authorizations. Add users to the table below and set the appropriate permission."); ?></p>
<button class="btn btn-info" data-bs-toggle="modal" data-bs-target="#permissionsModal"><i class="fas fa-info-circle"></i> <?= __("See available Permissions"); ?></button>
<div class="modal fade bg-black bg-opacity-50" id="permissionsModal" aria-labelledby="permissionsLabel" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="permissionsLabel"><?= __("Available Permissions") ?></h5>
</div>
<div class="modal-body">
<div class="table-responsive">
<table class="table">
<thead class="table">
<tr>
<th><?= __("Action"); ?></th>
<th><?php echo $permissions[3]; ?></th>
<th><?php echo $permissions[9]; ?></th>
</tr>
</thead>
<tbody>
<tr>
<td><?= __("Log QSOs via Web GUI (live and post)"); ?></td>
<td><i class="fas fa-check text-success"></i></td>
<td><i class="fas fa-check text-success"></i></td>
</tr>
<tr class="empty-row">
<td colspan="3"></td>
</tr>
<tr>
<td><?= __("Log QSOs via API"); ?></td>
<td><i class="fas fa-check text-success"></i></td>
<td><i class="fas fa-check text-success"></i></td>
</tr>
<tr class="empty-row">
<td colspan="3"></td>
</tr>
<tr>
<td><?= __("Edit a QSO"); ?></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="ps-5"><i class="fas fa-arrow-right me-3"></i><?= __("QSO was done by the operator"); ?></td>
<td><i class="fas fa-check text-success"></i></td>
<td><i class="fas fa-check text-success"></i></td>
</tr>
<tr>
<td class="ps-5"><i class="fas fa-arrow-right me-3"></i><?= __("QSO was done by another operator"); ?></td>
<td><i class="fas fa-times text-danger"></i></td>
<td><i class="fas fa-check text-success"></i></td>
</tr>
<tr class="empty-row">
<td colspan="3"></td>
</tr>
<tr>
<td><?= __("Delete a QSO"); ?></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="ps-5"><i class="fas fa-arrow-right me-3"></i><?= __("QSO was done by the operator"); ?></td>
<td><i class="fas fa-check text-success"></i></td>
<td><i class="fas fa-check text-success"></i></td>
</tr>
<tr>
<td class="ps-5"><i class="fas fa-arrow-right me-3"></i><?= __("QSO was done by another operator"); ?></td>
<td><i class="fas fa-times text-danger"></i></td>
<td><i class="fas fa-check text-success"></i></td>
</tr>
<tr class="empty-row">
<td colspan="3"></td>
</tr>
<tr>
<td><?= __("Manage Stationsetup (edit/create logbooks and locations)"); ?></td>
<td><i class="fas fa-times text-danger"></i></td>
<td><i class="fas fa-check text-success"></i></td>
</tr>
<tr class="empty-row">
<td colspan="3"></td>
</tr>
<tr>
<td><?= __("Manage Third-Party services"); ?></td>
<td><i class="fas fa-times text-danger"></i></td>
<td><i class="fas fa-check text-success"></i></td>
</tr>
<tr class="empty-row">
<td colspan="3"></td>
</tr>
<tr>
<td><?= __("Import QSO per ADIF"); ?></td>
<td><i class="fas fa-times text-danger"></i></td>
<td><i class="fas fa-check text-success"></i></td>
</tr>
<tr class="empty-row">
<td colspan="3"></td>
</tr>
<tr>
<td><?= __("Export QSO per ADIF"); ?></td>
<td><i class="fas fa-times text-danger"></i></td>
<td><i class="fas fa-check text-success"></i></td>
</tr>
<tr class="empty-row">
<td colspan="3"></td>
</tr>
<tr>
<td><?= __("User Management"); ?></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="ps-5"><i class="fas fa-arrow-right me-3"></i><?= __("Can create new users in Wavelog"); ?></td>
<td><i class="fas fa-times text-danger"></i></td>
<td><i class="fas fa-times text-danger"></i></td>
</tr>
<tr>
<td class="ps-5"><i class="fas fa-arrow-right me-3"></i><?= __("Can edit other users in Wavelog"); ?></td>
<td><i class="fas fa-times text-danger"></i></td>
<td><i class="fas fa-times text-danger"></i></td>
</tr>
<tr>
<td class="ps-5"><i class="fas fa-arrow-right me-3"></i><?= __("Can edit Club permissions and add/remove users"); ?></td>
<td><i class="fas fa-times text-danger"></i></td>
<td><i class="fas fa-check text-success"></i></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= __("Close"); ?></button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card mt-3">
<div class="card-header">
<?= __("Users with Permissions"); ?>
</div>
<div class="card-body">
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addUserModal">
<i class="fas fa-plus"></i> <?= __("Add User"); ?>
</button>
<div class="modal fade" id="addUserModal" tabindex="-1" aria-labelledby="addUserLabel" aria-hidden="true" data-bs-backdrop="static">
<div class="modal-dialog modal-dialog-centered" style="max-width: 700px;">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addUserLabel"><?= __("Add new User to Club"); ?></h5>
</div>
<form action="<?= site_url('club/alter_member'); ?>" method="post">
<div class="modal-body">
<input type="hidden" name="club_id" value="<?php echo $club->user_id; ?>">
<p>
<?= sprintf(__("You can only add users to the %s Clubstation if they already exist on this Wavelog Server."), $club->user_callsign); ?>
<?= __("If they don't exist, please ask your Wavelog Administrator to create an account for them."); ?><br><br>
<?= __("Search for the user by their callsign or first/lastname and select the permission level."); ?>
</p>
<div class="table-responsive">
<table class="table">
<thead class="table">
<tr>
<th class="text-center"><?= __("User (Callsign or Name)"); ?></th>
<th class="text-center"><?= __("Permission"); ?></th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input class="form-control" id="user_id" name="user_id" required />
<small class="form-text text-muted"><?= __("Type at least 2 characters."); ?></small>
</td>
<td>
<select class="form-select" id="permission" name="permission" required>
<option value="3"><?php echo $permissions[3]; ?></option>
<option value="9"><?php echo $permissions[9]; ?></option>
</select>
<div class="mt-2 form-check d-flex justify-content-end text-muted">
<input class="form-check-input me-2" type="checkbox" id="notify_user" name="notify_user">
<input type="hidden" name="notify_message" value="new_member">
<label class="form-check-label" for="notify_user">
<?= __("Notify the user via email"); ?>
</label>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-success ld-ext-right"><?= __("Save"); ?><div class="ld ld-ring ld-spin"></div></button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= __("Cancel"); ?></button>
</div>
</form>
</div>
</div>
</div>
<?php if (empty($club_members)) { ?>
<div class="text-center">
<h5><?= __("No users currently have access to this club station."); ?></h5>
</div>
<?php } else { ?>
<div class="table-responsive mt-3">
<table class="table table-striped table-hover" id="clubuserstable">
<thead>
<tr>
<th><?= __("Firstname"); ?></th>
<th><?= __("Lastname"); ?></th>
<th><?= __("Callsign"); ?></th>
<th><?= __("Username"); ?></th>
<th><?= __("E-Mail"); ?></th>
<th><?= __("Permission"); ?></th>
<th><?= __("Actions"); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($club_members as $member) { ?>
<tr>
<td style="text-align: center; vertical-align: middle;"><?php echo $member->user_firstname; ?></td>
<td style="text-align: center; vertical-align: middle;"><?php echo $member->user_lastname; ?></td>
<td style="text-align: center; vertical-align: middle;"><?php echo $member->user_callsign; ?></td>
<td style="text-align: center; vertical-align: middle;"><?php echo $member->user_name; ?></td>
<td style="text-align: center; vertical-align: middle;"><?php echo '<a href="mailto:' . $member->user_email . '">' . $member->user_email . '</a>'; ?></td>
<td style="text-align: center; vertical-align: middle;">
<?php if ($member->p_level == 3) { ?>
<span class="badge bg-info"><?php echo $permissions[3]; ?></span>
<?php } else if ($member->p_level == 9) { ?>
<span class="badge bg-warning"><?php echo $permissions[9]; ?></span>
<?php } ?>
<?php if ($member->user_type == 99) { ?>
<span class="badge bg-danger"><?= __("Wavelog Administrator"); ?></span>
<?php } ?>
</td>
<td style="text-align: center; vertical-align: middle;">
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#editModal_<?php echo $member->user_id; ?>"><i class="fas fa-edit"></i> <?= __("Edit"); ?></button>
<div class="modal fade" id="editModal_<?php echo $member->user_id; ?>" tabindex="-1" aria-labelledby="editLabel_<?php echo $member->user_id; ?>" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog modal-dialog-centered" style="max-width: 700px;">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editLabel_<?php echo $member->user_id; ?>"><?= __("Edit User"); ?></h5>
</div>
<form action="<?= site_url('club/alter_member'); ?>" method="post">
<div class="modal-body">
<input type="hidden" name="club_id" value="<?php echo $club->user_id; ?>">
<input type="hidden" name="user_id" value="<?php echo $member->user_id; ?>">
<p>
<?= __("You can modify the users permission level for this Clubstation."); ?>
</p>
<div class="table-responsive">
<table class="table">
<thead class="table">
<tr>
<th class="text-center"><?= __("User Callsign"); ?></th>
<th class="text-center"><?= __("Permission"); ?></th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-center pt-3">
<p><b><?php echo $member->user_callsign; ?> - <?php echo $member->user_firstname . ' ' . $member->user_lastname; ?></b></p>
</td>
<td>
<select class="form-select" id="permission" name="permission" required>
<option value="3" <?php if ($member->p_level == 3) { echo 'selected'; } ?>><?php echo $permissions[3]; ?></option>
<option value="9" <?php if ($member->p_level == 9) { echo 'selected'; } ?>><?php echo $permissions[9]; ?></option>
</select>
<div class="mt-2 form-check d-flex justify-content-end text-muted">
<input class="form-check-input me-2" type="checkbox" id="notify_user" name="notify_user">
<input type="hidden" name="notify_message" value="modified_member">
<label class="form-check-label" for="notify_user">
<?= __("Notify the user via email about the change"); ?>
</label>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-success ld-ext-right"><?= __("Save"); ?><div class="ld ld-ring ld-spin"></div></button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= __("Cancel"); ?></button>
</div>
</form>
</div>
</div>
</div>
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#deleteModal_<?php echo $member->user_id; ?>"><i class="fas fa-trash"></i> <?= __("Delete"); ?></button>
<div class="modal fade bg-black bg-opacity-50" id="deleteModal_<?php echo $member->user_id; ?>" tabindex="-1" aria-labelledby="deleteLabel_<?php echo $member->user_id; ?>" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog modal-dialog-centered modal-md">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteLabel_<?php echo $member->user_id; ?>"><?= __("Delete User") ?></h5>
</div>
<form action="<?= site_url('club/delete_member'); ?>" method="post">
<div class="modal-body" style="text-align: center !important;">
<input type="hidden" name="club_id" value="<?php echo $club->user_id; ?>">
<input type="hidden" name="user_id" value="<?php echo $member->user_id; ?>">
<p><?= __("Are you sure you want to delete this user from the club?"); ?></p>
<div class="mb-3">
<p>
<?= sprintf(__("Callsign: %s"), $member->user_callsign); ?><br>
<?= sprintf(__("Role: %s"), $permissions[$member->p_level]); ?>
</p>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-danger"><?= __("Delete"); ?></button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= __("Cancel"); ?></button>
</div>
</form>
</div>
</div>
</div>
</td>
</tr>
<?php } ?>
</tbody>
</table>
<?php } ?>
</div>
</div>
</div>
</div>

View File

@@ -142,7 +142,7 @@ function getDistance($distance) {
</div>
<?php } ?>
<?php if($this->optionslib->get_option('dashboard_banner') != "false") { ?>
<?php if($this->optionslib->get_option('dashboard_banner') != "false" && $this->session->userdata('clubstation') == 0) { ?>
<?php if($todays_qsos >= 1) { ?>
<div class="alert alert-success" role="alert" style="margin-top: 1rem;">
<?= sprintf(

View File

@@ -0,0 +1,17 @@
<?php
$message['subject'] = sprintf(__("Your permission level for Clubstation %s has been changed"), $club_callsign);
$message['body'] = sprintf(__("Dear %s,
Your permission level for Clubstation %s has been changed. You can access this callsign through your account at %s."), $user_callsign, $club_callsign, base_url()) . "\n\n" .
sprintf(__("Your new permission level is: %s"), $permission_level) . "\n\n" .
__("Log in and check it out!
Regards,
Wavelog");
echo json_encode($message);

View File

@@ -0,0 +1,17 @@
<?php
$message['subject'] = sprintf(__("New %s Membership on Wavelog!"), $club_callsign);
$message['body'] = sprintf(__("Dear %s
You have been added to the Clubstation %s. You can now access this callsign through your account on %s."), $user_callsign, $club_callsign, base_url()) . "\n\n" .
sprintf(__("Your permission level is: %s"), $permission_level) . "\n\n" .
__("Log in and check it out!
Regards,
Wavelog");
echo json_encode($message);

View File

@@ -153,7 +153,7 @@ if($this->session->userdata('user_id') != null) {
<!-- Version Dialog END -->
<!-- SPECIAL CALLSIGN OPERATOR FEATURE -->
<?php if ($this->config->item('special_callsign') == true && $this->uri->segment(1) == "dashboard") { ?>
<?php if ($this->config->item('special_callsign') && $this->uri->segment(1) == "dashboard" && $this->session->userdata('clubstation') == 1) { ?>
<script type="text/javascript" src="<?php echo base_url() ;?>assets/js/sections/operator.js"></script>
<script>
<?php
@@ -175,6 +175,43 @@ if($this->session->userdata('user_id') != null) {
<?php } ?>
</script>
<?php } ?>
<script>
function clubswitch_modal(club_id, club_callsign) {
$.ajax({
url: base_url + 'index.php/club/switch_modal',
type: 'POST',
data: {
club_id: club_id,
club_callsign: club_callsign
},
success: function(response) {
$('#clubswitchModal-container').html(response);
$('#clubswitchModal').modal('show');
},
error: function() {
alert('<?= __("Failed to load the modal. Please try again."); ?>');
}
});
$(window).on('blur', function() {
$('#clubswitchModal').modal('hide');
});
}
function stopImpersonate_modal() {
$.ajax({
url: base_url + 'index.php/user/stop_impersonate_modal',
success: function(response) {
$('#stopImpersonateModal-container').html(response);
$('#stopImpersonateModal').modal('show');
},
error: function() {
alert('<?= __("Failed to load the modal. Please try again."); ?>');
}
});
$(window).on('blur', function() {
$('#stopImpersonateModal').modal('hide');
});
}
</script>
<!-- SPECIAL CALLSIGN OPERATOR FEATURE END -->
<script>
@@ -2898,10 +2935,6 @@ function viewEqsl(picture, callsign) {
<?php } ?>
<?php } ?>
<?php if ($this->uri->segment(1) == "user") { ?>
<script src="<?php echo base_url() ;?>assets/js/sections/user.js"></script>
<?php } ?>
<?php
if (isset($scripts) && is_array($scripts)){
foreach($scripts as $script){

View File

@@ -83,10 +83,13 @@
<div class="container">
<a class="navbar-brand" href="<?php echo site_url(); ?>"><img class="headerLogo" src="<?php echo base_url(); ?>assets/logo/<?php echo $this->optionslib->get_logo('header_logo'); ?>.png" alt="Logo" /></a>
<?php if (ENVIRONMENT == "development") { ?>
<span class="badge text-bg-danger"><?= __("Developer Mode"); ?></span>
<span class="badge text-bg-danger me-1"><?= __("Developer Mode"); ?></span>
<?php } ?>
<?php if (ENVIRONMENT == "maintenance") { ?>
<span class="badge text-bg-info"><?= __("Maintenance Mode"); ?></span>
<span class="badge text-bg-info me-1"><?= __("Maintenance Mode"); ?></span>
<?php } ?>
<?php if ($this->session->userdata('clubstation') == '1' && $this->session->userdata('impersonate') == '1') { ?>
<span class="badge text-bg-primary me-1"><?= __("Clubstation"); ?></span>
<?php } ?>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button>
@@ -117,10 +120,12 @@
<li><a class="dropdown-item" href="<?php echo site_url('qso?manual=1'); ?>" title="Log QSO made in the past"><i class="fas fa-list"></i> <?= __("Post QSO"); ?></a></li>
<div class="dropdown-divider"></div>
<li><a class="dropdown-item" href="<?php echo site_url('simplefle'); ?>" title="Simple Fast Log Entry"><i class="fas fa-list"></i> <?= __("Simple Fast Log Entry"); ?></a></li>
<?php if (clubaccess_check(99)) { ?> <!-- Club Access Check -->
<div class="dropdown-divider"></div>
<li><a class="dropdown-item" href="<?php echo site_url('contesting?manual=0'); ?>" title="Live contest QSOs"><i class="fas fa-list"></i> <?= __("Live Contest Logging"); ?></a></li>
<div class="dropdown-divider"></div>
<li><a class="dropdown-item" href="<?php echo site_url('contesting?manual=1'); ?>" title="Post contest QSOs"><i class="fas fa-list"></i> <?= __("Post Contest Logging"); ?></a></li>
<?php } ?>
</ul>
</li>
@@ -369,15 +374,23 @@
<!-- Logged in As -->
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#"><i class="fas fa-user"></i> <?php echo str_replace("0","&Oslash;", strtoupper($this->session->userdata('user_callsign'))); ?></a>
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#">
<?php if ($this->session->userdata('clubstation') == 1) {
echo '<i class="fas fa-users"></i> '
. '<b>' . str_replace("0","&Oslash;", strtoupper($this->session->userdata('user_callsign'))) . '</b>'
. ' <br><small>'
. sprintf(_pgettext("Operator: Callsign", "Op: %s"), str_replace("0","&Oslash;", strtoupper($this->session->userdata('operator_callsign'))))
. '</small>';
} else {
echo '<i class="fas fa-user"></i> ' . str_replace("0","&Oslash;", strtoupper($this->session->userdata('user_callsign')));
} ?>
</a>
<ul class="dropdown-menu dropdown-menu-right header-dropdown">
<?php
if (!$this->config->item('special_callsign') ||
$this->session->userdata('user_type') == '99' ||
($this->config->item('special_callsign') && !$this->config->item('sc_hide_usermenu'))) { ?>
<?php if (clubaccess_check(9)) { ?> <!-- Club Access Check -->
<li><a class="dropdown-item" href="<?php echo site_url('user/edit') . "/" . $this->session->userdata('user_id'); ?>" title="Account"><i class="fas fa-user"></i> <?= __("Account"); ?></a></li>
<?php } ?>
<?php
$quickswitch_enabled = ($this->user_options_model->get_options('header_menu', array('option_name' => 'locations_quickswitch'))->row()->option_value ?? 'false');
if ($quickswitch_enabled != 'true') {
@@ -386,6 +399,28 @@
<?php } ?>
<li><a class="dropdown-item" href="<?php echo site_url('band'); ?>" title="Manage Bands"><i class="fas fa-cog"></i> <?= __("Bands"); ?></a></li>
<?php if ($this->config->item('special_callsign') && $this->session->userdata('clubstation') == 0) { ?>
<div class="dropdown-divider"></div>
<?php if (!empty($this->session->userdata('available_clubstations'))) { ?>
<li><a class="dropdown-item disabled"><?= __("Switch to Clubstation:"); ?></a></li>
<?php foreach ($this->session->userdata('available_clubstations') as $clubstation) { ?>
<li>
<div class="btn-group w-100" role="group">
<button class="dropdown-item text-start" style="flex: 1;" title="<?= sprintf(__("Switch to %s"), $clubstation->user_callsign); ?>" onclick="clubswitch_modal('<?php echo $clubstation->user_id; ?>', '<?php echo $clubstation->user_callsign; ?>')">
<i class="fas fa-exchange-alt"></i> <?php echo $clubstation->user_callsign; ?>
</button>
<?php if ($clubstation->p_level >= 9 || $this->session->userdata('user_type') == 99) { ?>
<a class="dropdown-item text-end" style="flex: 0 0 50px;" title="<?= sprintf(_pgettext("Managing a Club Callsign", "Manage %s"), $clubstation->user_callsign); ?>" href="<?php echo site_url('club/permissions/' . $clubstation->user_id); ?>">
<i class="fas fa-cogs"></i>
</a>
<?php } ?>
</div>
</li>
<?php } ?>
<?php } else { ?>
<li><a class="dropdown-item disabled"><?= __("No Clubstations available"); ?></a></li>
<?php } ?>
<?php } ?>
<div class="dropdown-divider"></div>
@@ -438,18 +473,28 @@
</ul>
</li>
<div class="dropdown-divider"></div>
<li><a class="dropdown-item" href="<?php echo site_url('api/help'); ?>" title="Manage API keys"><i class="fas fa-key"></i> <?= __("API Keys"); ?></a></li>
<?php } ?> <!-- End of Clubaccess check -->
<li><a class="dropdown-item" href="<?php echo site_url('api'); ?>" title="Manage API keys"><i class="fas fa-key"></i> <?= __("API Keys"); ?></a></li>
<li><a class="dropdown-item" href="<?php echo site_url('radio'); ?>" title="Interface with one or more radios"><i class="fas fa-broadcast-tower"></i> <?= __("Hardware Interfaces"); ?></a></li>
<div class="dropdown-divider"></div>
<li><a class="dropdown-item" href="javascript:displayVersionDialog();" title="Version Information"><i class="fas fa-star"></i> <?= __("Version Info"); ?></a></li>
<li><a class="dropdown-item" target="_blank" href="https://github.com/wavelog/wavelog/wiki" title="Help"><i class="fas fa-question"></i> <?= __("Help"); ?></a></li>
<li><a class="dropdown-item" target="_blank" href="https://github.com/wavelog/wavelog/discussions" title="Forum"><i class="far fa-comment-dots"></i> <?= __("Forum"); ?></a></li>
<div class="dropdown-divider"></div>
<?php if ($this->session->userdata('impersonate') == 1) { ?>
<li>
<button class="dropdown-item" style="flex: 1;" title="<?= sprintf(__("Stop impersonate and switch back to %s"), $this->session->userdata('cd_src_call') ?? ''); ?>" onclick="stopImpersonate_modal()">
<i class="fas fa-exchange-alt"></i> <?= sprintf(__("Switch back to %s"), $this->session->userdata('cd_src_call') ?? ''); ?>
</button>
</li>
<?php } ?>
<li><a class="dropdown-item" href="<?php echo site_url('user/logout'); ?>" title="Logout"><i class="fas fa-sign-out-alt"></i> <?= __("Logout"); ?></a></li>
</ul>
</li>
<?php
if ($quickswitch_enabled == 'true') { ?>
if (($quickswitch_enabled ?? '') == 'true') { ?>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#"><i class="fas fa-map-marker-alt"></i> | <i class="fas fa-book"></i></a>
<ul class="dropdown-menu dropdown-menu-right header-dropdown">
@@ -547,7 +592,9 @@
</ul>
</div>
</div>
</nav>
</nav>
<div id="clubswitchModal-container"></div>
<div id="stopImpersonateModal-container"></div>
<script>
let headerMenu = document.getElementById('header-menu');
let dropdowns = document.querySelectorAll('.dropdown-toggle');

View File

@@ -473,7 +473,7 @@ $options = json_decode($options);
</div>
</div>
</div>
<?php if(clubaccess_check(9)) { ?>
<div class="actionbody collapse">
<script>
var lang_filter_actions_delete_warning = '<?= __("Warning! Are you sure you want to delete the marked QSO(s)?"); ?>';
@@ -498,6 +498,7 @@ $options = json_decode($options);
<button type="button" class="btn btn-sm btn-info me-1" id="qslSlideshow"><?= __("QSL Slideshow"); ?></button>
</div>
</div>
<?php } ?>
<div class="quickfilterbody collapse">
<div class="mb-2 btn-group">
<span class="h6 me-1"><?= __("Quicksearch with selected: "); ?></span>
@@ -556,9 +557,11 @@ $options = json_decode($options);
<button type="button" class="btn btn-sm btn-primary me-1 lba_buttons flex-grow-0 mb-2" data-bs-toggle="collapse" data-bs-target=".filterbody" style="white-space: nowrap;">
<i class="fas fa-filter"></i> <?= __("Filters"); ?>
</button>
<?php if(clubaccess_check(9)) { ?>
<button type="button" class="btn btn-sm btn-success me-1 lba_buttons flex-grow-0 mb-2" data-bs-toggle="collapse" data-bs-target=".actionbody" style="white-space: nowrap;">
<i class="fas fa-tasks"></i> <?= __("Actions"); ?>
</button>
<?php } ?>
<label for="qsoResults" class="me-2" style="white-space: nowrap;"><?= __("# Results"); ?></label>
<select id="qsoResults" name="qsoresults" class="form-select form-select-sm me-2 w-auto">
<option value="250">250</option>
@@ -585,9 +588,11 @@ $options = json_decode($options);
<button type="button" class="btn btn-sm btn-primary me-1 ld-ext-right flex-grow-0 mb-2" id="invalidButton" style="white-space: nowrap;">
<i class="fa fa-exclamation-triangle"></i> <?= __("Invalid"); ?><div class="ld ld-ring ld-spin"></div>
</button>
<?php if(clubaccess_check(9)) { ?>
<button type="button" class="btn btn-sm btn-primary me-1 ld-ext-right flex-grow-0 mb-2" id="editButton" style="white-space: nowrap;">
<i class="fas fa-edit"></i> <?= __("Edit"); ?><div class="ld ld-ring ld-spin"></div>
</button>
<?php } ?>
<div class="btn-group me-1" role="group">
<button type="button" class="btn btn-sm btn-primary ld-ext-right flex-grow-0 mb-2" id="mapButton" onclick="mapQsos(this.form);" style="white-space: nowrap;">
<i class="fas fa-globe-europe"></i> <?= __("Map"); ?><div class="ld ld-ring ld-spin"></div>
@@ -597,12 +602,14 @@ $options = json_decode($options);
<li><button type="button" class="dropdown-item" onclick="mapGlobeQsos(this.form);" id="mapGlobeButton"><?= __("Globe map"); ?></button></li>
</ul>
</div>
<?php if(clubaccess_check(9)) { ?>
<button type="options" class="btn btn-sm btn-primary me-1 flex-grow-0 mb-2" id="optionButton" aria-label="<?= __("Options"); ?>" style="white-space: nowrap;" data-bs-toggle="tooltip" data-bs-placement="top" title="<?= __("Options"); ?>">
<i class="fas fa-cog"></i>
</button>
<button type="button" class="btn btn-sm btn-danger me-1 flex-grow-0 mb-2" id="deleteQsos" style="white-space: nowrap;" aria-label="<?= __("Delete"); ?>" data-bs-toggle="tooltip" data-bs-placement="top" title="<?= __("Delete"); ?>">
<i class="fas fa-trash-alt"></i>
</button>
<button type="button" class="btn btn-sm btn-danger me-1 flex-grow-0 mb-2" id="deleteQsos" style="white-space: nowrap;" aria-label="<?= __("Delete"); ?>" data-bs-toggle="tooltip" data-bs-placement="top" title="<?= __("Delete"); ?>">
<i class="fas fa-trash-alt"></i>
</button>
<?php } ?>
<button type="reset" class="btn btn-sm btn-danger me-1 flex-grow-0 mb-2" id="resetButton" style="white-space: nowrap;">
<i class="fas fa-undo"></i> <?= __("Reset"); ?>
</button>

View File

@@ -34,7 +34,9 @@
<a class="nav-item nav-link" id="nav-satellites-tab" data-bs-toggle="tab" href="#nav-satellites" role="tab" aria-controls="nav-awards" aria-selected="true"><?= __("Sats"); ?></a>
<a class="nav-item nav-link" id="nav-awards-tab" data-bs-toggle="tab" href="#nav-awards" role="tab" aria-controls="nav-awards" aria-selected="true"><?= __("Awards"); ?></a>
<a class="nav-item nav-link" id="nav-qso-notes-tab" data-bs-toggle="tab" href="#nav-qso-notes" role="tab" aria-controls="nav-qso-notes" aria-selected="false"><?= __("Notes"); ?></a>
<?php if (clubaccess_check(9)) { ?>
<a class="nav-item nav-link" id="nav-qsl-edit-tab" data-bs-toggle="tab" href="#nav-qsl-edit" role="tab" aria-controls="nav-qsl-edit" aria-selected="false"><?= __("QSL"); ?></a>
<?php } ?>
<a class="nav-item nav-link" id="nav-station-tab" data-bs-toggle="tab" href="#nav-station" role="tab" aria-controls="nav-station" aria-selected="false"><?= __("Station"); ?></a>
<a class="nav-item nav-link" id="nav-contest-tab" data-bs-toggle="tab" href="#nav-contest" role="tab" aria-controls="nav-contest" aria-selected="false"><?= __("Contest"); ?></a>
</div>
@@ -604,10 +606,12 @@
</select>
</div>
<?php if(clubaccess_check(9)) { ?>
<div class="mb-3">
<label for="operatorCallsign"><?= __("Operator Callsign"); ?></label>
<input type="text" id="operatorCallsign" class="form-control uppercase" name="operator_callsign" value="<?php echo $qso->COL_OPERATOR; ?>" />
</div>
<?php } ?>
</div>

View File

@@ -355,10 +355,12 @@
<small id="powerHelp" class="form-text text-muted"><?= __("Give power value in Watts. Include only numbers in the input."); ?></small>
</div>
<?php if (clubaccess_check(9)) { ?>
<div class="mb-3">
<label for="operator_callsign"><?= __("Operator Callsign"); ?></label>
<input type="text" class="form-control" id="operator_callsign" name="operator_callsign" value="<?php if ($this->session->userdata('operator_callsign')) { echo $this->session->userdata('operator_callsign'); } ?>" />
</div>
<?php } ?>
</div>

View File

@@ -413,36 +413,41 @@ $ci =& get_instance();
</a>
<div class="dropdown-menu menuOnResultTab" aria-labelledby="dropdownMenuLink" data-qsoid="qso_<?php echo $row->COL_PRIMARY_KEY; ?>">
<?php if (clubaccess_check(3, $row->COL_PRIMARY_KEY)) { ?>
<a class="dropdown-item" id="edit_qso" href="javascript:qso_edit(<?php echo $row->COL_PRIMARY_KEY; ?>)"><i class="fas fa-edit"></i> <?= __("Edit QSO"); ?></a>
<?php } ?>
<?php if (clubaccess_check(9)) { ?>
<?php if($row->COL_QSL_SENT !='Y') { ?>
<div class="qsl_sent_<?php echo $row->COL_PRIMARY_KEY; ?>">
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="javascript:qsl_sent(<?php echo $row->COL_PRIMARY_KEY; ?>, 'B')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Sent (Bureau)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_sent(<?php echo $row->COL_PRIMARY_KEY; ?>, 'D')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Sent (Direct)"); ?></a>
</div>
<?php } ?>
<?php if($row->COL_QSL_SENT !='Y') { ?>
<div class="qsl_sent_<?php echo $row->COL_PRIMARY_KEY; ?>">
<?php if($row->COL_QSL_RCVD !='Y') { ?>
<div class="qsl_rcvd_<?php echo $row->COL_PRIMARY_KEY; ?>">
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="javascript:qsl_rcvd(<?php echo $row->COL_PRIMARY_KEY; ?>, 'B')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Received (Bureau)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_rcvd(<?php echo $row->COL_PRIMARY_KEY; ?>, 'D')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Received (Direct)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_requested(<?php echo $row->COL_PRIMARY_KEY; ?>, 'B')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Card Requested (Bureau)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_requested(<?php echo $row->COL_PRIMARY_KEY; ?>, 'D')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Card Requested (Direct)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_ignore(<?php echo $row->COL_PRIMARY_KEY; ?>, 'D')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Card Not Required"); ?></a>
</div>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="javascript:qsl_sent(<?php echo $row->COL_PRIMARY_KEY; ?>, 'B')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Sent (Bureau)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_sent(<?php echo $row->COL_PRIMARY_KEY; ?>, 'D')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Sent (Direct)"); ?></a>
</div>
<?php } ?>
<?php } ?>
<?php if($row->COL_QSL_RCVD !='Y') { ?>
<div class="qsl_rcvd_<?php echo $row->COL_PRIMARY_KEY; ?>">
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="javascript:qsl_rcvd(<?php echo $row->COL_PRIMARY_KEY; ?>, 'B')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Received (Bureau)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_rcvd(<?php echo $row->COL_PRIMARY_KEY; ?>, 'D')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Received (Direct)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_requested(<?php echo $row->COL_PRIMARY_KEY; ?>, 'B')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Card Requested (Bureau)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_requested(<?php echo $row->COL_PRIMARY_KEY; ?>, 'D')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Card Requested (Direct)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_ignore(<?php echo $row->COL_PRIMARY_KEY; ?>, 'D')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Card Not Required"); ?></a>
</div>
<?php } ?>
<a class="dropdown-item" href="https://www.qrz.com/db/<?php echo $row->COL_CALL; ?>" target="_blank"><i class="fas fa-question"></i> <?= __("Lookup on QRZ.com"); ?></a>
<a class="dropdown-item" href="https://www.hamqth.com/<?php echo $row->COL_CALL; ?>" target="_blank"><i class="fas fa-question"></i> <?= __("Lookup on HamQTH"); ?></a>
<?php if (clubaccess_check(3, $row->COL_PRIMARY_KEY)) { ?>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="https://www.qrz.com/db/<?php echo $row->COL_CALL; ?>" target="_blank"><i class="fas fa-question"></i><?= __("Lookup on QRZ.com"); ?></a>
<a class="dropdown-item" href="https://www.hamqth.com/<?php echo $row->COL_CALL; ?>" target="_blank"><i class="fas fa-question"></i><?= __("Lookup on HamQTH"); ?></a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="javascript:qso_delete(<?php echo $row->COL_PRIMARY_KEY; ?>, '<?php echo $row->COL_CALL; ?>')"><i class="fas fa-trash-alt"></i> <?= __("Delete QSO"); ?></a>
<?php } ?>
</div>
</div>
</td>

View File

@@ -110,6 +110,7 @@
<small class="form-text text-muted"><?= sprintf(__("If you did operate from a new location, first create a new %sStation Location%s"), '<a href="'.site_url('station').'">', '</a>'); ?></small>
</div>
</div>
<?php if (clubaccess_check(9)) { ?>
<div class="col-xs-12 col-lg-6">
<div class="mb-3">
<label for="operator"><?= __("Operator"); ?> <span class="text-muted input-example"><?= __("e.g. OK2CQR"); ?></span></label>
@@ -118,6 +119,9 @@
<small class="form-text text-muted"><?= __("This is callsign of the operator. Without any pre- or suffixes."); ?></small>
</div>
</div>
<?php } else { ?>
<input type="hidden" id="operator" value="<?php echo $this->session->userdata('operator_callsign'); ?>">
<?php } ?>
</div>
</div>

View File

@@ -1,7 +1,12 @@
<div class="container">
<br>
<h3>
<?php if (isset($user_add)) {
echo __("Create User Account");
if ($clubstation) {
echo __("Create Clubstation Account");
} else {
echo __("Create User Account");
}
} else {
echo __("Edit Account")." <small class=\"text-muted\">".$user_name."</small>";
}
@@ -66,6 +71,9 @@
<input class="form-control" type="password" name="user_password" value="<?php if(isset($user_password)) { echo $user_password; } ?>" />
<span class="input-group-btn"><button class="btn btn-default btn-pwd-showhide" type="button"><i class="fa fa-eye-slash"></i></button></span>
</div>
<?php if($clubstation) { ?>
<small class="text-muted"><?= __("Don't share this password with operators!"); ?></small>
<?php } ?>
<?php if(isset($password_error)) {
echo "<small class=\"badge bg-danger\">".$password_error."</small>";
} else if (!isset($user_add)) { ?>
@@ -78,9 +86,13 @@
<?php if($this->session->userdata('user_type') == 99) { ?>
<select class="form-select" name="user_type">
<?php
$levels = $this->config->item('auth_level');
foreach ($levels as $key => $value) {
echo '<option value="'. $key . '" '. (($user_type ?? '') == $key ? "selected=\"selected\"":""). '>' . $value . '</option>';
if ($clubstation) {
echo '<option value="3" selected="selected">' . __("Clubstation") . '</option>';
} else {
$levels = $this->config->item('auth_level');
foreach ($levels as $key => $value) {
echo '<option value="'. $key . '" '. (($user_type ?? '') == $key ? "selected=\"selected\"":""). '>' . $value . '</option>';
}
}
?>
</select>
@@ -88,6 +100,9 @@
$l = $this->config->item('auth_level');
echo $l[$user_type];
}?>
<?php if ($clubstation) { ?>
<input type="hidden" name="clubstation" value="1" />
<?php } ?>
</div>
</div>
</div>
@@ -96,7 +111,7 @@
<!-- Personal Information -->
<div class="col-md">
<div class="card">
<div class="card-header"><?= __("Personal"); ?></div>
<div class="card-header"><?php if ($clubstation) { echo __("Callsign Owner"); } else { echo __("Personal");} ?></div>
<div class="card-body">
<div class="mb-3">
<label><?= __("First Name"); ?></label>
@@ -120,15 +135,15 @@
<div class="card-header"><?= __("Ham Radio"); ?></div>
<div class="card-body">
<div class="mb-3">
<label><?= __("Callsign"); ?></label>
<input class="form-control" type="text" name="user_callsign" value="<?php if(isset($user_callsign)) { echo $user_callsign; } ?>" />
<label><?php if ($clubstation) { echo __("Special/Club Callsign"); } else { echo __("Callsign"); } ?></label>
<input class="form-control uppercase" type="text" name="user_callsign" value="<?php if(isset($user_callsign)) { echo $user_callsign; } ?>" />
<?php if(isset($callsign_error)) { echo "<small class=\"badge bg-danger\">".$callsign_error."</small>"; } else { ?>
<?php } ?>
</div>
<div class="mb-3">
<label><?= __("Gridsquare"); ?></label>
<input class="form-control" type="text" name="user_locator" value="<?php if(isset($user_locator)) { echo $user_locator; } ?>" />
<input class="form-control uppercase" type="text" name="user_locator" value="<?php if(isset($user_locator)) { echo $user_locator; } ?>" />
<?php if(isset($locator_error)) { echo "<small class=\"badge bg-danger\">".$locator_error."</small>"; } else { ?>
<?php } ?>
</div>

View File

@@ -1,27 +1,10 @@
<script>
var lang_admin_confirm_pwd_reset = "<?= __("Do you really want to send this user a password-reset link?"); ?>";
var lang_admin_user = "<?= __("User"); ?>";
var lang_gen_hamradio_callsign = "<?= __("Callsign"); ?>";
var lang_admin_email_settings_incorrect = "<?= __("Email settings are incorrect."); ?>";
var lang_admin_password_reset_processed = "<?= __("Password-reset e-mail sent to user:"); ?>";
</script>
<div class="container">
<br>
<h2><?php echo $page_title; ?></h2>
<?php if ($this->session->flashdata('notice')) { ?>
<!-- Display Message -->
<div class="alert alert-info" role="alert">
<?php echo $this->session->flashdata('notice'); ?>
</div>
<?php } ?>
<!-- This Info will be shown by the admin password reset -->
<div class="alert" id="pwd_reset_message" style="display: hide" role="alert"></div>
<?php $this->load->view('layout/messages'); ?>
<div class="card">
<div class="card-header">
@@ -47,128 +30,211 @@
<th style="text-align: center; vertical-align: middle;" scope="col"><?= __("Type"); ?></th>
<th style="text-align: center; vertical-align: middle;" scope="col"><?= __("Last seen"); ?></th>
<th></th>
<th style="text-align: center; vertical-align: middle;" scope="col"><?= __("Edit"); ?></th>
<th style="text-align: center; vertical-align: middle;" scope="col"><?= __("Password Reset"); ?></th>
<?php if (!$disable_impersonate) { ?>
<th style="text-align: center; vertical-align: middle;" scope="col"><?= __("Impersonate"); ?></th>
<?php } ?>
<th style="text-align: center; vertical-align: middle;" scope="col"><?= __("Delete"); ?></th>
<th style="text-align: center; vertical-align: middle;" scope="col"><?= __("Actions"); ?></th>
</tr>
</thead>
<tbody>
<?php
$i = 0;
<?php $i = 0;
foreach ($results->result() as $row) { ?>
<?php echo '<tr class="tr' . ($i & 1) . '">'; ?>
<td style="text-align: left; vertical-align: middle;"><a href="<?php echo site_url('user/edit') . "/" . $row->user_id; ?>"><?php echo $row->user_name; ?></a></td>
<td style="text-align: left; vertical-align: middle;"><?php echo $row->user_callsign; ?></td>
<td style="text-align: left; vertical-align: middle;"><a href="mailto:<?php echo $row->user_email; ?>"><?php echo $row->user_email; ?></a></td>
<td style="text-align: left; vertical-align: middle;"><?php $l = $this->config->item('auth_level');
echo $l[$row->user_type]; ?></td>
<td style="text-align: left; vertical-align: middle;"><?php
if ($row->last_seen != null) { // if the user never logged in before the value is null. We can show "never" then.
$lastSeenTimestamp = strtotime($row->last_seen);
$currentTimestamp = time();
if (($currentTimestamp - $lastSeenTimestamp) < 120) {
echo "<a><i style=\"color: green;\" class=\"fas fa-circle\"></i> " . date($custom_date_format . ' H:i:s', $lastSeenTimestamp) . "</a>";
<tr class="tr<?php echo ($i & 1); ?>">
<td style="text-align: left; vertical-align: middle;">
<a href="<?php echo site_url('user/edit') . '/' . $row->user_id; ?>">
<?php echo $row->user_name; ?>
</a>
</td>
<td style="text-align: left; vertical-align: middle;">
<?php echo $row->user_callsign; ?>
</td>
<td style="text-align: left; vertical-align: middle;">
<a href="mailto:<?php echo $row->user_email; ?>">
<?php echo $row->user_email; ?>
</a>
</td>
<td style="text-align: left; vertical-align: middle;">
<?php
$l = $this->config->item('auth_level');
echo $l[$row->user_type];
?>
</td>
<td style="text-align: left; vertical-align: middle;">
<?php if ($row->last_seen != null) {
$lastSeenTimestamp = strtotime($row->last_seen);
$currentTimestamp = time();
if (($currentTimestamp - $lastSeenTimestamp) < 120) { ?>
<a><i style="color: green;" class="fas fa-circle"></i> <?php echo date($custom_date_format . ' H:i:s', $lastSeenTimestamp); ?></a>
<?php } else { ?>
<a><i style="color: red;" class="fas fa-circle"></i> <?php echo date($custom_date_format . ' H:i:s', $lastSeenTimestamp); ?></a>
<?php }
} else {
echo "<a><i style=\"color: red;\" class=\"fas fa-circle\"></i> " . date($custom_date_format . ' H:i:s', $lastSeenTimestamp) . "</a>";
}
} else {
echo __("Never");
}?>
</td>
<td style="text-align: left; vertical-align: middle;">
<span class="badge text-bg-success"><?= __("Locations"); ?>: <?php echo $row->stationcount; ?></span>
<br>
<span class="badge text-bg-info"><?= __("Logbooks"); ?>: <?php echo $row->logbookcount; ?></span>
<?php if ($row->qsocount > 0) { ?>
<span class="badge text-bg-light" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-custom-class="custom-tooltip" data-bs-html="true" data-bs-title="<?= __("Last QSO:"); ?><br><?php echo $row->lastqso; ?>"><?php echo $row->qsocount; ?> <?= __("QSO"); ?></span>
<?php } else { ?>
<span class="badge text-bg-light" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-custom-class="custom-tooltip" data-bs-html="true" data-bs-title="<?= __("No QSOs in Log"); ?>"><?php echo $row->qsocount; ?> <?= __("QSO"); ?></span>
<?php } ?>
</td>
<td style="text-align: center; vertical-align: middle;"><a href="<?php echo site_url('user/edit') . "/" . $row->user_id; ?>" class="btn btn-outline-primary btn-sm"><i class="fas fa-user-edit"></i></a>
<td style="text-align: center; vertical-align: middle;">
<?php
if ($session_uid != $row->user_id) {
echo '<a class="btn btn-primary btn-sm ms-1 admin_pwd_reset" data-username="' . $row->user_name . '" data-callsign="' . $row->user_callsign . '" data-userid="' . $row->user_id . '" data-usermail="' . $row->user_email . '"><i class="fas fa-key"></i></a>';
}
?></td>
<?php if (!$disable_impersonate) { ?>
<td style="text-align: center; vertical-align: middle;">
<?php
if ($session_uid != $row->user_id) { ?>
<button class="btn btn-info btn-sm" data-bs-toggle="modal" data-bs-target="#impersonateModal_<?php echo $i; ?>"><i class="fas fa-people-arrows"></i></button>
<div class="modal fade bg-black bg-opacity-50" id="impersonateModal_<?php echo $i; ?>" tabindex="-1" aria-labelledby="impersonateLabel_<?php echo $i; ?>" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog modal-dialog-centered modal-md">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="impersonateLabel_<?php echo $i; ?>"><?= __("Impersonate User") ?></h5>
</div>
<div class="modal-body" style="text-align: left !important;">
<div class="mb-3">
<?php if(!$has_flossie) { ?>
<p><?= __("You are about to impersonate another user. To return to your admin account, you'll need to logout and log back in as admin."); ?></p>
<p><?= __("Do you want to impersonate this user?"); ?></p>
<br>
<table>
<tr>
<td class="pe-3"><?= __("Username:"); ?></td>
<td><strong><?php echo $row->user_name; ?></strong></td>
</tr>
<tr>
<td class="pe-3"><?= __("Name:"); ?></td>
<td><strong><?php echo $row->user_firstname . ' ' . $row->user_lastname; ?></strong></td>
</tr>
<tr>
<td class="pe-3"><?= __("Callsign:"); ?></td>
<td><strong><?php echo $row->user_callsign; ?></strong></td>
</tr>
<tr>
<td class="pe-3"><?= __("E-Mail:"); ?></td>
<td><strong><a href="mailto:<?php echo $row->user_email; ?>"><?php echo $row->user_email; ?><a></strong></td>
</tr>
<tr>
<td class="pe-3"><?= __("Last Seen:"); ?></td>
<td><strong><?php if (isset($row->last_seen)) { echo date($custom_date_format . ' H:i:s', strtotime($row->last_seen)); } else { echo __("never"); }; ?></strong></td>
</tr>
</table>
<?php } else { ?>
<div class="alert alert-danger" role="alert">
<?= __("You currently can't impersonate another user. Please change the encryption_key in your config.php file first!"); ?>
</div>
<?php } ?>
</div>
<div class="modal-footer">
<form action="<?php echo site_url('user/impersonate'); ?>" method="post" style="display:inline;">
<input type="hidden" name="hash" value="<?php echo $this->encryption->encrypt($this->session->userdata('user_id') . '/' . $row->user_id . '/' . time()); ?>">
<button type="submit" class="btn btn-success" <?php if ($has_flossie) { echo 'disabled'; } ?>><?= __("Impersonate") ?></i></button>
</form>
<button type="button" class="btn btn-danger" data-bs-dismiss="modal"><?= __("Cancel") ?></button>
</div>
</div>
</div>
</div>
<?php }
?>
</td>
<?php } ?>
<td style="text-align: center; vertical-align: middle;">
<?php
if ($session_uid != $row->user_id) {
echo '<a href="' . site_url('user/delete') . '/' . $row->user_id . '" class="btn btn-danger btn-sm"><i class="fas fa-user-minus"></i></a>';
}
?></td>
</td>
echo __("Never");
} ?>
</td>
<td style="text-align: left; vertical-align: middle;">
<span class="badge text-bg-success"><?= __("Locations"); ?>: <?php echo $row->stationcount; ?></span>
<br>
<span class="badge text-bg-info"><?= __("Logbooks"); ?>: <?php echo $row->logbookcount; ?></span>
<?php if ($row->qsocount > 0) { ?>
<span class="badge text-bg-light" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-custom-class="custom-tooltip" data-bs-html="true" data-bs-title="<?= __("Last QSO:"); ?><br><?php echo $row->lastqso; ?>">
<?php echo $row->qsocount; ?> <?= __("QSO"); ?>
</span>
<?php } else { ?>
<span class="badge text-bg-light" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-custom-class="custom-tooltip" data-bs-html="true" data-bs-title="<?= __("No QSOs in Log"); ?>">
<?php echo $row->qsocount; ?> <?= __("QSO"); ?>
</span>
<?php } ?>
</td>
<!-- ### Actions ### -->
<td style="text-align: center; vertical-align: middle;">
<!-- Edit Button -->
<a href="<?php echo site_url('user/edit') . '/' . $row->user_id; ?>" class="btn btn-outline-primary btn-sm" data-bs-toggle="tooltip" data-bs-placement="top" title="<?= __("Edit"); ?>">
<i class="fas fa-user-edit"></i>
</a>
<!-- Impersonate Button -->
<?php if (!$disable_impersonate && $session_uid != $row->user_id) { ?>
<button class="btn btn-info btn-sm btn-tooltip" onclick="actions_modal('<?php echo $row->user_id; ?>', 'admin_impersonate')" title="<?= __("Impersonate"); ?>">
<i class="fas fa-people-arrows"></i>
</button>
<?php } ?>
<!-- Other Actions Button -->
<?php if ($session_uid != $row->user_id) { ?>
<button class="btn btn-secondary btn-sm btn-tooltip" onclick="actions_modal('<?php echo $row->user_id; ?>', 'more_actions')" title="<?= __("Other Actions"); ?>">
<i class="fas fa-bars"></i>
</button>
<?php } ?>
<!-- Delete Button -->
<?php if ($session_uid != $row->user_id) { ?>
<div class="vr mx-2"></div>
<a href="<?php echo site_url('user/delete') . '/' . $row->user_id; ?>" class="btn btn-danger btn-sm" data-bs-toggle="tooltip" data-bs-placement="top" title="<?= __("Delete"); ?>">
<i class="fas fa-user-minus"></i>
</a>
<?php } ?>
<!-- End Actions -->
</td>
</tr>
<?php $i++;
} ?>
<?php $i++; } ?>
</tbody>
</table>
</div>
</div>
</div>
<?php if ($clubmode) { ?>
<div class="card mt-3">
<div class="card-header">
<?= __("Clubstation List"); ?>
</div>
<div class="card-body">
<p class="card-text"><?= __("Clubstations in Wavelog are a unique way for clubs and special callsign events to handle multiple operators. A clubstation is basically a normal user account with some special features and some restrictions."); ?></p>
<p>
<a class="btn btn-primary" href="<?php echo site_url('user/add?club=1'); ?>"><i class="fas fa-user-plus"></i> <?= __("Create Clubstation"); ?></a>
<a class="btn btn-secondary" style="float: right;" href="<?php echo site_url('user'); ?>"><i class="fas fa-sync"></i> <?= __("Refresh List"); ?></a>
</p>
<?php if (!empty($clubs->result())) { ?>
<div class="table-responsive">
<table class="table table-striped" id="adminclubusertable">
<thead>
<tr>
<th style="text-align: center; vertical-align: middle;" scope="col"><?= __("Username"); ?></th>
<th style="text-align: center; vertical-align: middle;" scope="col"><?= __("Callsign"); ?></th>
<th style="text-align: center; vertical-align: middle;" scope="col"><?= __("E-mail"); ?></th>
<th style="text-align: center; vertical-align: middle;" scope="col"><?= __("Last Operator"); ?></th>
<th style="text-align: center; vertical-align: middle;" scope="col"><?= __("Last seen"); ?></th>
<th></th>
<th style="text-align: center; vertical-align: middle;" scope="col"><?= __("Actions"); ?></th>
</tr>
</thead>
<tbody>
<?php
$i = 0;
foreach ($clubs->result() as $row) { ?>
<?php echo '<tr class="tr' . ($i & 1) . '">'; ?>
<td style="text-align: left; vertical-align: middle;"><a href="<?php echo site_url('user/edit') . "/" . $row->user_id; ?>"><?php echo $row->user_name; ?></a></td>
<td style="text-align: left; vertical-align: middle;"><?php echo $row->user_callsign; ?></td>
<td style="text-align: left; vertical-align: middle;"><a href="mailto:<?php echo $row->user_email; ?>"><?php echo $row->user_email; ?></a></td>
<td style="text-align: left; vertical-align: middle;"><?php echo $row->lastoperator; ?></td>
<td style="text-align: left; vertical-align: middle;"><?php
if ($row->last_seen != null) { // if the user never logged in before the value is null. We can show "never" then.
$lastSeenTimestamp = strtotime($row->last_seen);
$currentTimestamp = time();
if (($currentTimestamp - $lastSeenTimestamp) < 120) {
echo "<a><i style=\"color: green;\" class=\"fas fa-circle\"></i> " . date($custom_date_format . ' H:i:s', $lastSeenTimestamp) . "</a>";
} else {
echo "<a><i style=\"color: red;\" class=\"fas fa-circle\"></i> " . date($custom_date_format . ' H:i:s', $lastSeenTimestamp) . "</a>";
}
} else {
echo __("Never");
}?>
</td>
<td style="text-align: left; vertical-align: middle;">
<span class="badge text-bg-success"><?= __("Locations"); ?>: <?php echo $row->stationcount; ?></span>
<br>
<span class="badge text-bg-info"><?= __("Logbooks"); ?>: <?php echo $row->logbookcount; ?></span>
<?php if ($row->qsocount > 0) { ?>
<span class="badge text-bg-light" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-custom-class="custom-tooltip" data-bs-html="true" data-bs-title="<?= __("Last QSO:"); ?><br><?php echo $row->lastqso; ?>"><?php echo $row->qsocount; ?> <?= __("QSO"); ?></span>
<?php } else { ?>
<span class="badge text-bg-light" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-custom-class="custom-tooltip" data-bs-html="true" data-bs-title="<?= __("No QSOs in Log"); ?>"><?php echo $row->qsocount; ?> <?= __("QSO"); ?></span>
<?php } ?>
</td>
<!-- ### Actions ### -->
<td style="text-align: center; vertical-align: middle;">
<!-- Edit Button -->
<a href="<?php echo site_url('user/edit') . '/' . $row->user_id; ?>" class="btn btn-outline-primary btn-sm" data-bs-toggle="tooltip" data-bs-placement="top" title="<?= __("Edit"); ?>">
<i class="fas fa-user-edit"></i>
</a>
<!-- Impersonate Button -->
<?php if (!$disable_impersonate && $session_uid != $row->user_id) { ?>
<button class="btn btn-info btn-sm btn-tooltip" onclick="actions_modal('<?php echo $row->user_id; ?>', 'admin_impersonate')" title="<?= __("Impersonate"); ?>">
<i class="fas fa-people-arrows"></i>
</button>
<?php } ?>
<!-- Club Permissions Button -->
<a href="<?php echo site_url('club/permissions') . "/" . $row->user_id; ?>" class="btn btn-warning btn-sm btn-tooltip" title="<?= __("Club Permissions"); ?>"><i style="color: black;" class="fas fa-user-lock"></i></a>
<!-- Other Actions Button -->
<?php if ($session_uid != $row->user_id) { ?>
<button class="btn btn-secondary btn-sm btn-tooltip" onclick="actions_modal('<?php echo $row->user_id; ?>', 'more_actions')" title="<?= __("Other Actions"); ?>">
<i class="fas fa-bars"></i>
</button>
<?php } ?>
<!-- Delete Button -->
<?php if ($session_uid != $row->user_id) { ?>
<div class="vr mx-2"></div>
<a href="<?php echo site_url('user/delete') . '/' . $row->user_id; ?>" class="btn btn-danger btn-sm" data-bs-toggle="tooltip" data-bs-placement="top" title="<?= __("Delete"); ?>">
<i class="fas fa-user-minus"></i>
</a>
<?php } ?>
<!-- End Actions -->
</td>
</tr>
<?php $i++;
} ?>
</tbody>
</table>
</div>
<?php } else { ?>
<div class="text-center">
<h5><?= __("No Clubstations configures yet."); ?></h5>
</div>
<?php } ?>
</div>
</div>
<?php } ?>
</div>
</div>
<div id="actionsModal-container"></div>
</div>

View File

@@ -0,0 +1,49 @@
<div class="modal fade bg-black bg-opacity-50" id="actionsModal" tabindex="-1" aria-labelledby="actionsLabel" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog modal-dialog-centered modal-fullscreen-md-down">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="actionsLabel"><?= __("Impersonate User") . " " . __("(with Admin Rights)"); ?></h5>
</div>
<div class="modal-body" style="text-align: left !important;">
<?php if (!$has_flossie) { ?>
<p><?= __("You are about to impersonate another user. To return to your admin account, you can use the switch back button in the header menu."); ?></p>
<p><?= __("Do you want to impersonate this user?"); ?></p>
<br>
<table>
<tr>
<td class="pe-3"><?= __("Username:"); ?></td>
<td><strong><?php echo $user_name; ?></strong></td>
</tr>
<tr>
<td class="pe-3"><?= __("Name:"); ?></td>
<td><strong><?php echo $user_firstname . ' ' . $user_lastname; ?></strong></td>
</tr>
<tr>
<td class="pe-3"><?= __("Callsign:"); ?></td>
<td><strong><?php echo $user_callsign; ?></strong></td>
</tr>
<tr>
<td class="pe-3"><?= __("E-Mail:"); ?></td>
<td><strong><a href="mailto:<?php echo $user_email; ?>"><?php echo $user_email; ?></a></strong></td>
</tr>
<tr>
<td class="pe-3"><?= __("Last Seen:"); ?></td>
<td><strong><?php echo isset($last_seen) ? date($custom_date_format . ' H:i:s', strtotime($last_seen)) : __("Never"); ?></strong></td>
</tr>
</table>
<?php } else { ?>
<div class="alert alert-danger" role="alert">
<?= __("You currently can't impersonate another user. Please change the encryption_key in your config.php file first!"); ?>
</div>
<?php } ?>
</div>
<div class="modal-footer">
<form action="<?php echo site_url('user/impersonate'); ?>" method="post" style="display: inline;">
<input type="hidden" name="hash" value="<?php echo $this->encryption->encrypt($this->session->userdata('user_id') . '/' . $user_id . '/' . time()); ?>">
<button type="submit" class="btn btn-success" <?php if ($has_flossie) { echo 'disabled'; } ?>><?= __("Impersonate") ?></button>
</form>
<button type="button" class="btn btn-danger" data-bs-dismiss="modal"><?= __("Cancel") ?></button>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,136 @@
<!-- First Layer - Actions Modal -->
<div class="modal fade bg-black bg-opacity-50" id="actionsModal" tabindex="-1" aria-labelledby="actionsLabel" data-bs-backdrop="static">
<div class="modal-dialog modal-dialog-centered modal-fullscreen-md-down">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="actionsLabel"><?= __("Other Actions") ?></h5>
</div>
<div class="modal-body" style="text-align: left !important;">
<p><?= __("Select an action to perform for the user:"); ?></p>
<table>
<tr>
<td class="pe-3"><?= __("Username:"); ?></td>
<td><strong><?php echo $user_name; ?></strong></td>
</tr>
<tr>
<td class="pe-3"><?= __("Name:"); ?></td>
<td><strong><?php echo $user_firstname . ' ' . $user_lastname; ?></strong></td>
</tr>
<tr>
<td class="pe-3"><?= __("Callsign:"); ?></td>
<td><strong><?php echo $user_callsign; ?></strong></td>
</tr>
<tr>
<td class="pe-3"><?= __("E-Mail:"); ?></td>
<td><strong><a href="mailto:<?php echo $user_email; ?>"><?php echo $user_email; ?></a></strong></td>
</tr>
</table>
<hr>
<div class="d-flex flex-column">
<?php if (!$is_clubstation) { ?>
<button class="btn btn-primary mb-2" data-bs-target="#passwordResetModal" data-bs-toggle="modal">
<i class="fas fa-key"></i> <?= __("Send a Password Reset Link via Email"); ?>
</button>
<?php } ?>
<?php if ($this->config->item('special_callsign')) { ?>
<button class="btn btn-warning mb-2" data-bs-target="#userConvertModal" data-bs-toggle="modal">
<i class="fas fa-users"></i> <?php if ($is_clubstation) { echo __("Convert to User"); } else { echo __("Convert to Clubstation"); } ?>
</button>
<?php } ?>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= __("Close") ?></button>
</div>
</div>
</div>
</div>
<!-- Second Layer - Password Reset Modal -->
<script>
var lang_admin_email_settings_incorrect = "<?= __("Email settings are incorrect."); ?>";
var lang_admin_password_reset_processed = "<?= __("Password-reset email sent successfully to user. You can close this dialog now."); ?>";
var lang_admin_password_reset_failed = "<?= __("Password-reset email could not be sent to user. Are the email settings in global options configured correctly?"); ?>";
</script>
<div class="modal fade bg-black bg-opacity-50" id="passwordResetModal" tabindex="-1" aria-labelledby="passwordResetLabel" data-bs-backdrop="static">
<div class="modal-dialog modal-dialog-centered modal-fullscreen-md-down">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="passwordResetLabel"><?= __("Send a Password Reset Link via Email"); ?></h5>
</div>
<div class="modal-body" style="text-align: left !important;">
<p><?= __("You are about to send a password reset link to the user. The user will be able to reset their password by clicking on the link in the email."); ?></p>
<p><?= __("Do you want to send the password reset email to this user?"); ?></p>
<p>
<?= __("User:"); ?> <strong><?php echo $user_name; ?></strong><br>
<?= __("Name:"); ?> <strong><?php echo $user_firstname . ' ' . $user_lastname; ?></strong><br>
<?= __("Callsign:"); ?> <strong><?php echo $user_callsign; ?></strong><br>
<?= __("Language:"); ?> <strong><?= __($user_language); ?></strong><br>
<?= __("E-Mail:"); ?> <strong><a href="mailto:<?php echo $user_email; ?>"><?php echo $user_email; ?></a></strong>
</p>
<br>
<button id="send_resetlink_btn" type="button" class="btn btn-primary ld-ext-right" onclick="send_passwort_reset(<?php echo $user_id; ?>)">
<?= __("Send the email") ?>
<div class="ld ld-ring ld-spin"></div>
<i id="passwordreset_sent" class="ms-2 fas" style="display: none;"></i>
</button>
<div class="alert mt-3" id="pwd_reset_message" style="display: none;" role="alert"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= __("Close") ?></button>
</div>
</div>
</div>
</div>
<!-- Second Layer - Convert to Clubstation Modal -->
<?php if ($this->config->item('special_callsign')) { ?>
<script>
var lang_account_conversion_processed = "<?= __("The account was successfully converted. You can now close this dialog."); ?>";
var lang_account_conversion_failed = "<?= __("The account could not be converted. An error has occurred."); ?>";
</script>
<div class="modal fade bg-black bg-opacity-50" id="userConvertModal" tabindex="-1" aria-labelledby="userConvertLabel" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog modal-dialog-centered modal-fullscreen-md-down">
<div class="modal-content">
<div class="modal-header bg-danger">
<h5 class="modal-title" id="userConvertLabel">
<?php if ($is_clubstation) {
echo __("Convert this account into a normal user");
} else {
echo __("Convert this account into a clubstation");
} ?>
</h5>
</div>
<div class="modal-body" style="text-align: left !important;">
<p><?php
if ($is_clubstation) {
echo __("You are about to convert this club station to a regular user account. The user will be able to log in again and all assigned club permissions will be removed. Use with caution!");
} else {
echo __("You are about to convert this user account to a club station. The user will no longer be able to log in and the account will be converted to a club station account. Use with caution!");
}
?></p>
<p><?= __("Are you sure you want to convert this account?"); ?></p>
<p>
<?= __("User:"); ?> <strong><?php echo $user_name; ?></strong><br>
<?= __("Name:"); ?> <strong><?php echo $user_firstname . ' ' . $user_lastname; ?></strong><br>
<?= __("Callsign:"); ?> <strong><?php echo $user_callsign; ?></strong><br>
<?= __("Language:"); ?> <strong><?= __($user_language); ?></strong><br>
<?= __("E-Mail:"); ?> <strong><a href="mailto:<?php echo $user_email; ?>"><?php echo $user_email; ?></a></strong>
</p>
<br>
<button id="convert_user_btn" type="button" class="btn btn-danger ld-ext-right" onclick="convert_user(<?php echo $user_id; ?>, <?php if ($is_clubstation) { echo '0'; } else { echo '1'; } ?>)">
<?= __("Convert") ?>
<div class="ld ld-ring ld-spin"></div>
<i id="user_converted" class="ms-2 fas" style="display: none;"></i>
</button>
<div class="alert mt-3" id="user_converted_message" style="display: none;" role="alert"></div>
</div>
<div class="modal-footer">
<button id="conversion_close" type="button" class="btn btn-secondary" onclick="window.location.reload();"><?= __("Close") ?></button>
</div>
</div>
</div>
</div>
<?php } ?>

View File

@@ -0,0 +1,19 @@
<div class="modal fade bg-black bg-opacity-50" id="stopImpersonateModal" tabindex="-1" aria-labelledby="stopImpersonateLabel" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog modal-dialog-centered modal-md">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="stopImpersonateLabel"><?= __("Switch back to main user") ?></h5>
</div>
<form action="<?= site_url('user/stop_impersonate'); ?>" method="post">
<div class="modal-body" style="text-align: center !important;">
<p><?= sprintf(__("Are you sure you want to switch back to %s?"), $this->session->userdata('cd_src_call')); ?></p>
<input type="hidden" name="stopImpersonate" value="1">
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-success"><?= __("Yes, switch over!"); ?></button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= __("Cancel"); ?></button>
</div>
</form>
</div>
</div>
</div>

View File

@@ -132,11 +132,13 @@
</a>
<div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
<?php if (clubaccess_check(3, $row->COL_PRIMARY_KEY)) { ?>
<a class="dropdown-item" id="edit_qso" href="javascript:qso_edit(<?php echo $row->COL_PRIMARY_KEY; ?>)"><i class="fas fa-edit"></i> <?= __("Edit QSO"); ?></a>
<?php } ?>
<?php if($row->COL_QSL_SENT !='Y') { ?>
<div class="dropdown-divider"></div>
<div class="qsl_sent_<?php echo $row->COL_PRIMARY_KEY; ?>">
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="javascript:qsl_sent(<?php echo $row->COL_PRIMARY_KEY; ?>, 'B')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Sent (Bureau)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_sent(<?php echo $row->COL_PRIMARY_KEY; ?>, 'D')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Sent (Direct)"); ?></a>
</div>

View File

@@ -395,36 +395,41 @@ function echoQrbCalcLink($mygrid, $grid, $vucc, $isVisitor = false) {
</div>
<div class="dropdown-menu menuOnResultTab" data-bs-toggle="popover" data-bs-placement="auto" data-qsoid="qso_<?php echo $row->COL_PRIMARY_KEY; ?>">
<?php if (clubaccess_check(3, $row->COL_PRIMARY_KEY)) { ?>
<a class="dropdown-item" id="edit_qso" href="javascript:qso_edit(<?php echo $row->COL_PRIMARY_KEY; ?>)"><i class="fas fa-edit"></i> <?= __("Edit QSO"); ?></a>
<?php if($row->COL_QSL_SENT !='Y') { ?>
<div class="qsl_sent_<?php echo $row->COL_PRIMARY_KEY; ?>">
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="javascript:qsl_sent(<?php echo $row->COL_PRIMARY_KEY; ?>, 'B')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Sent (Bureau)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_sent(<?php echo $row->COL_PRIMARY_KEY; ?>, 'D')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Sent (Direct)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_requested(<?php echo $row->COL_PRIMARY_KEY; ?>, 'B')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Card Requested (Bureau)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_requested(<?php echo $row->COL_PRIMARY_KEY; ?>, 'D')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Card Requested (Direct)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_ignore(<?php echo $row->COL_PRIMARY_KEY; ?>, 'D')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Card Not Required"); ?></a>
</div>
<?php } ?>
<?php if (clubaccess_check(9)) { ?>
<?php if($row->COL_QSL_SENT !='Y') { ?>
<div class="qsl_sent_<?php echo $row->COL_PRIMARY_KEY; ?>">
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="javascript:qsl_sent(<?php echo $row->COL_PRIMARY_KEY; ?>, 'B')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Sent (Bureau)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_sent(<?php echo $row->COL_PRIMARY_KEY; ?>, 'D')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Sent (Direct)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_requested(<?php echo $row->COL_PRIMARY_KEY; ?>, 'B')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Card Requested (Bureau)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_requested(<?php echo $row->COL_PRIMARY_KEY; ?>, 'D')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Card Requested (Direct)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_ignore(<?php echo $row->COL_PRIMARY_KEY; ?>, 'D')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Card Not Required"); ?></a>
</div>
<?php } ?>
<?php if($row->COL_QSL_RCVD !='Y') { ?>
<div class="qsl_rcvd_<?php echo $row->COL_PRIMARY_KEY; ?>">
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="javascript:qsl_rcvd(<?php echo $row->COL_PRIMARY_KEY; ?>, 'B')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Received (Bureau)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_rcvd(<?php echo $row->COL_PRIMARY_KEY; ?>, 'D')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Received (Direct)"); ?></a>
</div>
<?php if($row->COL_QSL_RCVD !='Y') { ?>
<div class="qsl_rcvd_<?php echo $row->COL_PRIMARY_KEY; ?>">
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="javascript:qsl_rcvd(<?php echo $row->COL_PRIMARY_KEY; ?>, 'B')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Received (Bureau)"); ?></a>
<a class="dropdown-item" href="javascript:qsl_rcvd(<?php echo $row->COL_PRIMARY_KEY; ?>, 'D')" ><i class="fas fa-envelope"></i> <?= __("Mark QSL Received (Direct)"); ?></a>
</div>
<?php } ?>
<div class="dropdown-divider"></div>
<?php } ?>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="https://www.qrz.com/db/<?php echo $row->COL_CALL; ?>" target="_blank"><i class="fas fa-question"></i> <?= __("Lookup on QRZ.com"); ?></a>
<a class="dropdown-item" href="https://www.hamqth.com/<?php echo $row->COL_CALL; ?>" target="_blank"><i class="fas fa-question"></i> <?= __("Lookup on HamQTH"); ?></a>
<?php if (clubaccess_check(3, $row->COL_PRIMARY_KEY)) { ?>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="javascript:qso_delete(<?php echo $row->COL_PRIMARY_KEY; ?>, '<?php echo $row->COL_CALL; ?>')"><i class="fas fa-trash-alt"></i> <?= __("Delete QSO"); ?></a>
<?php } ?>
</div>
</div>
</td>

View File

@@ -24,10 +24,11 @@
echo 'class="qslcardtab nav-item">
<a class="nav-link" id="qsltab" data-bs-toggle="tab" href="#qslcard" role="tab" aria-controls="home" aria-selected="false">'. __("QSL Card") .'</a>
</li>';
echo '<li class="nav-item">
<a class="nav-link" id="qslmanagementtab" data-bs-toggle="tab" href="#qslupload" role="tab" aria-controls="home" aria-selected="false">'. __("QSL Management") .'</a>
</li>';
if (clubaccess_check(9)) {
echo '<li class="nav-item">
<a class="nav-link" id="qslmanagementtab" data-bs-toggle="tab" href="#qslupload" role="tab" aria-controls="home" aria-selected="false">'. __("QSL Management") .'</a>
</li>';
}
}
?>
@@ -573,7 +574,9 @@
<?php if(($this->config->item('use_auth') && ($this->session->userdata('user_type') >= 2)) || $this->config->item('use_auth') === FALSE) { ?>
<br>
<?php if (clubaccess_check(3, $row->COL_PRIMARY_KEY)) { ?>
<div style="display: inline-block;"><p class="editButton"><a class="btn btn-primary" href="<?php echo site_url('qso/edit'); ?>/<?php echo $row->COL_PRIMARY_KEY; ?>" href="javascript:;"><i class="fas fa-edit"></i> <?= __("Edit QSO"); ?></a></p></div>
<?php } ?>
<div style="display: inline-block;"><form method="POST" action="<?php echo site_url('search'); ?>"><input type="hidden" value="<?php echo strtoupper($row->COL_CALL); ?>" name="callsign"><button class="btn btn-primary" type="submit"><i class="fas fa-eye"></i> <?= __("More QSOs"); ?></button></form></div>
<?php } ?>

12
assets/css/dl3el/bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,30 @@
.eqsl-green{
color: yellow !important;
}
.eqsl-red{
color: red;
}
.qsl-green{
color: yellow !important;
}
.qsl-red{
color:red;
}
.qsl-yellow{
color: orange !important;
}
.qsl-grey{
color: turquoise !important;
}
.lotw-green{
color: yellow !important;
}
.lotw-red{
color: #F00 !important;
}
.clublog-green{
color: yellow !important;
}
.clublog-red{
color: #F00 !important;
}

View File

@@ -0,0 +1,70 @@
$(document).ready(function(){
$('#clubuserstable').DataTable({
"pageLength": 25,
responsive: true,
ordering: true,
"scrollY": "100%",
"scrollCollapse": true,
"paging": true,
"language": {
url: getDataTablesLanguageUrl(),
},
dom: 'Bfrtip',
buttons: [
{
extend: 'csv',
exportOptions: {
columns: [ 0, 1, 2, 3, 4, 5 ]
}
}
]
});
$('#user_id').selectize({
delimiter: ';',
maxItems: 1,
closeAfterSelect: true,
valueField: 'user_id',
labelField: 'user_callsign',
searchField: ['user_callsign', 'user_firstname', 'user_lastname'],
options: [],
create: false,
load: function(query, callback) {
if (!query) return callback();
query = query.toUpperCase();
$.ajax({
url: base_url + 'index.php/club/get_users',
type: 'POST',
dataType: 'json',
data: { query: query },
error: function() {
callback();
},
success: function(res) {
callback(res);
}
});
},
render: {
option: function(item) {
let string = '<div style="text-align: left; margin-left: 10px; padding: 3px;">' + item.user_callsign.toUpperCase() + ' - ' + item.user_firstname + ' ' + item.user_lastname + '</div>';
return string;
},
item: function(item) {
let string = '<div style="text-align: left; margin-left: 2px;">' + item.user_callsign.toUpperCase() + ' - ' + item.user_firstname + ' ' + item.user_lastname + '</div>';
return string;
}
},
onInitialize: function() {
$('.selectize-control').parents().each(function() {
$(this).css('overflow', 'visible');
});
}
});
$('[type="submit"]').on('click', function() {
$(this).prop('disabled', true).addClass('running');
$(this).closest('form').submit();
});
});

View File

@@ -2,6 +2,105 @@
// Javascript for User Section
//
function clearRefSwitches() {
var iotaSwitch = document.getElementById("iotaToQsoTab");
iotaSwitch.checked = false;
var sotaSwitch = document.getElementById("sotaToQsoTab");
sotaSwitch.checked = false;
var wwffSwitch = document.getElementById("wwffToQsoTab");
wwffSwitch.checked = false;
var potaSwitch = document.getElementById("potaToQsoTab");
potaSwitch.checked = false;
var sigSwitch = document.getElementById("sigToQsoTab");
sigSwitch.checked = false;
var dokSwitch = document.getElementById("dokToQsoTab");
dokSwitch.checked = false;
}
function actions_modal(user_id, modal) {
$.ajax({
url: base_url + 'index.php/user/actions_modal',
type: 'POST',
data: {
modal: modal,
user_id: user_id
},
success: function(response) {
$('#actionsModal-container').html(response);
$('#actionsModal').modal('show');
},
error: function() {
alert(lang_general_word_error);
}
});
$(window).on('blur', function() {
$('#actionsModal').modal('hide');
});
}
function send_passwort_reset(user_id) {
$('#pwd_reset_message').hide().removeClass('alert-success alert-danger');
$('#send_resetlink_btn').prop('disabled', true).addClass('running');
$('#passwordreset_sent').hide().removeClass('fa-check fa-times text-success text-danger');
$.ajax({
url: base_url + 'index.php/user/admin_send_password_reset',
type: 'POST',
data: {
user_id: user_id,
submit_allowed: true
},
success: function(result) {
if (result) {
$('#pwd_reset_message').show().text(lang_admin_password_reset_processed).addClass('alert-success');
$('#send_resetlink_btn').prop('disabled', false).removeClass('running');
$('#passwordreset_sent').show().addClass('fa-check text-success');
} else {
$('#pwd_reset_message').show().text(lang_admin_email_settings_incorrect).addClass('alert-danger');
$('#send_resetlink_btn').prop('disabled', false).removeClass('running');
$('#passwordreset_sent').show().addClass('fa-times text-danger');
}
},
error: function() {
$('#pwd_reset_message').show().text(lang_admin_password_reset_failed).addClass('alert-danger');
$('#send_resetlink_btn').prop('disabled', false).removeClass('running');
$('#passwordreset_sent').show().addClass('fa-times text-danger');
}
});
}
function convert_user(user_id, convert_to) {
$('#user_converted_message').hide().removeClass('alert-success alert-danger');
$('#convert_user_btn').prop('disabled', true).removeClass('btn-secondary').addClass('btn-danger running');
$('#user_converted').hide().removeClass('fa-check fa-times text-success text-danger');
$.ajax({
url: base_url + 'index.php/user/convert',
type: 'POST',
data: {
user_id: user_id,
convert_to: convert_to,
},
success: function(result) {
if (result) {
$('#user_converted_message').show().text(lang_account_conversion_processed).addClass('alert-success');
$('#convert_user_btn').removeClass('running btn-danger').addClass('btn-secondary');
$('#user_converted').show().addClass('fa-check text-success');
} else {
$('#user_converted_message').show().text(lang_account_conversion_failed).addClass('alert-danger');
$('#convert_user_btn').prop('disabled', false).removeClass('running');
$('#user_converted').show().addClass('fa-times text-danger');
}
},
error: function() {
$('#user_converted_message').show().text(lang_account_conversion_failed).addClass('alert-danger');
$('#convert_user_btn').prop('disabled', false).removeClass('running');
$('#user_converted').show().addClass('fa-times text-danger');
}
});
}
$(document).ready(function(){
$('#adminusertable').DataTable({
@@ -25,6 +124,31 @@ $(document).ready(function(){
]
});
$('#adminclubusertable').DataTable({
"pageLength": 25,
responsive: true,
ordering: true,
"scrollY": "100%",
"scrollCollapse": true,
"paging": true,
"language": {
url: getDataTablesLanguageUrl(),
},
dom: 'Bfrtip',
buttons: [
{
extend: 'csv',
exportOptions: {
columns: [ 0, 1, 2, 3, 4, 5 ]
}
}
]
});
$(function () {
$('.btn-tooltip').tooltip();
});
$('.icon_selectBox').off('click').on('click', function(){
var boxcontent = $(this).attr('data-boxcontent');
if ($('.icon_selectBox_data[data-boxcontent="'+boxcontent+'"]').is(":hidden")) { $('.icon_selectBox_data[data-boxcontent="'+boxcontent+'"]').show(); } else { $('.icon_selectBox_data[data-boxcontent="'+boxcontent+'"]').hide(); }
@@ -144,18 +268,3 @@ $(document).ready(function(){
});
});
function clearRefSwitches() {
var iotaSwitch = document.getElementById("iotaToQsoTab");
iotaSwitch.checked = false;
var sotaSwitch = document.getElementById("sotaToQsoTab");
sotaSwitch.checked = false;
var wwffSwitch = document.getElementById("wwffToQsoTab");
wwffSwitch.checked = false;
var potaSwitch = document.getElementById("potaToQsoTab");
potaSwitch.checked = false;
var sigSwitch = document.getElementById("sigToQsoTab");
sigSwitch.checked = false;
var dokSwitch = document.getElementById("dokToQsoTab");
dokSwitch.checked = false;
}

View File

@@ -674,25 +674,21 @@ $config['disable_oqrs'] = false;
/*
|--------------------------------------------------------------------------
| Special Callsign Feature
| Special Callsign Feature aka. Clubstations Support
|--------------------------------------------------------------------------
|
| This config switch is meant to use for Special Callsign operations in a dedicated Wavelog Installation
| If this switch is set to true it will enable a dialog which pops up for each operator after login
| to ask for his personal callsign. This causes the QSOs to get saved with the correct operator data.
| Example: Special Callsign: DL250CDF
| Operator: DF2TG
| This config switch is meant to use for Special Callsign operations or Clubstations.
| If this switch is set to true it enables a whole bunch of features to handle Special Callsigns and Club Callsigns.
| For more Information please visit the Wiki:
| https://github.com/wavelog/wavelog/wiki/Clubstations
|
| !!! Important !!!
| $config['disable_impersonate'] has to be set to false to use this feature.
|
| It is recommend to enable also "Disable Syncing to 3rd party-Services at UI"
| More Information about this feature and how to use it, you can find here:
| https://github.com/wavelog/wavelog/wiki/Recommended-Setup-for-Special-Callsigns-and-Clubs
*/
$config['special_callsign'] = false;
// hides the usermenu; takes action only if "special_callsign" is true
$config['sc_hide_usermenu'] = true;
/*
|--------------------------------------------------------------------------
@@ -700,7 +696,7 @@ $config['sc_hide_usermenu'] = true;
|--------------------------------------------------------------------------
|
| This config switch disables the impersonate feature. This feauture is used to impersonate another user.
| Impersonate is enabled by default. To disable it, set the value to false.
| Impersonate is enabled by default. To disable it, set the value to false. Also the special_callsign feature needs this to be false.
|
*/