当前提交
98c0bafdb4
共有 25 个文件被更改,包括 672 次插入 和 220595 次删除
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
||||||
/application/config/database.php
|
/application/config/database.php
|
||||||
/application/config/config.php
|
/application/config/config.php
|
||||||
|
/uploads/*.adi
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ $config['auth_level'][99] = "Administrator";
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| 'username' QRZ.com Username
|
| 'username' QRZ.com Username
|
||||||
| 'password' Default locator used to calculate bearings/distance
|
| 'password' QRZ.com Password
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$config['qrz_username'] = "";
|
$config['qrz_username'] = "";
|
||||||
|
|
|
||||||
41
application/config/migration.php
普通文件
41
application/config/migration.php
普通文件
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?php defined('BASEPATH') OR exit('No direct script access allowed');
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Enable/Disable Migrations
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Migrations are disabled by default but should be enabled
|
||||||
|
| whenever you intend to do a schema migration.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
$config['migration_enabled'] = TRUE;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Migrations version
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This is used to set migration version that the file system should be on.
|
||||||
|
| If you run $this->migration->latest() this is the version that schema will
|
||||||
|
| be upgraded / downgraded to.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
$config['migration_version'] = 2;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Migrations Path
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Path to your migrations folder.
|
||||||
|
| Typically, it will be within your application path.
|
||||||
|
| Also, writing permission is required within the migrations path.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
$config['migration_path'] = APPPATH . 'migrations/';
|
||||||
|
|
||||||
|
|
||||||
|
/* End of file migration.php */
|
||||||
|
/* Location: ./application/config/migration.php */
|
||||||
|
|
@ -11,7 +11,10 @@ class Dashboard extends CI_Controller {
|
||||||
|
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
|
// Check our version and run any migrations
|
||||||
|
$this->load->library('Migration');
|
||||||
|
$this->migration->current();
|
||||||
|
|
||||||
// Database connections
|
// Database connections
|
||||||
$this->load->model('logbook_model');
|
$this->load->model('logbook_model');
|
||||||
$this->load->model('user_model');
|
$this->load->model('user_model');
|
||||||
|
|
@ -162,4 +165,4 @@ class Dashboard extends CI_Controller {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,95 +11,160 @@ class Lotw extends CI_Controller {
|
||||||
$this->load->model('user_model');
|
$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(!$this->user_model->authorize(2)) { $this->session->set_flashdata('notice', 'You\'re not allowed to do that!'); redirect('dashboard'); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function loadFromFile($filepath)
|
||||||
|
{
|
||||||
|
// Figure out how we should be marking QSLs confirmed via LoTW
|
||||||
|
$query = $query = $this->db->query('SELECT lotw_rcvd_mark FROM config');
|
||||||
|
$q = $query->row();
|
||||||
|
$config['lotw_rcvd_mark'] = $q->lotw_rcvd_mark;
|
||||||
|
|
||||||
|
ini_set('memory_limit', '-1');
|
||||||
|
set_time_limit(0);
|
||||||
|
|
||||||
|
$this->load->library('adif_parser');
|
||||||
|
|
||||||
|
$this->adif_parser->load_from_file($filepath);
|
||||||
|
|
||||||
|
$this->adif_parser->initialize();
|
||||||
|
|
||||||
|
$table = "<table>";
|
||||||
|
|
||||||
|
while($record = $this->adif_parser->get_record())
|
||||||
|
{
|
||||||
|
if(count($record) == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//echo date('Y-m-d', strtotime($record['qso_date']))."<br>";
|
||||||
|
//echo date('H:m', strtotime($record['time_on']))."<br>";
|
||||||
|
|
||||||
|
//$this->logbook_model->import($record);
|
||||||
|
|
||||||
|
//echo $record["call"]."<br>";
|
||||||
|
//print_r($record->);
|
||||||
|
|
||||||
|
$time_on = date('Y-m-d', strtotime($record['qso_date'])) ." ".date('H:i', strtotime($record['time_on']));
|
||||||
|
|
||||||
|
$qsl_date = date('Y-m-d', strtotime($record['qslrdate'])) ." ".date('H:i', strtotime($record['qslrdate']));
|
||||||
|
|
||||||
|
if (isset($record['time_off'])) {
|
||||||
|
$time_off = date('Y-m-d', strtotime($record['qso_date'])) ." ".date('H:i', strtotime($record['time_off']));
|
||||||
|
} else {
|
||||||
|
$time_off = date('Y-m-d', strtotime($record['qso_date'])) ." ".date('H:i', strtotime($record['time_on']));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a positive match from LoTW, record it in the DB according to the user's preferences
|
||||||
|
if ($record['qsl_rcvd'] == "Y")
|
||||||
|
{
|
||||||
|
$record['qsl_rcvd'] = $config['lotw_rcvd_mark'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$status = $this->logbook_model->import_check($time_on, $record['call'], $record['band']);
|
||||||
|
$lotw_status = $this->logbook_model->lotw_update($time_on, $record['call'], $record['band'], $qsl_date, $record['qsl_rcvd']);
|
||||||
|
|
||||||
|
$table .= "<tr>";
|
||||||
|
$table .= "<td>".$time_on."</td>";
|
||||||
|
$table .= "<td>".$record['call']."</td>";
|
||||||
|
$table .= "<td>".$record['mode']."</td>";
|
||||||
|
$table .= "<td>".$record['qsl_rcvd']."</td>";
|
||||||
|
$table .= "<td>".$qsl_date."</td>";
|
||||||
|
$table .= "<td>QSO Record: ".$status."</td>";
|
||||||
|
$table .= "<td>LoTW Record: ".$lotw_status."</td>";
|
||||||
|
$table .= "<tr>";
|
||||||
|
};
|
||||||
|
|
||||||
|
$table .= "</table>";
|
||||||
|
|
||||||
|
unlink($filepath);
|
||||||
|
|
||||||
|
$data['lotw_table'] = $table;
|
||||||
|
|
||||||
|
$data['page_title'] = "LoTW ADIF Information";
|
||||||
|
$this->load->view('layout/header', $data);
|
||||||
|
$this->load->view('lotw/analysis');
|
||||||
|
$this->load->view('layout/footer');
|
||||||
|
}
|
||||||
|
|
||||||
public function import() {
|
public function import() {
|
||||||
$data['page_title'] = "LoTW ADIF Import";
|
$data['page_title'] = "LoTW ADIF Import";
|
||||||
|
|
||||||
$config['upload_path'] = './uploads/';
|
$config['upload_path'] = './uploads/';
|
||||||
$config['allowed_types'] = 'adi|ADI';
|
$config['allowed_types'] = 'adi|ADI';
|
||||||
|
|
||||||
$this->load->library('upload', $config);
|
$this->load->library('upload', $config);
|
||||||
|
|
||||||
if ( ! $this->upload->do_upload())
|
$this->load->model('logbook_model');
|
||||||
{
|
|
||||||
$data['error'] = $this->upload->display_errors();
|
if ($this->input->post('lotwimport') == 'fetch')
|
||||||
|
{
|
||||||
$this->load->view('layout/header', $data);
|
$file = $config['upload_path'] . 'lotwreport_download.adi';
|
||||||
$this->load->view('lotw/import');
|
|
||||||
$this->load->view('layout/footer');
|
// Get credentials for LoTW
|
||||||
|
$query = $this->user_model->get_by_id($this->session->userdata('user_id'));
|
||||||
|
$q = $query->row();
|
||||||
|
$data['user_lotw_name'] = $q->user_lotw_name;
|
||||||
|
$data['user_lotw_password'] = $q->user_lotw_password;
|
||||||
|
|
||||||
|
// Get URL for downloading LoTW
|
||||||
|
$query = $query = $this->db->query('SELECT lotw_download_url FROM config');
|
||||||
|
$q = $query->row();
|
||||||
|
$lotw_url = $q->lotw_download_url;
|
||||||
|
|
||||||
|
// Validate that LoTW credentials are not empty
|
||||||
|
// TODO: We don't actually see the error message
|
||||||
|
if ($data['user_lotw_name'] == '' || $data['user_lotw_password'] == '')
|
||||||
|
{
|
||||||
|
$this->session->set_flashdata('warning', 'You have not defined your ARRL LoTW credentials!'); redirect('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query the logbook to determine when the last LoTW confirmation was
|
||||||
|
$lotw_last_qsl_date = $this->logbook_model->lotw_last_qsl_date();
|
||||||
|
|
||||||
|
// TODO: Consolidate code
|
||||||
|
// TODO: Specifiy in config file whether we want LoTW confirms as V or Y. Both are acceptable under ADIF specification. HRD seems to use V. Everyone else that I've used uses Y.
|
||||||
|
|
||||||
|
// Build URL for LoTW report file
|
||||||
|
$lotw_url .= "?";
|
||||||
|
$lotw_url .= "login=" . $data['user_lotw_name'];
|
||||||
|
$lotw_url .= "&password=" . $data['user_lotw_password'];
|
||||||
|
$lotw_url .= "&qso_query=1&qso_qsl='yes'";
|
||||||
|
|
||||||
|
//TODO: Option to specifiy whether we download location data from LoTW or not
|
||||||
|
//$lotw_url .= "&qso_qsldetail=\"yes\";
|
||||||
|
|
||||||
|
$lotw_url .= "&qso_qslsince=";
|
||||||
|
$lotw_url .= "$lotw_last_qsl_date";
|
||||||
|
|
||||||
|
// Only pull back entries that belong to this callsign
|
||||||
|
$lotw_call = $this->session->userdata('user_callsign');
|
||||||
|
$lotw_url .= "&qso_owncall=$lotw_call";
|
||||||
|
|
||||||
|
file_put_contents($file, file_get_contents($lotw_url));
|
||||||
|
|
||||||
|
ini_set('memory_limit', '-1');
|
||||||
|
$this->loadFromFile($file);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if ( ! $this->upload->do_upload())
|
||||||
$data = array('upload_data' => $this->upload->data());
|
|
||||||
|
|
||||||
ini_set('memory_limit', '-1');
|
|
||||||
set_time_limit(0);
|
|
||||||
|
|
||||||
$this->load->model('logbook_model');
|
|
||||||
|
|
||||||
$this->load->library('adif_parser');
|
|
||||||
|
|
||||||
$this->adif_parser->load_from_file('./uploads/'.$data['upload_data']['file_name']);
|
|
||||||
|
|
||||||
$this->adif_parser->initialize();
|
|
||||||
|
|
||||||
$table = "<table>";
|
|
||||||
|
|
||||||
while($record = $this->adif_parser->get_record())
|
|
||||||
{
|
{
|
||||||
if(count($record) == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//echo date('Y-m-d', strtotime($record['qso_date']))."<br>";
|
|
||||||
//echo date('H:m', strtotime($record['time_on']))."<br>";
|
|
||||||
|
|
||||||
//$this->logbook_model->import($record);
|
|
||||||
|
|
||||||
//echo $record["call"]."<br>";
|
|
||||||
//print_r($record->);
|
|
||||||
|
|
||||||
$time_on = date('Y-m-d', strtotime($record['qso_date'])) ." ".date('H:i', strtotime($record['time_on']));
|
|
||||||
|
|
||||||
$qsl_date = date('Y-m-d', strtotime($record['qslrdate'])) ." ".date('H:i', strtotime($record['qslrdate']));
|
|
||||||
|
|
||||||
if (isset($record['time_off'])) {
|
|
||||||
$time_off = date('Y-m-d', strtotime($record['qso_date'])) ." ".date('H:i', strtotime($record['time_off']));
|
|
||||||
} else {
|
|
||||||
$time_off = date('Y-m-d', strtotime($record['qso_date'])) ." ".date('H:i', strtotime($record['time_on']));
|
|
||||||
}
|
|
||||||
|
|
||||||
$status = $this->logbook_model->import_check($time_on, $record['call'], $record['band']);
|
|
||||||
$lotw_status = $this->logbook_model->lotw_update($time_on, $record['call'], $record['band'], $qsl_date, $record['qsl_rcvd']);
|
|
||||||
|
|
||||||
$table .= "<tr>";
|
|
||||||
$table .= "<td>".$time_on."</td>";
|
|
||||||
$table .= "<td>".$record['call']."</td>";
|
|
||||||
$table .= "<td>".$record['mode']."</td>";
|
|
||||||
$table .= "<td>".$record['qsl_rcvd']."</td>";
|
|
||||||
$table .= "<td>".$qsl_date."</td>";
|
|
||||||
$table .= "<td>QSO Record: ".$status."</td>";
|
|
||||||
$table .= "<td>LoTW Record: ".$lotw_status."</td>";
|
|
||||||
$table .= "<tr>";
|
|
||||||
};
|
|
||||||
|
|
||||||
$table .= "</table>";
|
$data['error'] = $this->upload->display_errors();
|
||||||
|
|
||||||
unlink('./uploads/'.$data['upload_data']['file_name']);
|
|
||||||
|
|
||||||
$data['lotw_table'] = $table;
|
|
||||||
|
|
||||||
$data['page_title'] = "LoTW ADIF Information";
|
|
||||||
$this->load->view('layout/header', $data);
|
|
||||||
$this->load->view('lotw/analysis');
|
|
||||||
$this->load->view('layout/footer');
|
|
||||||
|
|
||||||
|
$this->load->view('layout/header', $data);
|
||||||
|
$this->load->view('lotw/import');
|
||||||
|
$this->load->view('layout/footer');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$data = array('upload_data' => $this->upload->data());
|
||||||
|
|
||||||
|
$this->loadFromFile('./uploads/'.$data['upload_data']['file_name']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
} // end function
|
||||||
|
} // end class
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<? class Migrate extends CI_Controller {
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$this->load->library('Migration');
|
||||||
|
|
||||||
|
if ( ! $this->migration->current()) {
|
||||||
|
show_error($this->migration->error_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} ?>
|
||||||
|
|
@ -190,6 +190,18 @@ class User extends CI_Controller {
|
||||||
$data['user_timezone'] = $q->user_timezone;
|
$data['user_timezone'] = $q->user_timezone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($this->input->post('user_lotw_name')) {
|
||||||
|
$data['user_lotw_name'] = $this->input->post('user_lotw_name');
|
||||||
|
} else {
|
||||||
|
$data['user_lotw_name'] = $q->user_lotw_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->input->post('user_lotw_password')) {
|
||||||
|
$data['user_lotw_password'] = $this->input->post('user_lotw_password');
|
||||||
|
} else {
|
||||||
|
$data['user_lotw_password'] = $q->user_lotw_password;
|
||||||
|
}
|
||||||
|
|
||||||
$this->load->view('user/edit', $data);
|
$this->load->view('user/edit', $data);
|
||||||
$this->load->view('layout/footer');
|
$this->load->view('layout/footer');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
<?php defined('BASEPATH') OR exit('No direct script access allowed');
|
|
||||||
|
|
||||||
class Migration_Create_accounts extends CI_Migration {
|
|
||||||
|
|
||||||
function up()
|
|
||||||
{
|
|
||||||
if ( ! $this->db->table_exists('accounts'))
|
|
||||||
{
|
|
||||||
// Setup Keys
|
|
||||||
$this->dbforge->add_key('id', TRUE);
|
|
||||||
|
|
||||||
$this->dbforge->add_field(array(
|
|
||||||
'id' => array('type' => 'INT', 'constraint' => 5, 'unsigned' => TRUE, 'auto_increment' => TRUE),
|
|
||||||
'company_name' => array('type' => 'VARCHAR', 'constraint' => '200', 'null' => FALSE),
|
|
||||||
'first_name' => array('type' => 'VARCHAR', 'constraint' => '200', 'null' => FALSE),
|
|
||||||
'last_name' => array('type' => 'VARCHAR', 'constraint' => '200', 'null' => FALSE),
|
|
||||||
'phone' => array('type' => 'TEXT', 'null' => FALSE),
|
|
||||||
'email' => array('type' => 'TEXT', 'null' => FALSE),
|
|
||||||
'address' => array('type' => 'TEXT', 'null' => FALSE),
|
|
||||||
'Last_Update' => array('type' => 'DATETIME', 'null' => FALSE)
|
|
||||||
));
|
|
||||||
|
|
||||||
$this->dbforge->add_field("Created_At TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP");
|
|
||||||
$this->dbforge->create_table('accounts', TRUE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function down()
|
|
||||||
{
|
|
||||||
$this->dbforge->drop_table('accounts');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php defined('BASEPATH') OR exit('No direct script access allowed');
|
||||||
|
|
||||||
|
class Migration_add_lotw_credentials extends CI_Migration {
|
||||||
|
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
$fields = array(
|
||||||
|
'user_lotw_name VARCHAR(32) DEFAULT NULL',
|
||||||
|
'user_lotw_password VARCHAR(64) DEFAULT NULL'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->dbforge->add_column('users', $fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
$this->dbforge->drop_column('users', 'user_lotw_name');
|
||||||
|
$this->dbforge->drop_column('users', 'user_lotw_password');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?php defined('BASEPATH') OR exit('No direct script access allowed');
|
||||||
|
|
||||||
|
class Migration_add_config_table extends CI_Migration {
|
||||||
|
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
$this->dbforge->add_field('id');
|
||||||
|
|
||||||
|
$this->dbforge->add_field(array(
|
||||||
|
'lotw_download_url' => array(
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 255,
|
||||||
|
),
|
||||||
|
'lotw_upload_url' => array(
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 255,
|
||||||
|
),
|
||||||
|
'lotw_rcvd_mark' => array(
|
||||||
|
'type' => 'VARCHAR',
|
||||||
|
'constraint' => 1,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
$this->dbforge->create_table('config');
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'lotw_download_url' => 'https://p1k.arrl.org/lotwuser/lotwreport.adi',
|
||||||
|
'lotw_upload_url' => 'https://p1k.arrl.org/lotwuser/upload',
|
||||||
|
'lotw_rcvd_mark' => 'Y'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->db->insert('config', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
$this->dbforge->drop_table('config');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
@ -585,6 +585,18 @@ class Logbook_model extends CI_Model {
|
||||||
|
|
||||||
return "Updated";
|
return "Updated";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function lotw_last_qsl_date() {
|
||||||
|
$this->db->select('COL_LOTW_QSLRDATE');
|
||||||
|
$this->db->where('COL_LOTW_QSLRDATE IS NOT NULL');
|
||||||
|
$this->db->order_by("COL_LOTW_QSLRDATE", "desc");
|
||||||
|
$this->db->limit(1);
|
||||||
|
|
||||||
|
$query = $this->db->get($this->config->item('table_name'));
|
||||||
|
$row = $query->row();
|
||||||
|
|
||||||
|
return $row->COL_LOTW_QSLRDATE;
|
||||||
|
}
|
||||||
|
|
||||||
function import($record) {
|
function import($record) {
|
||||||
// Join date+time
|
// Join date+time
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,9 @@ class User_Model extends CI_Model {
|
||||||
'user_locator' => $fields['user_locator'],
|
'user_locator' => $fields['user_locator'],
|
||||||
'user_firstname' => $fields['user_firstname'],
|
'user_firstname' => $fields['user_firstname'],
|
||||||
'user_lastname' => $fields['user_lastname'],
|
'user_lastname' => $fields['user_lastname'],
|
||||||
'user_timezone' => $fields['user_timezone']
|
'user_timezone' => $fields['user_timezone'],
|
||||||
|
'user_lotw_name' => $fields['user_lotw_name'],
|
||||||
|
'user_lotw_password' => $fields['user_lotw_password']
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check to see if the user is allowed to change user levels
|
// Check to see if the user is allowed to change user levels
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,24 @@
|
||||||
<div id="container">
|
<div id="container">
|
||||||
<h2><?php echo $page_title; ?></h2>
|
<h2><?php echo $page_title; ?></h2>
|
||||||
|
|
||||||
<p>Upload the Exported ADIF file from LoTW from the <a href="https://p1k.arrl.org/lotwuser/qsos?qsoscmd=adif" target="_blank">Download Report</a> Area, to mark QSOs as confirmed on LOTW.</p>
|
|
||||||
|
|
||||||
<p><span class="label important">Important</span> Log files must have the file type .adi</p>
|
|
||||||
|
|
||||||
<?php echo form_open_multipart('lotw/import');?>
|
<?php echo form_open_multipart('lotw/import');?>
|
||||||
|
<table>
|
||||||
<input type="file" name="userfile" size="20" />
|
<tr>
|
||||||
|
<td><input type="radio" name="lotwimport" id="upload" value="upload" checked <?=set_radio('lotwimport','upload') ?> /> Upload a file</td>
|
||||||
<br /><br />
|
<td>
|
||||||
|
<p>Upload the Exported ADIF file from LoTW from the <a href="https://p1k.arrl.org/lotwuser/qsos?qsoscmd=adif" target="_blank">Download Report</a> Area, to mark QSOs as confirmed on LOTW.</p>
|
||||||
<input class="btn primary" type="submit" value="Upload" />
|
<p><span class="label important">Important</span> Log files must have the file type .adi</p>
|
||||||
|
<input type="file" name="userfile" size="20" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><input type="radio" name="lotwimport" id="fetch" value="fetch" <?=set_radio('lotwimport','fetch') ?> /> Pull LoTW data for me</td>
|
||||||
|
<td>
|
||||||
|
<p>Cloudlog will use the LoTW username an password stored in your user profile to download a report from LoTW for you. The report Cloudlog downloads will have all confirmations since your last LoTW confirmation, up until now.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<input class="btn primary" type="submit" value="Import" />
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -159,8 +159,8 @@
|
||||||
<option value="N" <?php if($COL_QSL_RCVD == "N") { echo "selected=\"selected\""; } ?>>No</option>
|
<option value="N" <?php if($COL_QSL_RCVD == "N") { echo "selected=\"selected\""; } ?>>No</option>
|
||||||
<option value="Y" <?php if($COL_QSL_RCVD == "Y") { echo "selected=\"selected\""; } ?>>Yes</option>
|
<option value="Y" <?php if($COL_QSL_RCVD == "Y") { echo "selected=\"selected\""; } ?>>Yes</option>
|
||||||
<option value="R" <?php if($COL_QSL_RCVD == "R") { echo "selected=\"selected\""; } ?>>Requested</option>
|
<option value="R" <?php if($COL_QSL_RCVD == "R") { echo "selected=\"selected\""; } ?>>Requested</option>
|
||||||
<option value="Q" <?php if($COL_QSL_RCVD == "R") { echo "selected=\"selected\""; } ?>>Invalid (Ignore)</option>
|
<option value="Q" <?php if($COL_QSL_RCVD == "I") { echo "selected=\"selected\""; } ?>>Invalid (Ignore)</option>
|
||||||
<option value="I" <?php if($COL_QSL_RCVD == "R") { echo "selected=\"selected\""; } ?>>Verified (Match)</option>
|
<option value="I" <?php if($COL_QSL_RCVD == "V") { echo "selected=\"selected\""; } ?>>Verified (Match)</option>
|
||||||
</select></td>
|
</select></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -203,8 +203,8 @@
|
||||||
<option value="N" <?php if($COL_EQSL_QSL_RCVD == "N") { echo "selected=\"selected\""; } ?>>No</option>
|
<option value="N" <?php if($COL_EQSL_QSL_RCVD == "N") { echo "selected=\"selected\""; } ?>>No</option>
|
||||||
<option value="Y" <?php if($COL_EQSL_QSL_RCVD == "Y") { echo "selected=\"selected\""; } ?>>Yes</option>
|
<option value="Y" <?php if($COL_EQSL_QSL_RCVD == "Y") { echo "selected=\"selected\""; } ?>>Yes</option>
|
||||||
<option value="R" <?php if($COL_EQSL_QSL_RCVD == "R") { echo "selected=\"selected\""; } ?>>Requested</option>
|
<option value="R" <?php if($COL_EQSL_QSL_RCVD == "R") { echo "selected=\"selected\""; } ?>>Requested</option>
|
||||||
<option value="Q" <?php if($COL_EQSL_QSL_RCVD == "R") { echo "selected=\"selected\""; } ?>>Invalid (Ignore)</option>
|
<option value="I" <?php if($COL_EQSL_QSL_RCVD == "I") { echo "selected=\"selected\""; } ?>>Invalid (Ignore)</option>
|
||||||
<option value="I" <?php if($COL_EQSL_QSL_RCVD == "R") { echo "selected=\"selected\""; } ?>>Verified (Match)</option>
|
<option value="V" <?php if($COL_EQSL_QSL_RCVD == "V") { echo "selected=\"selected\""; } ?>>Verified (Match)</option>
|
||||||
</select></td>
|
</select></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
@ -225,8 +225,8 @@
|
||||||
<option value="N" <?php if($COL_LOTW_QSL_RCVD == "N") { echo "selected=\"selected\""; } ?>>No</option>
|
<option value="N" <?php if($COL_LOTW_QSL_RCVD == "N") { echo "selected=\"selected\""; } ?>>No</option>
|
||||||
<option value="Y" <?php if($COL_LOTW_QSL_RCVD == "Y") { echo "selected=\"selected\""; } ?>>Yes</option>
|
<option value="Y" <?php if($COL_LOTW_QSL_RCVD == "Y") { echo "selected=\"selected\""; } ?>>Yes</option>
|
||||||
<option value="R" <?php if($COL_LOTW_QSL_RCVD == "R") { echo "selected=\"selected\""; } ?>>Requested</option>
|
<option value="R" <?php if($COL_LOTW_QSL_RCVD == "R") { echo "selected=\"selected\""; } ?>>Requested</option>
|
||||||
<option value="Q" <?php if($COL_LOTW_QSL_RCVD == "R") { echo "selected=\"selected\""; } ?>>Invalid (Ignore)</option>
|
<option value="I" <?php if($COL_LOTW_QSL_RCVD == "I") { echo "selected=\"selected\""; } ?>>Invalid (Ignore)</option>
|
||||||
<option value="I" <?php if($COL_LOTW_QSL_RCVD == "R") { echo "selected=\"selected\""; } ?>>Verified (Match)</option>
|
<option value="V" <?php if($COL_LOTW_QSL_RCVD == "V") { echo "selected=\"selected\""; } ?>>Verified (Match)</option>
|
||||||
</select></td>
|
</select></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,20 @@ $this->load->helper('form');
|
||||||
<td><?php echo form_dropdown('user_timezone', $timezones, $user_timezone); ?></td>
|
<td><?php echo form_dropdown('user_timezone', $timezones, $user_timezone); ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>Logbook of The World (LoTW) Username</td>
|
||||||
|
<td><input type="text" name="user_lotw_name" value="<?php if(isset($user_lotw_name)) { echo $user_lotw_name; } ?>" />
|
||||||
|
<?php if(isset($userlotwname_error)) { echo "<div class=\"small error\">".$userlotwname_error."</div>"; } ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>Logbook of The World (LoTW) Password</td>
|
||||||
|
<td><input type="password" name="user_lotw_password" />
|
||||||
|
<?php if(isset($lotwpassword_error)) { echo "<div class=\"small error\">".$lotwpassword_error."</div>"; } else { ?>
|
||||||
|
<div class="small">Leave blank to keep existing password</div></td>
|
||||||
|
<?php } ?>
|
||||||
|
</tr>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
<input type="hidden" name="id" value="<?php echo $this->uri->segment(3); ?>" />
|
<input type="hidden" name="id" value="<?php echo $this->uri->segment(3); ?>" />
|
||||||
|
|
|
||||||
2
install/assets/install.sql
普通文件 -> 可执行文件
2
install/assets/install.sql
普通文件 -> 可执行文件
|
|
@ -3815,6 +3815,8 @@ CREATE TABLE IF NOT EXISTS `users` (
|
||||||
`user_locator` varchar(255) NOT NULL,
|
`user_locator` varchar(255) NOT NULL,
|
||||||
`user_firstname` varchar(255) NOT NULL,
|
`user_firstname` varchar(255) NOT NULL,
|
||||||
`user_lastname` varchar(255) NOT NULL,
|
`user_lastname` varchar(255) NOT NULL,
|
||||||
|
`user_lotw_name` varchar(32) NULL COMMENT 'LoTW Username',
|
||||||
|
`user_lotw_password` varchar(64) NULL COMMENT 'LoTW Password',
|
||||||
PRIMARY KEY (`user_id`),
|
PRIMARY KEY (`user_id`),
|
||||||
UNIQUE KEY `user_name` (`user_name`),
|
UNIQUE KEY `user_name` (`user_name`),
|
||||||
UNIQUE KEY `user_email` (`user_email`)
|
UNIQUE KEY `user_email` (`user_email`)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$lang['migration_none_found'] = "No migrations were found.";
|
||||||
|
$lang['migration_not_found'] = "This migration could not be found.";
|
||||||
|
$lang['migration_multiple_version'] = "This are multiple migrations with the same version number: %d.";
|
||||||
|
$lang['migration_class_doesnt_exist'] = "The migration class \"%s\" could not be found.";
|
||||||
|
$lang['migration_missing_up_method'] = "The migration class \"%s\" is missing an 'up' method.";
|
||||||
|
$lang['migration_missing_down_method'] = "The migration class \"%s\" is missing an 'down' method.";
|
||||||
|
$lang['migration_invalid_filename'] = "Migration \"%s\" has an invalid filename.";
|
||||||
|
|
||||||
|
|
||||||
|
/* End of file migration_lang.php */
|
||||||
|
/* Location: ./system/language/english/migration_lang.php */
|
||||||
328
system/libraries/Migration.php
普通文件
328
system/libraries/Migration.php
普通文件
|
|
@ -0,0 +1,328 @@
|
||||||
|
<?php defined('BASEPATH') OR exit('No direct script access allowed');
|
||||||
|
/**
|
||||||
|
* CodeIgniter
|
||||||
|
*
|
||||||
|
* An open source application development framework for PHP 5.1.6 or newer
|
||||||
|
*
|
||||||
|
* @package CodeIgniter
|
||||||
|
* @author EllisLab Dev Team
|
||||||
|
* @copyright Copyright (c) 2006 - 2012, EllisLab, Inc.
|
||||||
|
* @license http://codeigniter.com/user_guide/license.html
|
||||||
|
* @link http://codeigniter.com
|
||||||
|
* @since Version 1.0
|
||||||
|
* @filesource
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migration Class
|
||||||
|
*
|
||||||
|
* All migrations should implement this, forces up() and down() and gives
|
||||||
|
* access to the CI super-global.
|
||||||
|
*
|
||||||
|
* @package CodeIgniter
|
||||||
|
* @subpackage Libraries
|
||||||
|
* @category Libraries
|
||||||
|
* @author Reactor Engineers
|
||||||
|
* @link
|
||||||
|
*/
|
||||||
|
class CI_Migration {
|
||||||
|
|
||||||
|
protected $_migration_enabled = FALSE;
|
||||||
|
protected $_migration_path = NULL;
|
||||||
|
protected $_migration_version = 0;
|
||||||
|
|
||||||
|
protected $_error_string = '';
|
||||||
|
|
||||||
|
public function __construct($config = array())
|
||||||
|
{
|
||||||
|
# Only run this constructor on main library load
|
||||||
|
if (get_parent_class($this) !== FALSE)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($config as $key => $val)
|
||||||
|
{
|
||||||
|
$this->{'_' . $key} = $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_message('debug', 'Migrations class initialized');
|
||||||
|
|
||||||
|
// Are they trying to use migrations while it is disabled?
|
||||||
|
if ($this->_migration_enabled !== TRUE)
|
||||||
|
{
|
||||||
|
show_error('Migrations has been loaded but is disabled or set up incorrectly.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not set, set it
|
||||||
|
$this->_migration_path == '' AND $this->_migration_path = APPPATH . 'migrations/';
|
||||||
|
|
||||||
|
// Add trailing slash if not set
|
||||||
|
$this->_migration_path = rtrim($this->_migration_path, '/').'/';
|
||||||
|
|
||||||
|
// Load migration language
|
||||||
|
$this->lang->load('migration');
|
||||||
|
|
||||||
|
// They'll probably be using dbforge
|
||||||
|
$this->load->dbforge();
|
||||||
|
|
||||||
|
// If the migrations table is missing, make it
|
||||||
|
if ( ! $this->db->table_exists('migrations'))
|
||||||
|
{
|
||||||
|
$this->dbforge->add_field(array(
|
||||||
|
'version' => array('type' => 'INT', 'constraint' => 3),
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->dbforge->create_table('migrations', TRUE);
|
||||||
|
|
||||||
|
$this->db->insert('migrations', array('version' => 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate to a schema version
|
||||||
|
*
|
||||||
|
* Calls each migration step required to get to the schema version of
|
||||||
|
* choice
|
||||||
|
*
|
||||||
|
* @param int Target schema version
|
||||||
|
* @return mixed TRUE if already latest, FALSE if failed, int if upgraded
|
||||||
|
*/
|
||||||
|
public function version($target_version)
|
||||||
|
{
|
||||||
|
$start = $current_version = $this->_get_version();
|
||||||
|
$stop = $target_version;
|
||||||
|
|
||||||
|
if ($target_version > $current_version)
|
||||||
|
{
|
||||||
|
// Moving Up
|
||||||
|
++$start;
|
||||||
|
++$stop;
|
||||||
|
$step = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Moving Down
|
||||||
|
$step = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$method = ($step === 1) ? 'up' : 'down';
|
||||||
|
$migrations = array();
|
||||||
|
|
||||||
|
// We now prepare to actually DO the migrations
|
||||||
|
// But first let's make sure that everything is the way it should be
|
||||||
|
for ($i = $start; $i != $stop; $i += $step)
|
||||||
|
{
|
||||||
|
$f = glob(sprintf($this->_migration_path . '%03d_*.php', $i));
|
||||||
|
|
||||||
|
// Only one migration per step is permitted
|
||||||
|
if (count($f) > 1)
|
||||||
|
{
|
||||||
|
$this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $i);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migration step not found
|
||||||
|
if (count($f) == 0)
|
||||||
|
{
|
||||||
|
// If trying to migrate up to a version greater than the last
|
||||||
|
// existing one, migrate to the last one.
|
||||||
|
if ($step == 1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If trying to migrate down but we're missing a step,
|
||||||
|
// something must definitely be wrong.
|
||||||
|
$this->_error_string = sprintf($this->lang->line('migration_not_found'), $i);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = basename($f[0]);
|
||||||
|
$name = basename($f[0], '.php');
|
||||||
|
|
||||||
|
// Filename validations
|
||||||
|
if (preg_match('/^\d{3}_(\w+)$/', $name, $match))
|
||||||
|
{
|
||||||
|
$match[1] = strtolower($match[1]);
|
||||||
|
|
||||||
|
// Cannot repeat a migration at different steps
|
||||||
|
if (in_array($match[1], $migrations))
|
||||||
|
{
|
||||||
|
$this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $match[1]);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
include $f[0];
|
||||||
|
$class = 'Migration_' . ucfirst($match[1]);
|
||||||
|
|
||||||
|
if ( ! class_exists($class))
|
||||||
|
{
|
||||||
|
$this->_error_string = sprintf($this->lang->line('migration_class_doesnt_exist'), $class);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! is_callable(array($class, $method)))
|
||||||
|
{
|
||||||
|
$this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$migrations[] = $match[1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->_error_string = sprintf($this->lang->line('migration_invalid_filename'), $file);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log_message('debug', 'Current migration: ' . $current_version);
|
||||||
|
|
||||||
|
$version = $i + ($step == 1 ? -1 : 0);
|
||||||
|
|
||||||
|
// If there is nothing to do so quit
|
||||||
|
if ($migrations === array())
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_message('debug', 'Migrating from ' . $method . ' to version ' . $version);
|
||||||
|
|
||||||
|
// Loop through the migrations
|
||||||
|
foreach ($migrations AS $migration)
|
||||||
|
{
|
||||||
|
// Run the migration class
|
||||||
|
$class = 'Migration_' . ucfirst(strtolower($migration));
|
||||||
|
call_user_func(array(new $class, $method));
|
||||||
|
|
||||||
|
$current_version += $step;
|
||||||
|
$this->_update_version($current_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_message('debug', 'Finished migrating to '.$current_version);
|
||||||
|
|
||||||
|
return $current_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set's the schema to the latest migration
|
||||||
|
*
|
||||||
|
* @return mixed true if already latest, false if failed, int if upgraded
|
||||||
|
*/
|
||||||
|
public function latest()
|
||||||
|
{
|
||||||
|
if ( ! $migrations = $this->find_migrations())
|
||||||
|
{
|
||||||
|
$this->_error_string = $this->line->lang('migration_none_found');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$last_migration = basename(end($migrations));
|
||||||
|
|
||||||
|
// Calculate the last migration step from existing migration
|
||||||
|
// filenames and procceed to the standard version migration
|
||||||
|
return $this->version((int) substr($last_migration, 0, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set's the schema to the migration version set in config
|
||||||
|
*
|
||||||
|
* @return mixed true if already current, false if failed, int if upgraded
|
||||||
|
*/
|
||||||
|
public function current()
|
||||||
|
{
|
||||||
|
return $this->version($this->_migration_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error string
|
||||||
|
*
|
||||||
|
* @return string Error message returned as a string
|
||||||
|
*/
|
||||||
|
public function error_string()
|
||||||
|
{
|
||||||
|
return $this->_error_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set's the schema to the latest migration
|
||||||
|
*
|
||||||
|
* @return mixed true if already latest, false if failed, int if upgraded
|
||||||
|
*/
|
||||||
|
protected function find_migrations()
|
||||||
|
{
|
||||||
|
// Load all *_*.php files in the migrations path
|
||||||
|
$files = glob($this->_migration_path . '*_*.php');
|
||||||
|
$file_count = count($files);
|
||||||
|
|
||||||
|
for ($i = 0; $i < $file_count; $i++)
|
||||||
|
{
|
||||||
|
// Mark wrongly formatted files as false for later filtering
|
||||||
|
$name = basename($files[$i], '.php');
|
||||||
|
if ( ! preg_match('/^\d{3}_(\w+)$/', $name))
|
||||||
|
{
|
||||||
|
$files[$i] = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort($files);
|
||||||
|
return $files;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves current schema version
|
||||||
|
*
|
||||||
|
* @return int Current Migration
|
||||||
|
*/
|
||||||
|
protected function _get_version()
|
||||||
|
{
|
||||||
|
$row = $this->db->get('migrations')->row();
|
||||||
|
return $row ? $row->version : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the current schema version
|
||||||
|
*
|
||||||
|
* @param int Migration reached
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function _update_version($migrations)
|
||||||
|
{
|
||||||
|
return $this->db->update('migrations', array(
|
||||||
|
'version' => $migrations
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the use of CI super-global
|
||||||
|
*
|
||||||
|
* @param mixed $var
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function __get($var)
|
||||||
|
{
|
||||||
|
return get_instance()->$var;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* End of file Migration.php */
|
||||||
|
/* Location: ./system/libraries/Migration.php */
|
||||||
31495
uploads/lotwreport.adi
31495
uploads/lotwreport.adi
文件差异内容过多而无法显示
加载差异
31495
uploads/lotwreport1.adi
31495
uploads/lotwreport1.adi
文件差异内容过多而无法显示
加载差异
31495
uploads/lotwreport2.adi
31495
uploads/lotwreport2.adi
文件差异内容过多而无法显示
加载差异
31495
uploads/lotwreport3.adi
31495
uploads/lotwreport3.adi
文件差异内容过多而无法显示
加载差异
31495
uploads/lotwreport4.adi
31495
uploads/lotwreport4.adi
文件差异内容过多而无法显示
加载差异
31495
uploads/lotwreport5.adi
31495
uploads/lotwreport5.adi
文件差异内容过多而无法显示
加载差异
31495
uploads/lotwreport6.adi
31495
uploads/lotwreport6.adi
文件差异内容过多而无法显示
加载差异
正在加载…
在新工单中引用