[QSLCard] PR #677 from AndreasK79 this adds QSL Card management features for uploading QSL card images

This adds basic document management for QSL cards into Cloudlog which can be used when viewing QSO record.

You might need to make sure /assets/qslcard is writeable and deletable by PHP
这个提交包含在:
Peter Goodhall 2020-11-01 15:52:04 +00:00 提交者 GitHub
当前提交 356296c953
找不到此签名对应的密钥
GPG 密钥 ID: 4AEE18F83AFDEB23
共有 11 个文件被更改,包括 798 次插入249 次删除

1
.gitignore vendored
查看文件

@ -10,6 +10,7 @@
/updates/*.html
/images/eqsl_card_images/*.jpg
/updates/clublog_scp.txt
/assets/qslcard/*
.idea/*
.DS_Store
sync.sh

查看文件

@ -21,7 +21,7 @@ $config['migration_enabled'] = TRUE;
| be upgraded / downgraded to.
|
*/
$config['migration_version'] = 53;
$config['migration_version'] = 54;
/*
|--------------------------------------------------------------------------

查看文件

@ -411,6 +411,9 @@ class Logbook extends CI_Controller {
$data['measurement_base'] = $this->session->userdata('user_measurement_base');
}
$this->load->model('Qsl_model');
$data['qslimages'] = $this->Qsl_model->getQslForQsoId($id);
$this->load->view('interface_assets/mini_header', $data);
$this->load->view('view_log/qso');
$this->load->view('interface_assets/footer');

查看文件

@ -0,0 +1,140 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/*
Controller for QSL Cards
*/
class Qsl extends CI_Controller {
function __construct() {
parent::__construct();
$this->load->model('user_model');
if(!$this->user_model->authorize(2)) { $this->session->set_flashdata('notice', 'You\'re not allowed to do that!'); redirect('dashboard'); }
}
public function index() {
// Render Page
$data['page_title'] = "QSL Cards";
$this->load->model('qsl_model');
$data['qslarray'] = $this->qsl_model->getQsoWithQslList();
$this->load->view('interface_assets/header', $data);
$this->load->view('qslcard/index');
$this->load->view('interface_assets/footer');
}
public function upload() {
// Render Page
$data['page_title'] = "Upload QSL Cards";
$this->load->view('interface_assets/header', $data);
$this->load->view('qslcard/upload');
$this->load->view('interface_assets/footer');
}
public function delete() {
$this->load->model('user_model');
if(!$this->user_model->authorize(2)) { $this->session->set_flashdata('notice', 'You\'re not allowed to do that!'); redirect('dashboard'); }
$id = $this->input->post('id');
$this->load->model('Qsl_model');
$path = './assets/qslcard/';
$file = $this->Qsl_model->getFilename($id)->row();
$filename = $file->filename;
unlink($path.$filename);
$this->Qsl_model->deleteQsl($id);
}
public function uploadqsl() {
$this->load->model('user_model');
if(!$this->user_model->authorize(2)) { $this->session->set_flashdata('notice', 'You\'re not allowed to do that!'); redirect('dashboard'); }
if (!file_exists('./assets/qslcard')) {
mkdir('./assets/qslcard', 0755, true);
}
$qsoid = $this->input->post('qsoid');
if (isset($_FILES['qslcardfront']) && $_FILES['qslcardfront']['name'] != "" && $_FILES['qslcardfront']['error'] == 0)
{
$result['front'] = $this->uploadQslCardFront($qsoid);
}
if (isset($_FILES['qslcardback']) && $_FILES['qslcardback']['name'] != "" && $_FILES['qslcardback']['error'] == 0)
{
$result['back'] = $this->uploadQslCardBack($qsoid);
}
header("Content-type: application/json");
echo json_encode(['status' => $result]);
}
function uploadQslCardFront($qsoid) {
$config['upload_path'] = './assets/qslcard';
$config['allowed_types'] = 'jpg|gif|png';
$array = explode(".", $_FILES['qslcardfront']['name']);
$ext = end($array);
$config['file_name'] = $qsoid . '_' . time() . '.' . $ext;
$this->load->library('upload', $config);
if ( ! $this->upload->do_upload('qslcardfront')) {
// Upload of QSL card Failed
$error = array('error' => $this->upload->display_errors());
return $error;
}
else {
// Load database queries
$this->load->model('Qsl_model');
//Upload of QSL card was successful
$data = $this->upload->data();
// Now we need to insert info into database about file
$filename = $data['file_name'];
$insertid = $this->Qsl_model->saveQsl($qsoid, $filename);
$result['status'] = 'Success';
$result['insertid'] = $insertid;
$result['filename'] = $filename;
return $result;
}
}
function uploadQslCardBack($qsoid) {
$config['upload_path'] = './assets/qslcard';
$config['allowed_types'] = 'jpg|gif|png';
$array = explode(".", $_FILES['qslcardback']['name']);
$ext = end($array);
$config['file_name'] = $qsoid . '_' . time() . '.' . $ext;
$this->load->library('upload', $config);
if ( ! $this->upload->do_upload('qslcardback')) {
// Upload of QSL card Failed
$error = array('error' => $this->upload->display_errors());
return $error;
}
else {
// Load database queries
$this->load->model('Qsl_model');
//Upload of QSL card was successful
$data = $this->upload->data();
// Now we need to insert info into database about file
$filename = $data['file_name'];
$insertid = $this->Qsl_model->saveQsl($qsoid, $filename);
$result['status'] = 'Success';
$result['insertid'] = $insertid;
$result['filename'] = $filename;
return $result;
}
}
}

