Add logbook sharing email notification

Introduces email notifications when a logbook is shared with a new user, including a new email template and improved error logging for email sending. Also enhances the test email UI with a loading spinner and updates error messages for better clarity.
This commit is contained in:
Peter Goodhall 2025-11-22 15:09:44 +00:00
parent f2b8a3eeaa
commit 476b59abc4
5 changed files with 133 additions and 8 deletions

View file

@ -316,7 +316,12 @@ class Logbooks extends CI_Controller {
// Add permission
$result = $this->logbooks_model->add_logbook_permission($logbook_id, $user->user_id, $permission_level);
if ($result) {
if ($result['success']) {
// Send email notification if this is a new user being added
if ($result['is_new'] && $user->user_email != "") {
$this->send_logbook_shared_email($logbook_id, $user, $permission_level);
}
// Return updated collaborators list
$data['collaborators'] = $this->logbooks_model->list_logbook_collaborators($logbook_id);
$data['is_owner'] = $this->logbooks_model->is_logbook_owner($logbook_id);
@ -383,4 +388,68 @@ class Logbooks extends CI_Controller {
}
}
private function send_logbook_shared_email($logbook_id, $user, $permission_level) {
// Send email notification when a logbook is shared with a user
// Check if email is configured
if ($this->optionslib->get_option('emailProtocol') == '') {
log_message('info', 'Email not configured - skipping logbook sharing notification');
return;
}
// Get logbook details
$logbook_query = $this->logbooks_model->logbook($logbook_id);
if ($logbook_query->num_rows() == 0) {
return;
}
$logbook = $logbook_query->row();
// Get owner information
$owner_query = $this->user_model->get_by_id($this->session->userdata('user_id'));
if ($owner_query->num_rows() == 0) {
return;
}
$owner = $owner_query->row();
// Load email library
$this->load->library('email');
// Configure SMTP if needed
if ($this->optionslib->get_option('emailProtocol') == "smtp") {
$config = Array(
'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);
}
// Prepare email data
$email_data = array(
'user_callsign' => $user->user_callsign,
'owner_callsign' => $owner->user_callsign,
'logbook_name' => $logbook->logbook_name,
'permission_level' => $permission_level,
'base_url' => base_url(),
);
$message = $this->load->view('email/logbook_shared.php', $email_data, TRUE);
$this->email->from($this->optionslib->get_option('emailAddress'), $this->optionslib->get_option('emailSenderName'));
$this->email->to($user->user_email);
$this->email->subject('Cloudlog - Logbook Shared With You');
$this->email->message($message);
if (!$this->email->send()) {
log_message('error', 'Failed to send logbook sharing email to ' . $user->user_email . '. Error: ' . $this->email->print_debugger());
} else {
log_message('info', 'Logbook sharing notification sent to ' . $user->user_email);
}
}
}

View file

@ -379,12 +379,14 @@ class Options extends CI_Controller {
$this->email->message($message);
if (! $this->email->send()){
$this->session->set_flashdata('testmailFailed', $this->lang->line('options_send_testmail_failed'));
// Log the error for debugging
log_message('error', 'Test email failed to send. Error: ' . $this->email->print_debugger());
$this->session->set_flashdata('testmailFailed', $this->lang->line('options_send_testmail_failed') . ' Check application logs for details.');
} else {
$this->session->set_flashdata('testmailSuccess', $this->lang->line('options_send_testmail_success'));
}
} else {
$this->session->set_flashdata('testmailFailed', $this->lang->line('options_send_testmail_failed'));
$this->session->set_flashdata('testmailFailed', $this->lang->line('options_send_testmail_failed') . ' No email address found in your account settings.');
}
redirect('/options/email');

View file

@ -422,6 +422,7 @@ class Logbooks_model extends CI_Model {
public function add_logbook_permission($logbook_id, $user_id, $permission_level = 'read') {
// Add a user to a logbook with specified permission level
// Only owner or admin can add users
// Returns array with 'success' and 'is_new' keys
$clean_logbook_id = $this->security->xss_clean($logbook_id);
$clean_user_id = $this->security->xss_clean($user_id);
@ -430,13 +431,13 @@ class Logbooks_model extends CI_Model {
// Validate permission level
$valid_permissions = array('read', 'write', 'admin');
if (!in_array($clean_permission, $valid_permissions)) {
return false;
return array('success' => false, 'is_new' => false);
}
// Check if current user has admin rights or is owner
if (!$this->is_logbook_owner($clean_logbook_id) &&
!$this->check_logbook_is_accessible($clean_logbook_id, 'admin')) {
return false;
return array('success' => false, 'is_new' => false);
}
// Check if permission already exists
@ -444,6 +445,8 @@ class Logbooks_model extends CI_Model {
$this->db->where('user_id', $clean_user_id);
$existing = $this->db->get('station_logbooks_permissions');
$is_new = ($existing->num_rows() == 0);
if ($existing->num_rows() > 0) {
// Update existing permission
$data = array(
@ -463,7 +466,7 @@ class Logbooks_model extends CI_Model {
$this->db->insert('station_logbooks_permissions', $data);
}
return true;
return array('success' => true, 'is_new' => $is_new);
}
public function remove_logbook_permission($logbook_id, $user_id) {

View file

@ -0,0 +1,43 @@
Hi <?php echo $user_callsign; ?>,
<?php echo $owner_callsign; ?> has shared a logbook with you on Cloudlog.
Logbook: <?php echo $logbook_name; ?>
Permission Level: <?php echo ucfirst($permission_level); ?>
<?php if ($permission_level == 'read') { ?>
You can now view this logbook and its QSOs.
<?php } elseif ($permission_level == 'write') { ?>
You can now view, add, edit, and delete QSOs in this logbook, as well as manage station locations.
<?php } elseif ($permission_level == 'admin') { ?>
You have full administrative access to this logbook, including the ability to manage other users' access.
<?php } ?>
Getting Started:
<?php if ($permission_level != 'read') { ?>
1. Log into your Cloudlog account at: <?php echo $base_url; ?>
2. Create a Station Location using the shared callsign (<?php echo $owner_callsign; ?>):
- Go to Station Locations and click "Create Station Location"
- Enter the callsign: <?php echo $owner_callsign; ?>
- Enter the location details (gridsquare, DXCC, etc.)
- Save the station location
3. Link your Station Location to this logbook:
- Go to Logbooks > Edit "<?php echo $logbook_name; ?>"
- In the "Link Location" section, select your newly created station location
- Click "Link Location"
4. You're ready to start logging! Set this logbook as active from the Logbooks page.
<?php } else { ?>
1. Log into your Cloudlog account at: <?php echo $base_url; ?>
2. Go to Logbooks and set "<?php echo $logbook_name; ?>" as your active logbook to view the QSOs.
<?php } ?>
If you have any questions, please contact <?php echo $owner_callsign; ?>.
Regards,
Cloudlog.

View file

@ -112,10 +112,18 @@
<input class="btn btn-primary" type="submit" value="<?php echo lang('options_save'); ?>" />
</form>
<br>
<?php echo form_open('options/sendTestMail'); ?>
<input class="btn btn-primary" type="submit" value="<?php echo lang('options_send_testmail'); ?>" />
<?php echo form_open('options/sendTestMail', ['id' => 'testMailForm']); ?>
<input class="btn btn-primary" id="testMailBtn" type="submit" value="<?php echo lang('options_send_testmail'); ?>" />
<span id="testMailSpinner" class="spinner-border spinner-border-sm ms-2" role="status" aria-hidden="true" style="display: none;"></span>
<small class="form-text text-muted"><?php echo lang('options_send_testmail_hint'); ?></small>
</form>
<script>
document.getElementById('testMailForm').addEventListener('submit', function() {
document.getElementById('testMailBtn').disabled = true;
document.getElementById('testMailSpinner').style.display = 'inline-block';
});
</script>
</div>
</div>
</div>