查看文件

@ -0,0 +1,19 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Migration_add_qsl_images extends CI_Migration {
public function up()
{
// create qsl images table
$this->db->query("CREATE TABLE IF NOT EXISTS `qsl_images`
(`id` integer NOT NULL auto_increment, `qsoid` int, `filename` text, primary key (id))
ENGINE=myisam DEFAULT CHARSET=utf8;");
}
public function down()
{
$this->db->query("");
}
}

查看文件

@ -0,0 +1,63 @@
<?php
class Qsl_model extends CI_Model {
function __construct()
{
// Call the Model constructor
parent::__construct();
}
function getQsoWithQslList() {
$CI =& get_instance();
$CI->load->model('Stations');
$station_id = $CI->Stations->find_active();
$this->db->select('*');
$this->db->from($this->config->item('table_name'));
$this->db->join('qsl_images', 'qsl_images.qsoid = ' . $this->config->item('table_name') . '.col_primary_key');
$this->db->where('station_id', $station_id);
return $this->db->get();
}
function getQslForQsoId($id) {
// Clean ID
$clean_id = $this->security->xss_clean($id);
$this->db->select('*');
$this->db->from('qsl_images');
$this->db->where('qsoid', $clean_id);
return $this->db->get()->result();
}
function saveQsl($qsoid, $filename) {
$data = array(
'qsoid' => $qsoid,
'filename' => $filename
);
$this->db->insert('qsl_images', $data);
return $this->db->insert_id();
}
function deleteQsl($id) {
// Clean ID
$clean_id = $this->security->xss_clean($id);
// Delete Mode
$this->db->delete('qsl_images', array('id' => $clean_id));
}
function getFilename($id) {
// Clean ID
$clean_id = $this->security->xss_clean($id);
$this->db->select('filename');
$this->db->from('qsl_images');
$this->db->where('id', $clean_id);
return $this->db->get();
}
}

查看文件

@ -1449,7 +1449,6 @@ $(document).ready(function(){
L.marker([lat,long], {icon: redIcon}).addTo(mymap)
.bindPopup(callsign);
mymap.on('click', onMapClick);
},
});
@ -2267,5 +2266,160 @@ $(document).ready(function(){
</script>
<?php } ?>
<?php if ($this->uri->segment(1) == "qsl") { ?>
<script>
$('.qsltable').DataTable({
"pageLength": 25,
responsive: false,
ordering: false,
"scrollY": "500px",
"scrollCollapse": true,
"paging": false,
"scrollX": true
});
function viewQsl(picture, callsign) {
var baseURL= "<?php echo base_url();?>";
var $textAndPic = $('<div></div>');
$textAndPic.append('<img class="img-fluid" style="height:auto;width:auto;"src="'+baseURL+'/assets/qslcard/'+picture+'" />');
BootstrapDialog.show({
title: 'QSL Card for ' + callsign,
size: BootstrapDialog.SIZE_WIDE,
message: $textAndPic,
buttons: [{
label: 'Close',
action: function(dialogRef){
dialogRef.close();
}
}]
});
}
</script>
<?php } ?>
<script>
function deleteQsl(id) {
BootstrapDialog.confirm({
title: 'DANGER',
message: 'Warning! Are you sure you want to delete this QSL card?' ,
type: BootstrapDialog.TYPE_DANGER,
closable: true,
draggable: true,
btnOKClass: 'btn-danger',
callback: function(result) {
if(result) {
var baseURL= "<?php echo base_url();?>";
$.ajax({
url: baseURL + 'index.php/qsl/delete',
type: 'post',
data: {'id': id
},
success: function(data) {
$("#" + id).parent("tr:first").remove(); // removes qsl from table
// remove qsl from carousel
$(".carousel-indicators li:last-child").remove();
$(".carouselimageid_"+id).remove();
$('#carouselExampleIndicators').find('.carousel-item').first().addClass('active');
// remove table and hide tab if all qsls are deleted
if ($('.qsltable tr').length == 1) {
$('.qsltable').remove();
$('.qslcardtab').attr('hidden','');
}
}
});
}
}
});
}
</script>
<script>
function uploadQsl() {
var baseURL= "<?php echo base_url();?>";
var formdata = new FormData(document.getElementById("fileinfo"));
$.ajax({
url: baseURL + 'index.php/qsl/uploadqsl',
type: 'post',
data: formdata,
enctype: 'multipart/form-data',
processData: false,
contentType: false,
success: function(data) {
if (data.status.front.status == 'Success') {
if ($('.qsltable').length > 0) {
$('.qsltable tr:last').after('<tr><td style="text-align: center">'+data.status.front.filename+'</td>' +
'<td id="'+data.status.front.insertid+'"style="text-align: center"><button onclick="deleteQsl('+data.status.front.insertid+');" class="btn btn-sm btn-danger">Delete</button></td></tr>');
var quantity = $(".carousel-indicators li").length;
$(".carousel-indicators").append('<li data-target="#carouselExampleIndicators" data-slide-to="'+quantity+'"></li>');
$(".carousel-inner").append('<div class="carousel-item carouselimageid_'+data.status.front.insertid+'"><img class="d-block w-100" src="'+baseURL+'/assets/qslcard/'+data.status.front.filename+'" alt="QSL picture #'+(quantity+1)+'"></div>');
$("#qslcardfront").val(null);
}
else {
$("#qslupload").prepend('<table style="width:100%" class="qsltable table table-sm table-bordered table-hover table-striped table-condensed">'+
'<thead>'+
'<tr>'+
'<th style="text-align: center">QSL image file</th>'+
'<th style="text-align: center"></th>'+
'</tr>'+
'</thead><tbody>'+
'<tr><td style="text-align: center">'+data.status.front.filename+'</td>' +
'<td id="'+data.status.front.insertid+'"style="text-align: center"><button onclick="deleteQsl('+data.status.front.insertid+');" class="btn btn-sm btn-danger">Delete</button></td>' +
'</tr>'+
'</tbody></table>');
$('.qslcardtab').removeAttr('hidden');
var quantity = $(".carousel-indicators li").length;
$(".carousel-indicators").append('<li class="active" data-target="#carouselExampleIndicators" data-slide-to="'+quantity+'"></li>');
$(".carousel-inner").append('<div class="active carousel-item carouselimageid_'+data.status.front.insertid+'"><img class="d-block w-100" src="'+baseURL+'/assets/qslcard/'+data.status.front.filename+'" alt="QSL picture #'+(quantity+1)+'"></div>');
$(".carouselExampleIndicators").carousel();
$("#qslcardfront").val(null);
}
} else {
$("#qslupload").append('<div class="alert alert-danger"><a href="#" class="close" data-dismiss="alert" aria-label="close">&times;</a>\n' +
data.status.front +
'</div>');
}
if (data.status.back.status == 'Success') {
var qsoid = $("#qsoid").text();
if ($('.qsltable').length > 0) {
$('.qsltable tr:last').after('<tr><td style="text-align: center">'+data.status.back.filename+'</td>' +
'<td id="'+data.status.back.insertid+'"style="text-align: center"><button onclick="deleteQsl('+data.status.back.insertid+');" class="btn btn-sm btn-danger">Delete</button></td></tr>');
var quantity = $(".carousel-indicators li").length;
$(".carousel-indicators").append('<li data-target="#carouselExampleIndicators" data-slide-to="'+quantity+'"></li>');
$(".carousel-inner").append('<div class="carousel-item carouselimageid_'+data.status.back.insertid+'"><img class="d-block w-100" src="'+baseURL+'/assets/qslcard/'+data.status.back.filename+'" alt="QSL picture #'+(quantity+1)+'"></div>');
$("#qslcardback").val(null);
}
else {
$("#qslupload").prepend('<table style="width:100%" class="qsltable table table-sm table-bordered table-hover table-striped table-condensed">'+
'<thead>'+
'<tr>'+
'<th style="text-align: center">QSL image file</th>'+
'<th style="text-align: center"></th>'+
'</tr>'+
'</thead><tbody>'+
'<tr><td style="text-align: center">'+data.status.back.filename+'</td>' +
'<td id="'+data.status.back.insertid+'"style="text-align: center"><button onclick="deleteQsl('+data.status.back.insertid+');" class="btn btn-sm btn-danger">Delete</button></td>' +
'</tr>'+
'</tbody></table>');
$('.qslcardtab').removeAttr('hidden');
var quantity = $(".carousel-indicators li").length;
$(".carousel-indicators").append('<li class="active" data-target="#carouselExampleIndicators" data-slide-to="'+quantity+'"></li>');
$(".carousel-inner").append('<div class="active carousel-item carouselimageid_'+data.status.back.insertid+'"><img class="d-block w-100" src="'+baseURL+'/assets/qslcard/'+data.status.back.filename+'" alt="QSL picture #'+(quantity+1)+'"></div>');
$(".carouselExampleIndicators").carousel();
$("#qslcardback").val(null);
}
} else {
$("#qslupload").append('<div class="alert alert-danger"><a href="#" class="close" data-dismiss="alert" aria-label="close">&times;</a>\n' +
data.status.back +
'</div>');
}
}
});
}
</script>
</body>
</html>

查看文件

@ -67,6 +67,8 @@
<a class="dropdown-item" href="<?php echo site_url('qso?manual=0');?>" title="Log Live QSOs">Live QSO</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="<?php echo site_url('qso?manual=1');?>" title="Log QSO made in the past">Post QSO</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="<?php echo site_url('qsl');?>" title="QSL"> View QSL</a>
</div>
</li>

查看文件

@ -0,0 +1,28 @@
<div class="container">
<h2><?php echo $page_title; ?></h2>
<?php
if (is_array($qslarray->result())) {
echo '<table style="width:100%" class="qsltable table table-sm table-bordered table-hover table-striped table-condensed">
<thead>
<tr>
<th style=\'text-align: center\'>Callsign</th>
<th style=\'text-align: center\'>QSL</th>
<th style=\'text-align: center\'></th>
<th style=\'text-align: center\'></th>
</tr>
</thead><tbody>';
foreach ($qslarray->result() as $qsl) {
echo '<tr>';
echo '<td style=\'text-align: center\'>' . $qsl->COL_CALL . '</td>';
echo '<td style=\'text-align: center\'>' . $qsl->filename . '</td>';
echo '<td id="'.$qsl->id.'" style=\'text-align: center\'><button onclick="deleteQsl(\''.$qsl->id.'\')" class="btn btn-sm btn-danger">Delete</button></td>';
echo '<td style=\'text-align: center\'><button onclick="viewQsl(\''.$qsl->filename.'\', \''. $qsl->COL_CALL . '\')" class="btn btn-sm btn-success">View</button></td>';
echo '</tr>';
}
echo '</tbody></table>';
}
?>
</div>

查看文件

@ -0,0 +1,25 @@
<div class="container">
<h2><?php echo $page_title; ?></h2>
<div class="card-body">
<?php if($front != 'Success') { ?>
<div class="alert alert-danger" role="alert">
<?php echo $front; ?>
</div>
<?php } else { ?>
<div class="alert alert-success" role="alert">
Front QSL Card image has been uploaded!
</div>
<?php } ?>
<?php if($back != 'Success') { ?>
<div class="alert alert-danger" role="alert">
<?php echo $back; ?>
</div>
<?php } else { ?>
<div class="alert alert-success" role="alert">
Back QSL Card image has been uploaded!
</div>
<?php } ?>
</div>
</div>

查看文件

@ -1,11 +1,32 @@
<?php if ($query->num_rows() > 0) { foreach ($query->result() as $row) { ?>
<div class="container-fluid">
<div class="row">
<div class="col">
<h3>QSO Details</h3>
</div>
</div>
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="table-tab" data-toggle="tab" href="#qsodetails" role="tab" aria-controls="table" aria-selected="true">QSO Details</a>
</li>
<?php
if (($this->config->item('use_auth')) && ($this->session->userdata('user_type') >= 2)) {
echo '<li ';
if (count($qslimages) == 0) {
echo 'hidden ';
}
echo 'class="qslcardtab nav-item">
<a class="nav-link" id="qsltab" data-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-toggle="tab" href="#qslupload" role="tab" aria-controls="home" aria-selected="false">QSL Card Management</a>
</li>';
}
?>
</ul>
<div class="tab-content" id="myTabContent">
<div class="tab-pane active" id="qsodetails" role="tabpanel" aria-labelledby="home-tab">
<div class="row">
<div class="col">
@ -243,6 +264,7 @@
<?php } ?>
</table>
</div>
<div class="col">
<div id="mapqso" style="width: 340px; height: 250px"></div>
@ -271,6 +293,98 @@
</div>
</div>
<?php
if (($this->config->item('use_auth')) && ($this->session->userdata('user_type') >= 2)) {
?>
<div class="tab-pane fade" id="qslupload" role="tabpanel" aria-labelledby="table-tab">
<?php
if (count($qslimages) > 0) {
echo '<table style="width:100%" class="qsltable table table-sm table-bordered table-hover table-striped table-condensed">
<thead>
<tr>
<th style=\'text-align: center\'>QSL image file</th>
<th style=\'text-align: center\'></th>
</tr>
</thead><tbody>';
foreach ($qslimages as $qsl) {
echo '<tr>';
echo '<td style=\'text-align: center\'>' . $qsl->filename . '</td>';
echo '<td id="'.$qsl->id.'" style=\'text-align: center\'><button onclick="deleteQsl('.$qsl->id.')" class="btn btn-sm btn-danger">Delete</button></td>';
echo '</tr>';
}
echo '</tbody></table>';
}
?>
<form class="form" id="fileinfo" name="fileinfo" enctype="multipart/form-data">
<fieldset>
<div class="form-group">
<label for="qslcardfront">Upload QSL Card front image</label>
<input class="form-control-file" type="file" id="qslcardfront" name="qslcardfront" accept="image/*" capture="environment">
</div>
<div class="form-group">
<label for="qslcardback">Upload QSL card back image</label>
<input class="form-control-file" type="file" id="qslcardback" name="qslcardback" accept="image/*" capture="environment">
</div>
<input type="hidden" class="form-control" id="qsoinputid" name="qsoid" value="<?php echo $row->COL_PRIMARY_KEY; ?>">
<button type="button" onclick="uploadQsl();" id="button1id" name="button1id" class="btn btn-primary">Upload QSL card image</button>
</fieldset>
</form>
</div>
<div class="tab-pane fade" id="qslcard" role="tabpanel" aria-labelledby="table-tab">
<div id="carouselExampleIndicators" class="carousel slide" data-ride="carousel">
<ol class="carousel-indicators">
<?php
$i = 0;
foreach ($qslimages as $image) {
echo '<li data-target="#carouselExampleIndicators" data-slide-to="' . $i . '"';
if ($i == 0) {
echo 'class="active"';
}
$i++;
echo '></li>';
}
?>
</ol>
<div class="carousel-inner">
<?php
$i = 1;
foreach ($qslimages as $image) {
echo '<div class="carousel-item carouselimageid_' . $image->id;
if ($i == 1) {
echo ' active';
}
echo '">';
echo '<img class="d-block w-100" src="' . base_url() . '/assets/qslcard/' . $image->filename .'" alt="QSL picture #'. $i++.'">';
echo '</div>';
}
?>
</div>
<a class="carousel-control-prev" href="#carouselExampleIndicators" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="carousel-control-next" href="#carouselExampleIndicators" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
</div>
<?php
}
?>
</div>
</div>
<?php
if($row->COL_GRIDSQUARE != null) {
$stn_loc = $this->qra->qra2latlong(trim($row->COL_GRIDSQUARE));