View | Details | Raw Unified | Return to bug 39190
Collapse All | Expand All

(-)a/Koha/BackgroundJob.pm (+1 lines)
Lines 446-451 sub core_types_to_classes { Link Here
446
        marc_import_revert_batch            => 'Koha::BackgroundJob::MARCImportRevertBatch',
446
        marc_import_revert_batch            => 'Koha::BackgroundJob::MARCImportRevertBatch',
447
        pseudonymize_statistic              => 'Koha::BackgroundJob::PseudonymizeStatistic',
447
        pseudonymize_statistic              => 'Koha::BackgroundJob::PseudonymizeStatistic',
448
        import_from_kbart_file              => 'Koha::BackgroundJob::ImportKBARTFile',
448
        import_from_kbart_file              => 'Koha::BackgroundJob::ImportKBARTFile',
449
        file_transport_test                 => 'Koha::BackgroundJob::TestTransport',
449
    };
450
    };
450
}
451
}
451
452
(-)a/Koha/BackgroundJob/TestTransport.pm (+91 lines)
Line 0 Link Here
1
package Koha::BackgroundJob::TestTransport;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use JSON qw( decode_json encode_json );
21
use Try::Tiny;
22
use Koha::File::Transports;
23
24
use base 'Koha::BackgroundJob';
25
26
=head1 NAME
27
28
Koha::BackgroundJob::TestTransport - Background job derived class to test File::Transports
29
30
=head1 API
31
32
=head2 Class methods
33
34
=head3 job_type
35
36
Define the job type of this job: file_transport_test
37
38
=cut
39
40
sub job_type {
41
    return 'file_transport_test';
42
}
43
44
=head3 process
45
46
Process the transport test.
47
48
=cut
49
50
sub process {
51
    my ( $self, $args ) = @_;
52
    $self->start;
53
54
    my $transport = Koha::File::Transports->find( $args->{transport_id} );
55
    $transport->test_connection;
56
    my $status   = { status => 'ok' };
57
    my $messages = $transport->object_messages;
58
    for my $message (@$messages) {
59
        $status->{status} = 'errors' if $message->{type} eq 'error';
60
        push @{ $status->{operations} },
61
            { code => $message->{message}, status => $message->{type}, detail => $message->{payload} };
62
    }
63
    $transport->set( { status => encode_json($status) } )->store();
64
65
    my $data = $status;
66
    $self->finish($data);
67
}
68
69
=head3 enqueue
70
71
Enqueue the new job
72
73
=cut
74
75
sub enqueue {
76
    my ( $self, $args ) = @_;
77
78
    my $transport = $args->{transport_id};
79
    Koha::Exceptions::MissingParameter->throw("Missing transport_id parameter is mandatory")
80
        unless $transport;
81
82
    $self->SUPER::enqueue(
83
        {
84
            job_size  => 1,
85
            job_args  => {%$args},
86
            job_queue => 'default',
87
        }
88
    );
89
}
90
91
1;
(-)a/Koha/File/Transport.pm (-10 / +53 lines)
Lines 16-27 package Koha::File::Transport; Link Here
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
use constant {
20
use constant {
20
    DEFAULT_TIMEOUT   => 10,
21
    DEFAULT_TIMEOUT   => 10,
21
    TEST_FILE_NAME    => '.koha_test_file',
22
    TEST_FILE_NAME    => '.koha_test_file',
22
    TEST_FILE_CONTENT => "Hello, world!\n",
23
    TEST_FILE_CONTENT => "Hello, world!\n",
23
};
24
};
25
use JSON            qw( decode_json encode_json );
26
use List::MoreUtils qw( any );
24
27
28
use Koha::BackgroundJob::TestTransport;
25
use Koha::Database;
29
use Koha::Database;
26
use Koha::Exceptions::Object;
30
use Koha::Exceptions::Object;
27
use Koha::Encryption;
31
use Koha::Encryption;
Lines 63-73 sub store { Link Here
63
        $self->$dir_field( $dir . '/' ) unless substr( $dir, -1 ) eq '/';
67
        $self->$dir_field( $dir . '/' ) unless substr( $dir, -1 ) eq '/';
64
    }
68
    }
65
69
70
    my @config_fields  = (qw(host port user_name password key_file upload_directory download_directory));
71
    my $changed_config = ( !$self->in_storage || any { $self->_result->is_column_changed($_) } @config_fields ) ? 1 : 0;
72
66
    # Store
73
    # Store
67
    $self->SUPER::store;
74
    $self->SUPER::store;
68
75
76
    # Subclass triggers
77
    my $subclass = $self->transport eq 'sftp' ? 'Koha::File::Transport::SFTP' : 'Koha::File::Transport::FTP';
78
    $self = $subclass->_new_from_dbic( $self->_result );
79
    $self->_post_store_trigger;
80
81
    # Enqueue a connection test
82
    if ($changed_config) {
83
        Koha::BackgroundJob::TestTransport->new->enqueue( { transport_id => $self->id } );
84
    }
85
69
    # Return the updated object including the encrypt_sensitive_data
86
    # Return the updated object including the encrypt_sensitive_data
70
    return $self->discard_changes;
87
    return $self;
71
}
88
}
72
89
73
=head3 plain_text_password
90
=head3 plain_text_password
Lines 111-117 sub to_api { Link Here
111
    my ( $self, $params ) = @_;
128
    my ( $self, $params ) = @_;
112
129
113
    my $json = $self->SUPER::to_api($params) or return;
130
    my $json = $self->SUPER::to_api($params) or return;
114
    delete @{$json}{qw(password key_file)};    # Remove sensitive data
131
    delete @{$json}{qw(password key_file)};                                    # Remove sensitive data
132
    $json->{status} = $self->status ? decode_json( $self->status ) : undef;    # Decode json status
115
133
116
    return $json;
134
    return $json;
117
}
135
}
Lines 141-154 sub test_connection { Link Here
141
    $self->connect or return;
159
    $self->connect or return;
142
160
143
    for my $dir_type (qw(download upload)) {
161
    for my $dir_type (qw(download upload)) {
144
        my $dir = $self->{"${dir_type}_directory"};
162
        my $field = "${dir_type}_directory";
145
        next if $dir eq '';
163
        my $dir   = $self->$field;
164
        $dir ||= undef;
146
165
147
        $self->change_directory($dir) or return;
166
        $self->change_directory(undef) or next;
148
        $self->list_files()           or return;
167
        $self->change_directory($dir)  or next;
168
        $self->list_files()            or next;
149
    }
169
    }
150
170
151
    return 1;
171
    my $return   = 1;
172
    my $messages = $self->object_messages;
173
    for my $message ( @{$messages} ) {
174
        $return = 0 if $message->type eq 'error';
175
    }
176
177
    return $return;
152
}
178
}
153
179
154
=head2 Subclass methods
180
=head2 Subclass methods
Lines 220-225 sub list_files { Link Here
220
    die "Subclass must implement list_files";
246
    die "Subclass must implement list_files";
221
}
247
}
222
248
249
=head3 _post_store_trigger
250
251
    $server->_post_store_trigger;
252
253
Method triggered by parent store to allow local additions to the store call
254
255
=cut
256
257
sub _post_store_trigger {
258
    my ($self) = @_;
259
260
    #Subclass may implement a _post_store_trigger as required
261
    return $self;
262
}
263
223
=head2 Internal methods
264
=head2 Internal methods
224
265
225
=head3 _encrypt_sensitive_data
266
=head3 _encrypt_sensitive_data
Lines 232-245 sub _encrypt_sensitive_data { Link Here
232
    my ($self) = @_;
273
    my ($self) = @_;
233
    my $encryption = Koha::Encryption->new;
274
    my $encryption = Koha::Encryption->new;
234
275
235
    # Only encrypt if the value has changed (is_changed from Koha::Object)
276
    # Only encrypt if the value has changed ($self->_result->is_column_changed from Koha::Object)
236
    if ( ( !$self->in_storage || $self->is_changed('password') ) && $self->password ) {
277
    if ( ( !$self->in_storage || $self->_result->is_column_changed('password') ) && $self->password ) {
237
        $self->password( $encryption->encrypt_hex( $self->password ) );
278
        $self->password( $encryption->encrypt_hex( $self->password ) );
238
    }
279
    }
239
280
240
    if ( ( !$self->in_storage || $self->is_changed('key_file') ) && $self->key_file ) {
281
    if ( ( !$self->in_storage || $self->_result->is_column_changed('key_file') ) && $self->key_file ) {
241
        $self->key_file( $encryption->encrypt_hex( _dos2unix( $self->key_file ) ) );
282
        $self->key_file( $encryption->encrypt_hex( _dos2unix( $self->key_file ) ) );
242
    }
283
    }
284
285
    return;
243
}
286
}
244
287
245
=head3 _dos2unix
288
=head3 _dos2unix
(-)a/Koha/File/Transport/SFTP.pm (-40 / +91 lines)
Lines 32-55 Koha::File::Transport::SFTP - SFTP implementation of file transport Link Here
32
32
33
=head2 Class methods
33
=head2 Class methods
34
34
35
=head3 store
36
37
    $server->store;
38
39
Overloaded store method that ensures key_file also gets written to the filesystem.
40
41
=cut
42
43
sub store {
44
    my ($self) = @_;
45
46
    $self->SUPER::store;
47
    $self->discard_changes;
48
    $self->_write_key_file;
49
50
    return $self;
51
}
52
53
=head3 connect
35
=head3 connect
54
36
55
    my $success = $self->connect;
37
    my $success = $self->connect;
Lines 60-84 Start the SFTP transport connect, returns true on success or undefined on failur Link Here
60
42
61
sub connect {
43
sub connect {
62
    my ($self) = @_;
44
    my ($self) = @_;
45
    my $operation = "connection";
63
46
47
    # String to capture STDERR output
48
    $self->{stderr_capture} = '';
49
    open my $stderr_fh, '>', \$self->{stderr_capture} or die "Can't open scalar as filehandle: $!";
64
    $self->{connection} = Net::SFTP::Foreign->new(
50
    $self->{connection} = Net::SFTP::Foreign->new(
65
        host     => $self->host,
51
        host     => $self->host,
66
        port     => $self->port,
52
        port     => $self->port,
67
        user     => $self->user_name,
53
        user     => $self->user_name,
68
        password => $self->plain_text_password,
54
        password => $self->plain_text_password,
69
        key_path => $self->_locate_key_file,
55
        $self->_locate_key_file ? ( key_path => $self->_locate_key_file ) : (),
70
        timeout  => $self->DEFAULT_TIMEOUT,
56
        timeout   => $self->DEFAULT_TIMEOUT,
71
        more     => [qw(-o StrictHostKeyChecking=no)],
57
        stderr_fh => $stderr_fh,
58
        more      => [qw( -v -o StrictHostKeyChecking=no)],
72
    );
59
    );
73
    $self->{connection}->die_on_error("SFTP failure for remote host");
60
    $self->{stderr_fh} = $stderr_fh;
74
61
75
    return $self->_abort_operation() if ( $self->{connection}->error );
62
    return $self->_abort_operation($operation) if ( $self->{connection}->error );
76
63
77
    $self->add_message(
64
    $self->add_message(
78
        {
65
        {
79
            message => $self->{connection}->status,
66
            message => $operation,
80
            type    => 'success',
67
            type    => 'success',
81
            payload => { detail => '' }
68
            payload => {
69
                status => $self->{connection}->status,
70
                error  => $self->{connection}->error,
71
                path   => $self->{connection}->cwd
72
            }
82
        }
73
        }
83
    );
74
    );
84
75
Lines 97-112 Returns true on success or undefined on failure. Link Here
97
88
98
sub upload_file {
89
sub upload_file {
99
    my ( $self, $local_file, $remote_file ) = @_;
90
    my ( $self, $local_file, $remote_file ) = @_;
91
    my $operation = "upload";
100
92
101
    my $logger = Koha::Logger->get_logger();
93
    my $logger = Koha::Logger->get_logger();
102
94
103
    $self->{connection}->put( $local_file, $remote_file ) or return $self->_abort_operation();
95
    $self->{connection}->put( $local_file, $remote_file ) or return $self->_abort_operation($operation);
104
96
105
    $self->add_message(
97
    $self->add_message(
106
        {
98
        {
107
            message => $self->{connection}->status,
99
            message => $operation,
108
            type    => 'success',
100
            type    => 'success',
109
            payload => { detail => '' }
101
            payload => {
102
                status => $self->{connection}->status,
103
                error  => $self->{connection}->error,
104
                path   => $self->{connection}->cwd
105
            }
110
        }
106
        }
111
    );
107
    );
112
108
Lines 125-138 Returns true on success or undefined on failure. Link Here
125
121
126
sub download_file {
122
sub download_file {
127
    my ( $self, $remote_file, $local_file ) = @_;
123
    my ( $self, $remote_file, $local_file ) = @_;
124
    my $operation = 'download';
128
125
129
    $self->{connection}->get( $remote_file, $local_file ) or return $self->_abort_operation();
126
    $self->{connection}->get( $remote_file, $local_file ) or return $self->_abort_operation($operation);
130
127
131
    $self->add_message(
128
    $self->add_message(
132
        {
129
        {
133
            message => $self->{connection}->status,
130
            message => $operation,
134
            type    => 'success',
131
            type    => 'success',
135
            payload => { detail => '' }
132
            payload => {
133
                status => $self->{connection}->status,
134
                error  => $self->{connection}->error,
135
                path   => $self->{connection}->cwd
136
            }
136
        }
137
        }
137
    );
138
    );
138
139
Lines 151-164 Returns true on success or undefined on failure. Link Here
151
152
152
sub change_directory {
153
sub change_directory {
153
    my ( $self, $remote_directory ) = @_;
154
    my ( $self, $remote_directory ) = @_;
155
    my $operation = 'change_directory';
154
156
155
    $self->{connection}->setcwd($remote_directory) or return $self->_abort_operation();
157
    $self->{connection}->setcwd($remote_directory) or return $self->_abort_operation( $operation, $remote_directory );
156
158
157
    $self->add_message(
159
    $self->add_message(
158
        {
160
        {
159
            message => $self->{connection}->status,
161
            message => $operation,
160
            type    => 'success',
162
            type    => 'success',
161
            payload => { detail => '' }
163
            payload => {
164
                status => $self->{connection}->status,
165
                error  => $self->{connection}->error,
166
                path   => $self->{connection}->cwd
167
            }
162
        }
168
        }
163
    );
169
    );
164
170
Lines 175-188 Returns an array of filenames found in the current directory of the server conne Link Here
175
181
176
sub list_files {
182
sub list_files {
177
    my ($self) = @_;
183
    my ($self) = @_;
184
    my $operation = "list";
178
185
179
    my $file_list = $self->{connection}->ls or return $self->_abort_operation();
186
    my $file_list = $self->{connection}->ls or return $self->_abort_operation($operation);
180
187
181
    $self->add_message(
188
    $self->add_message(
182
        {
189
        {
183
            message => $self->{connection}->status,
190
            message => $operation,
184
            type    => 'success',
191
            type    => 'success',
185
            payload => { detail => '' }
192
            payload => {
193
                status => $self->{connection}->status,
194
                error  => $self->{connection}->error,
195
                path   => $self->{connection}->cwd
196
            }
186
        }
197
        }
187
    );
198
    );
188
199
Lines 191-196 sub list_files { Link Here
191
202
192
=head2 Internal methods
203
=head2 Internal methods
193
204
205
=head3 _post_store_trigger
206
207
    $server->post_store_trigger;
208
209
Local trigger run by the parent store method after storage.
210
Ensures key_file also gets written to the filesystem.
211
212
=cut
213
214
sub _post_store_trigger {
215
    my ($self) = @_;
216
    $self->_write_key_file;
217
    return $self;
218
}
219
194
=head3 _write_key_file
220
=head3 _write_key_file
195
221
196
    my $success = $server->_write_key_file;
222
    my $success = $server->_write_key_file;
Lines 204-209 Returns 1 on success, undef on failure. Link Here
204
sub _write_key_file {
230
sub _write_key_file {
205
    my ($self) = @_;
231
    my ($self) = @_;
206
232
233
    return unless $self->plain_text_key;
234
207
    my $upload_path = C4::Context->config('upload_path') or return;
235
    my $upload_path = C4::Context->config('upload_path') or return;
208
    my $logger      = Koha::Logger->get;
236
    my $logger      = Koha::Logger->get;
209
    my $key_path    = File::Spec->catdir( $upload_path, 'ssh_keys' );
237
    my $key_path    = File::Spec->catdir( $upload_path, 'ssh_keys' );
Lines 254-266 Helper method to abort the current operation and return. Link Here
254
=cut
282
=cut
255
283
256
sub _abort_operation {
284
sub _abort_operation {
257
    my ($self) = @_;
285
    my ( $self, $operation, $path ) = @_;
286
287
    my $stderr = $self->{stderr_capture};
288
    $self->{stderr_capture} = '';
258
289
259
    $self->add_message(
290
    $self->add_message(
260
        {
291
        {
261
            message => $self->{connection}->error,
292
            message => $operation,
262
            type    => 'error',
293
            type    => 'error',
263
            payload => { detail => $self->{connection} ? $self->{connection}->status : '' }
294
            payload => {
295
                status    => $self->{connection}->status,
296
                error     => $self->{connection}->error,
297
                path      => $path ? $path : $self->{connection}->cwd,
298
                error_raw => $stderr
299
            }
264
        }
300
        }
265
    );
301
    );
266
302
Lines 271-274 sub _abort_operation { Link Here
271
    return;
307
    return;
272
}
308
}
273
309
310
=head3 DESTROY
311
312
Ensure proper cleanup of open filehandles
313
314
=cut
315
316
sub DESTROY {
317
    my ($self) = @_;
318
319
    # Ensure the filehandle is closed properly
320
    if ( $self->{stderr_fh} ) {
321
        close $self->{stderr_fh} or warn "Failed to close STDERR filehandle: $!";
322
    }
323
}
324
274
1;
325
1;
(-)a/Koha/SFTP/Server.pm (-6 / +6 lines)
Lines 409-420 sub test_conn { Link Here
409
        }
409
        }
410
    }
410
    }
411
411
412
    $self->update_status('tests_ok');
412
    #    $self->update_status('tests_ok');
413
    foreach my $val ( values %$sftp_test_results ) {
413
    #    foreach my $val ( values %$sftp_test_results ) {
414
        if ( defined $val->{'err'} ) {
414
    #        if ( defined $val->{'err'} ) {
415
            $self->update_status('tests_failed');
415
    #            $self->update_status('tests_failed');
416
        }
416
    #        }
417
    }
417
    #    }
418
418
419
    return ( 1, $sftp_test_results );
419
    return ( 1, $sftp_test_results );
420
}
420
}
(-)a/admin/sftp_servers.pl (-19 lines)
Lines 192-216 if ( $op eq 'cud-add' ) { Link Here
192
            reason => 'invalid_id'
192
            reason => 'invalid_id'
193
        };
193
        };
194
    }
194
    }
195
196
} elsif ( $op eq 'test_form' ) {
197
    my $sftp_server_id = $input->param('sftp_server_id');
198
    my $sftp_server;
199
200
    $sftp_server = Koha::File::Transports->find($sftp_server_id)
201
        unless !$sftp_server_id;
202
203
    if ($sftp_server) {
204
        $template->param(
205
            sftp_server => $sftp_server,
206
        );
207
    } else {
208
        push @messages, {
209
            type   => 'alert',
210
            code   => 'error_on_test',
211
            reason => 'invalid_id',
212
        };
213
    }
214
}
195
}
215
196
216
if ( $op eq 'list' ) {
197
if ( $op eq 'list' ) {
(-)a/api/v1/swagger/definitions/sftp_server.yaml (-1 / +1 lines)
Lines 58-64 properties: Link Here
58
    description: Path on the remote server we upload to (optional)
58
    description: Path on the remote server we upload to (optional)
59
  status:
59
  status:
60
    type:
60
    type:
61
      - string
61
      - object
62
      - "null"
62
      - "null"
63
    description: Most recent status of the FTP/SFTP server (optional)
63
    description: Most recent status of the FTP/SFTP server (optional)
64
  debug:
64
  debug:
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/background_jobs/file_transport_test.inc (+48 lines)
Line 0 Link Here
1
[% USE Koha %]
2
3
[%- BLOCK operation_description -%]
4
    [%- SWITCH operation.code -%]
5
6
    [%- CASE 'connection' -%]
7
        <span>Connection</span>
8
    [%- CASE 'upload' -%]
9
        <span>Upload</span>
10
    [%- CASE 'download' -%]
11
        <span>Download</span>
12
    [%- CASE 'list' -%]
13
        <span>List files</span>
14
    [%- CASE 'change_directory' -%]
15
        <span>Change directory</span>
16
    [%- CASE -%]
17
        <span>[% operation.code | html %]</span>
18
    [%- END -%]
19
[%- END -%]
20
21
[% SET report = job.decoded_data %]
22
[% BLOCK report %]
23
    [% IF job.status == 'finished' %]
24
        [% IF report.status == 'errors' %]
25
            <div class="alert alert-alert"> Connection test completed with errors </div>
26
        [% ELSE %]
27
            <div class="alert alert-success"> Connection test completed without errors </div>
28
        [% END %]
29
    [% ELSIF job.status == 'cancelled' %]
30
        <span>The connection test was cancelled before it finished.</span>
31
    [% END %]
32
[% END %]
33
34
[% BLOCK detail %]
35
    [% FOREACH operation IN report.operations %]
36
        [% IF operation.status == 'error' %]
37
            <div class="text-danger"> <i class="fa-solid fa-circle-xmark"></i> [% PROCESS operation_description %] failed. </div>
38
        [% ELSE %]
39
            <div class="text-success"> <i class="fa-solid fa-circle-check"></i> [% PROCESS operation_description %] passed. </div>
40
        [% END %]
41
    [% END %]
42
    [% IF job.status == 'cancelled' %]
43
        <span>The connection test was cancelled before it finished.</span>
44
    [% END %]
45
[% END %]
46
47
[% BLOCK js %]
48
[% END %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/background_jobs.tt (+4 lines)
Lines 227-232 Link Here
227
                '_id': 'import_from_kbart_file',
227
                '_id': 'import_from_kbart_file',
228
                '_str': _("Import titles from a KBART file")
228
                '_str': _("Import titles from a KBART file")
229
            },
229
            },
230
            {
231
                '_id': 'file_transport_test',
232
                '_str': _("File transport connection test")
233
            }
230
        ];
234
        ];
231
235
232
        function get_job_type (job_type) {
236
        function get_job_type (job_type) {
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/sftp_servers.tt (-867 / +693 lines)
Lines 3-39 Link Here
3
[% PROCESS 'i18n.inc' %]
3
[% PROCESS 'i18n.inc' %]
4
[% SET footerjs = 1 %]
4
[% SET footerjs = 1 %]
5
[% INCLUDE 'doc-head-open.inc' %]
5
[% INCLUDE 'doc-head-open.inc' %]
6
<title>[% FILTER collapse %]
6
<title
7
    [% IF op == 'add_form' %]
7
    >[% FILTER collapse %]
8
        [% t("New FTP/SFTP server") | html %] &rsaquo;
8
        [% IF op == 'add_form' %]
9
    [% ELSIF op == 'edit_form' %]
9
            [% t("New FTP/SFTP server") | html %]
10
        [% tx("Modify FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %] &rsaquo;
10
            &rsaquo;
11
    [% ELSIF op == 'test_form' %]
11
        [% ELSIF op == 'edit_form' %]
12
        [% tx("Test FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %] &rsaquo;
12
            [% tx("Modify FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %]
13
    [% END %]
13
            &rsaquo;
14
    [% t("FTP/SFTP Servers") | html %] &rsaquo;
14
        [% END %]
15
    [% t("Administration") | html %] &rsaquo;
15
        [% t("FTP/SFTP Servers") | html %]
16
    [% t("Koha") | html %]
16
        &rsaquo; [% t("Administration") | html %] &rsaquo; [% t("Koha") | html %]
17
[% END %]</title>
17
    [% END %]</title
18
>
18
[% INCLUDE 'doc-head-close.inc' %]
19
[% INCLUDE 'doc-head-close.inc' %]
19
<style>
20
    #testOutput {
21
        font-size: 1.25rem;
22
    }
23
24
    #testOutput .pending-loading {
25
        font-size: 1.5rem;
26
        margin-left: 1.25rem;
27
    }
28
29
    #testOutput code {
30
        font-size: 87.5%;
31
        color: #e83e8c;
32
        background: transparent;
33
        word-break: break-word;
34
    }
35
</style>
36
37
</head>
20
</head>
38
21
39
<body id="admin_sftp_servers" class="admin">
22
<body id="admin_sftp_servers" class="admin">
Lines 47-53 Link Here
47
            <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a>
30
            <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a>
48
        [% END %]
31
        [% END %]
49
32
50
        [% IF op == 'add_form' || op == 'edit_form' || op == 'test_form' %]
33
        [% IF op == 'add_form' || op == 'edit_form' %]
51
            [% WRAPPER breadcrumb_item %]
34
            [% WRAPPER breadcrumb_item %]
52
                <a href="/cgi-bin/koha/admin/sftp_servers.pl">FTP/SFTP servers</a>
35
                <a href="/cgi-bin/koha/admin/sftp_servers.pl">FTP/SFTP servers</a>
53
            [% END %]
36
            [% END %]
Lines 57-73 Link Here
57
            [% WRAPPER breadcrumb_item bc_active= 1 %]
40
            [% WRAPPER breadcrumb_item bc_active= 1 %]
58
                <span>New FTP/SFTP server</span>
41
                <span>New FTP/SFTP server</span>
59
            [% END %]
42
            [% END %]
60
61
        [% ELSIF op == 'edit_form' %]
43
        [% ELSIF op == 'edit_form' %]
62
            [% WRAPPER breadcrumb_item bc_active= 1 %]
44
            [% WRAPPER breadcrumb_item bc_active= 1 %]
63
                [% tx("Modify FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %]
45
                [% tx("Modify FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %]
64
            [% END %]
46
            [% END %]
65
66
        [% ELSIF op == 'test_form' %]
67
            [% WRAPPER breadcrumb_item bc_active= 1 %]
68
                [% tx("Test FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %]
69
            [% END %]
70
71
        [% ELSE %]
47
        [% ELSE %]
72
            [% WRAPPER breadcrumb_item bc_active= 1 %]
48
            [% WRAPPER breadcrumb_item bc_active= 1 %]
73
                <span>FTP/SFTP servers</span>
49
                <span>FTP/SFTP servers</span>
Lines 82-963 Link Here
82
            <main>
58
            <main>
83
                [% INCLUDE 'messages.inc' %]
59
                [% INCLUDE 'messages.inc' %]
84
60
85
[% FOREACH m IN messages %]
61
                [% FOREACH m IN messages %]
86
    <div class="alert alert-[% m.type | html %]" id="sftp_action_result_dialog">
62
                    <div class="alert alert-[% m.type | html %]" id="sftp_action_result_dialog">
87
        [% SWITCH m.code %]
63
                        [% SWITCH m.code %]
88
        [% CASE 'error_on_insert' %]
64
                        [% CASE 'error_on_insert' %]
89
            <span>An error occurred when adding the server. The passed ID already exists.</span>
65
                            <span>An error occurred when adding the server. The passed ID already exists.</span>
90
        [% CASE 'error_on_update' %]
66
                        [% CASE 'error_on_update' %]
91
            <span>An error occurred trying to open the server for editing. The passed ID is invalid.</span>
67
                            <span>An error occurred trying to open the server for editing. The passed ID is invalid.</span>
92
        [% CASE 'error_on_edit' %]
68
                        [% CASE 'error_on_edit' %]
93
            <span>An error occurred trying to open the server for editing. The passed ID is invalid.</span>
69
                            <span>An error occurred trying to open the server for editing. The passed ID is invalid.</span>
94
        [% CASE 'error_on_test' %]
70
                        [% CASE 'success_on_update' %]
95
            <span>An error occurred when connecting to this server. Please see the text below for more information.</span>
71
                            <span>Server updated successfully.</span>
96
        [% CASE 'success_on_update' %]
72
                        [% CASE 'success_on_insert' %]
97
            <span>Server updated successfully.</span>
73
                            <span>Server added successfully.</span>
98
        [% CASE 'success_on_insert' %]
74
                        [% CASE %]
99
            <span>Server added successfully.</span>
75
                            <span>[% m.code | html %]</span>
100
        [% CASE %]
76
                        [% END %]
101
            <span>[% m.code | html %]</span>
102
        [% END %]
103
    </div>
104
[% END %]
105
106
    <div class="alert alert-info"    id="sftp_delete_success" style="display: none;"></div>
107
    <div class="alert alert-warning" id="sftp_delete_error"   style="display: none;"></div>
108
109
[% IF op == 'add_form' %]
110
    <!-- Modal -->
111
    <div id="confirm_key_accept" class="modal" tabindex="-1" role="dialog" aria-labelledby="confirm_key_accept_submit" aria-hidden="true">
112
        <div class="modal-dialog modal-lg">
113
            <div class="modal-content modal-lg">
114
                    <div class="modal-header">
115
                        <h1 class="modal-title">Are you sure?</h1>
116
                        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
117
                    </div>
118
                    <div class="modal-body">
119
                        <div id="modal_message" class="alert alert-warning">Because you are using the SFTP transport, please run a test of this connection to accept the server's host key.</div>
120
                        <p>Before saving, please check the following details again to make sure you are certain they are correct. Sending or receiving data from an unknown source potentially puts your system at risk.</p>
121
                        <table class="mx-4 mb-3">
122
                            <thead></thead>
123
                            <tbody>
124
                                <tr>
125
                                    <td><strong>Host</strong></td>
126
                                    <td id="modal_host"></td>
127
                                </tr>
128
                                <tr>
129
                                    <td><strong>Port</strong></td>
130
                                    <td id="modal_port"></td>
131
                                </tr>
132
                                <tr>
133
                                    <td><strong>Transport</strong></td>
134
                                    <td id="modal_transport"></td>
135
                                </tr>
136
                                <tr>
137
                                    <td><strong>Username</strong></td>
138
                                    <td id="modal_user_name"></td>
139
                                </tr>
140
                                <tr>
141
                                    <td><strong>Authentication mode</strong></td>
142
                                    <td id="modal_auth_mode"></td>
143
                                </tr>
144
                            </tbody>
145
                        </table>
146
                        <p>If you are ready to progress with these details, please click Save.</p>
147
                    </div>
148
                    <div class="modal-footer">
149
                        <button class="btn btn-default approve" type="submit"><i class="fa fa-check"></i> Save</button>
150
                        <button class="btn btn-default deny cancel" type="button" data-bs-dismiss="modal"><i class="fa fa-times"></i> Cancel</button>
151
                    </div>
77
                    </div>
152
                </form>
78
                [% END %]
153
            </div>
79
154
        </div>
80
                <div class="alert alert-info" id="sftp_delete_success" style="display: none;"></div>
155
    </div>
81
                <div class="alert alert-warning" id="sftp_delete_error" style="display: none;"></div>
156
    <!-- END Modal -->
82
157
83
                [% IF op == 'add_form' %]
158
    <h1>New FTP/SFTP server</h1>
84
                    <!-- Modal -->
159
85
                    <div id="confirm_key_accept" class="modal" tabindex="-1" role="dialog" aria-labelledby="confirm_key_accept_submit" aria-hidden="true">
160
    <form action="/cgi-bin/koha/admin/sftp_servers.pl" id="add" name="add" class="validated" method="post">
86
                        <div class="modal-dialog modal-lg">
161
        [% INCLUDE 'csrf-token.inc' %]
87
                            <div class="modal-content modal-lg">
162
        <input type="hidden" name="op" value="cud-add" />
88
                                <div class="modal-header">
163
        <fieldset class="rows">
89
                                    <h1 class="modal-title">Are you sure?</h1>
164
            <ol>
90
                                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
165
                <li>
91
                                </div>
166
                    <label for="sftp_name" class="required">Name: </label>
92
                                <div class="modal-body">
167
                    <input type="text" name="sftp_name" id="sftp_name" size="60" class="required focus" required="required" />
93
                                    <div id="modal_message" class="alert alert-warning">Saving these changes will trigger a connection test and this will automatically accept the remote servers host key</div>
168
                    <span class="required">Required</span>
94
                                    <p>Before saving, please check the following details again to make sure you are certain they are correct. Sending or receiving data from an unknown source potentially puts your system at risk.</p>
169
                </li>
95
                                    <table class="mx-4 mb-3">
170
            </ol>
96
                                        <thead></thead>
171
        </fieldset>
97
                                        <tbody>
172
98
                                            <tr>
173
        <fieldset class="rows">
99
                                                <td><strong>Transport</strong></td>
174
            <ol>
100
                                                <td id="modal_transport"></td>
175
                <li>
101
                                            </tr>
176
                    <label for="sftp_host" class="required">Host: </label>
102
                                            <tr>
177
                    <input type="text" value="localhost" name="sftp_host" id="sftp_host" size="60" class="required" />
103
                                                <td><strong>Host</strong></td>
178
                    <span class="required">Required</span>
104
                                                <td id="modal_host"></td>
179
                </li>
105
                                            </tr>
180
                <li>
106
                                            <tr>
181
                    <label for="sftp_port" class="required">Port: </label>
107
                                                <td><strong>Port</strong></td>
182
                    <input type="text" inputmode="numeric" pattern="[0-9]*" value="22" name="sftp_port" id="sftp_port" size="20" class="required" />
108
                                                <td id="modal_port"></td>
183
                    <span class="required">Required</span>
109
                                            </tr>
184
                </li>
110
                                            <tr>
185
                <li>
111
                                                <td><strong>Username</strong></td>
186
                    <label for="sftp_transport" class="required">Transport: </label>
112
                                                <td id="modal_user_name"></td>
187
                    <select name="sftp_transport" id="sftp_transport" class="required">
113
                                            </tr>
188
                        <option value="ftp">FTP</option>
114
                                            <tr>
189
                        <option value="sftp" selected="selected">SFTP</option>
115
                                                <td><strong>Authentication mode</strong></td>
190
                    </select>
116
                                                <td id="modal_auth_mode"></td>
191
                    <span class="required">Required</span>
117
                                            </tr>
192
                </li>
118
                                        </tbody>
193
                <li>
119
                                    </table>
194
                    <label for="sftp_passive">Passive mode: </label>
120
                                    <p>If you are ready to progress with these details, please click Save.</p>
195
                    <select name="sftp_passive" id="sftp_passive" disabled="disabled">
121
                                </div>
196
                        <option value="1" selected="selected">On (Recommended)</option>
122
                                <div class="modal-footer">
197
                        <option value="0">Off</option>
123
                                    <button class="btn btn-default approve" type="submit"><i class="fa fa-check"></i> Save</button>
198
                    </select>
124
                                    <button class="btn btn-default deny cancel" type="button" data-bs-dismiss="modal"><i class="fa fa-times"></i> Cancel</button>
199
                    <span class="hint">Only applies to FTP connections</span>
125
                                </div>
200
                </li>
126
                            </div>
201
                <li>
127
                        </div>
202
                    <label for="sftp_auth_mode">Authentication mode: </label>
203
                    <select name="sftp_auth_mode" id="sftp_auth_mode">
204
                        <option value="password" selected="selected">Password-based</option>
205
                        <option value="key_file">Key file-based</option>
206
                        <option value="noauth">No authentication</option>
207
                    </select>
208
                </li>
209
                <li>
210
                    <label for="sftp_user_name" class="required">Username: </label>
211
                    <input type="text" name="sftp_user_name" id="sftp_user_name" size="60" autocomplete="off" class="required" />
212
                    <span class="required">Required</span>
213
                </li>
214
                <li>
215
                    <label for="sftp_password">Password: </label>
216
                    <input type="password" name="sftp_password" id="sftp_password" size="60" autocomplete="off" />
217
                </li>
218
                <li>
219
                    <label for="sftp_key_file">Key file: </label>
220
                    <textarea name="sftp_key_file" id="sftp_key_file" rows="10" cols="58"></textarea>
221
                    <span class="hint">Only applies to SFTP connections</span>
222
                </li>
223
                <li>
224
                    <label for="sftp_download_directory" >Remote download directory: </label>
225
                    <input type="text" name="sftp_download_directory" id="sftp_download_directory" size="60" autocomplete="off" /><br />
226
                    <span class="hint">The path on the remote server where we will download from</span>
227
                </li>
228
                <li>
229
                    <label for="sftp_upload_directory" >Remote upload directory: </label>
230
                    <input type="text" name="sftp_upload_directory" id="sftp_upload_directory" size="60" autocomplete="off" /><br />
231
                    <span class="hint">The path on the remote server where we will upload to</span>
232
                </li>
233
                <input type="hidden" value="" name="sftp_status" id="sftp_status">
234
                <li>
235
                    <label for="sftp_debug_mode">Debug mode: </label>
236
                    <select name="sftp_debug_mode" id="sftp_debug_mode">
237
                        <option value="1">Enabled</option>
238
                        <option value="0" selected="selected">Disabled</option>
239
                    </select>
240
                    <span class="hint">Enables additional debug output in the logs</span>
241
                </li>
242
            </ol>
243
        </fieldset>
244
        <fieldset class="action">
245
            <a id="confirm_key_accept_submit" data-bs-target="#confirm_key_accept" class="btn btn-primary" data-bs-toggle="modal">Submit</a>
246
            <a class="cancel" href="/cgi-bin/koha/admin/sftp_servers.pl">Cancel</a>
247
        </fieldset>
248
    </form>
249
[% END %]
250
251
[% IF op == 'edit_form' && !messages %]
252
    <!-- Modal -->
253
    <div id="confirm_key_accept" class="modal" tabindex="-1" role="dialog" aria-labelledby="confirm_key_accept_submit" aria-hidden="true">
254
        <div class="modal-dialog modal-lg">
255
            <div class="modal-content modal-lg">
256
                    <div class="modal-header">
257
                        <h1 class="modal-title">Are you sure?</h1>
258
                        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
259
                    </div>
128
                    </div>
260
                    <div class="modal-body">
129
                    <!-- END Modal -->
261
                        <div id="modal_message" class="alert alert-warning">Because you are using the SFTP transport, please run a test of this connection to accept the server's host key.</div>
130
262
                        <p>Before saving, please check the following details again to make sure you are certain they are correct. Sending or receiving data from an unknown source potentially puts your system at risk.</p>
131
                    <h1>New FTP/SFTP server</h1>
263
                        <table class="mx-4 mb-3">
132
264
                            <thead></thead>
133
                    <form action="/cgi-bin/koha/admin/sftp_servers.pl" id="add" name="add" class="validated" method="post">
265
                            <tbody>
134
                        [% INCLUDE 'csrf-token.inc' %]
266
                                <tr>
135
                        <input type="hidden" name="op" value="cud-add" />
267
                                    <td><strong>Host</strong></td>
136
                        <fieldset class="rows">
268
                                    <td id="modal_host"></td>
137
                            <ol>
269
                                </tr>
138
                                <li>
270
                                <tr>
139
                                    <label for="sftp_name" class="required">Name: </label>
271
                                    <td><strong>Port</strong></td>
140
                                    <input type="text" name="sftp_name" id="sftp_name" size="60" class="required focus" required="required" />
272
                                    <td id="modal_port"></td>
141
                                    <span class="required">Required</span>
273
                                </tr>
142
                                </li>
274
                                <tr>
143
                            </ol>
275
                                    <td><strong>Transport</strong></td>
144
                        </fieldset>
276
                                    <td id="modal_transport"></td>
145
277
                                </tr>
146
                        <fieldset class="rows">
278
                                <tr>
147
                            <ol>
279
                                    <td><strong>Username</strong></td>
148
                                <li>
280
                                    <td id="modal_user_name"></td>
149
                                    <label for="sftp_transport" class="required">Transport: </label>
281
                                </tr>
150
                                    <select name="sftp_transport" id="sftp_transport" class="required">
282
                                <tr>
151
                                        <option value="ftp">FTP</option>
283
                                    <td><strong>Authentication mode</strong></td>
152
                                        <option value="sftp" selected="selected">SFTP</option>
284
                                    <td id="modal_auth_mode"></td>
153
                                    </select>
285
                                </tr>
154
                                    <span class="required">Required</span>
286
                            </tbody>
155
                                </li>
287
                        </table>
156
                                <li>
288
                        <p>If you are ready to progress with these details, please click Save.</p>
157
                                    <label for="sftp_host" class="required">Host: </label>
158
                                    <input type="text" value="localhost" name="sftp_host" id="sftp_host" size="60" class="required" />
159
                                    <span class="required">Required</span>
160
                                </li>
161
                                <li>
162
                                    <label for="sftp_port" class="required">Port: </label>
163
                                    <input type="text" inputmode="numeric" pattern="[0-9]*" value="22" name="sftp_port" id="sftp_port" size="20" class="required" />
164
                                    <span class="required">Required</span>
165
                                </li>
166
                                <li>
167
                                    <label for="sftp_passive">Passive mode: </label>
168
                                    <select name="sftp_passive" id="sftp_passive" disabled="disabled">
169
                                        <option value="1" selected="selected">On (Recommended)</option>
170
                                        <option value="0">Off</option>
171
                                    </select>
172
                                    <span class="hint">Only applies to FTP connections</span>
173
                                </li>
174
                                <li>
175
                                    <label for="sftp_auth_mode">Authentication mode: </label>
176
                                    <select name="sftp_auth_mode" id="sftp_auth_mode">
177
                                        <option value="password" selected="selected">Password-based</option>
178
                                        <option value="key_file">Key file-based</option>
179
                                        <option value="noauth">No authentication</option>
180
                                    </select>
181
                                </li>
182
                                <li>
183
                                    <label for="sftp_user_name" class="required">Username: </label>
184
                                    <input type="text" name="sftp_user_name" id="sftp_user_name" size="60" autocomplete="off" class="required" />
185
                                    <span class="required">Required</span>
186
                                </li>
187
                                <li>
188
                                    <label for="sftp_password">Password: </label>
189
                                    <input type="password" name="sftp_password" id="sftp_password" size="60" autocomplete="off" />
190
                                </li>
191
                                <li>
192
                                    <label for="sftp_key_file">Key file: </label>
193
                                    <textarea name="sftp_key_file" id="sftp_key_file" rows="10" cols="58"></textarea>
194
                                    <span class="hint">Only applies to SFTP connections</span>
195
                                </li>
196
                                <li>
197
                                    <label for="sftp_download_directory">Remote download directory: </label>
198
                                    <input type="text" name="sftp_download_directory" id="sftp_download_directory" size="60" autocomplete="off" /><br />
199
                                    <span class="hint">The path on the remote server where we will download from</span>
200
                                </li>
201
                                <li>
202
                                    <label for="sftp_upload_directory">Remote upload directory: </label>
203
                                    <input type="text" name="sftp_upload_directory" id="sftp_upload_directory" size="60" autocomplete="off" /><br />
204
                                    <span class="hint">The path on the remote server where we will upload to</span>
205
                                </li>
206
                                <input type="hidden" value="" name="sftp_status" id="sftp_status" />
207
                                <li>
208
                                    <label for="sftp_debug_mode">Debug mode: </label>
209
                                    <select name="sftp_debug_mode" id="sftp_debug_mode">
210
                                        <option value="1">Enabled</option>
211
                                        <option value="0" selected="selected">Disabled</option>
212
                                    </select>
213
                                    <span class="hint">Enables additional debug output in the logs</span>
214
                                </li>
215
                            </ol>
216
                        </fieldset>
217
                        <fieldset class="action">
218
                            <a id="confirm_key_accept_submit" data-bs-target="#confirm_key_accept" class="btn btn-primary" data-bs-toggle="modal">Submit</a>
219
                            <a class="cancel" href="/cgi-bin/koha/admin/sftp_servers.pl">Cancel</a>
220
                        </fieldset>
221
                    </form>
222
                [% END %]
223
224
                [% IF op == 'edit_form' && !messages %]
225
                    <!-- Modal -->
226
                    <div id="confirm_key_accept" class="modal" tabindex="-1" role="dialog" aria-labelledby="confirm_key_accept_submit" aria-hidden="true">
227
                        <div class="modal-dialog modal-lg">
228
                            <div class="modal-content modal-lg">
229
                                <div class="modal-header">
230
                                    <h1 class="modal-title">Are you sure?</h1>
231
                                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
232
                                </div>
233
                                <div class="modal-body">
234
                                    <div id="modal_message" class="alert alert-warning">Saving these changes will trigger a connection test and this will automatically accept the remote servers host key</div>
235
                                    <p>Before saving, please check the following details again to make sure you are certain they are correct. Sending or receiving data from an unknown source potentially puts your system at risk.</p>
236
                                    <table class="mx-4 mb-3">
237
                                        <thead></thead>
238
                                        <tbody>
239
                                            <tr>
240
                                                <td><strong>Transport</strong></td>
241
                                                <td id="modal_transport"></td>
242
                                            </tr>
243
                                            <tr>
244
                                                <td><strong>Host</strong></td>
245
                                                <td id="modal_host"></td>
246
                                            </tr>
247
                                            <tr>
248
                                                <td><strong>Port</strong></td>
249
                                                <td id="modal_port"></td>
250
                                            </tr>
251
                                            <tr>
252
                                                <td><strong>Username</strong></td>
253
                                                <td id="modal_user_name"></td>
254
                                            </tr>
255
                                            <tr>
256
                                                <td><strong>Authentication mode</strong></td>
257
                                                <td id="modal_auth_mode"></td>
258
                                            </tr>
259
                                        </tbody>
260
                                    </table>
261
                                    <p>If you are ready to progress with these details, please click Save.</p>
262
                                </div>
263
                                <div class="modal-footer">
264
                                    <button class="btn btn-default approve" type="submit"><i class="fa fa-check"></i> Save</button>
265
                                    <button class="btn btn-default deny cancel" type="button" data-bs-dismiss="modal"><i class="fa fa-times"></i> Cancel</button>
266
                                </div>
267
                            </div>
268
                        </div>
289
                    </div>
269
                    </div>
290
                    <div class="modal-footer">
270
                    <!-- END Modal -->
291
                        <button class="btn btn-default approve" type="submit"><i class="fa fa-check"></i> Save</button>
271
292
                        <button class="btn btn-default deny cancel" type="button" data-bs-dismiss="modal"><i class="fa fa-times"></i> Cancel</button>
272
                    <h1>[% tx("Modify FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %]</h1>
273
274
                    <form action="/cgi-bin/koha/admin/sftp_servers.pl" id="edit_save" name="edit_save" class="validated" method="post">
275
                        [% INCLUDE 'csrf-token.inc' %]
276
                        <input type="hidden" name="op" value="cud-edit_save" />
277
                        <input type="hidden" name="sftp_server_id" value="[%- sftp_server.id | html -%]" />
278
                        <fieldset class="rows">
279
                            <ol>
280
                                <li>
281
                                    <label for="sftp_name" class="required">Name: </label>
282
                                    <input type="text" value="[% sftp_server.name | html %]" name="sftp_name" id="sftp_name" size="60" class="required focus" required="required" />
283
                                    <span class="required">Required</span>
284
                                </li>
285
                            </ol>
286
                        </fieldset>
287
288
                        <fieldset class="rows">
289
                            <ol>
290
                                <li>
291
                                    <label for="sftp_transport" class="required">Transport: </label>
292
                                    <select name="sftp_transport" id="sftp_transport" class="required">
293
                                        [% IF sftp_server.transport == 'ftp' %]
294
                                            <option value="ftp" selected="selected">FTP</option>
295
                                        [% ELSE %]
296
                                            <option value="ftp">FTP</option>
297
                                        [% END %]
298
                                        [% IF sftp_server.transport == 'sftp' %]
299
                                            <option value="sftp" selected="selected">SFTP</option>
300
                                        [% ELSE %]
301
                                            <option value="sftp">SFTP</option>
302
                                        [% END %]
303
                                    </select>
304
                                    <span class="required">Required</span>
305
                                </li>
306
                                <li>
307
                                    <label for="sftp_host" class="required">Host: </label>
308
                                    <input type="text" value="[% sftp_server.host | html %]" name="sftp_host" id="sftp_host" size="60" class="required" />
309
                                    <span class="required">Required</span>
310
                                </li>
311
                                <li>
312
                                    <label for="sftp_port" class="required">Port: </label>
313
                                    <input type="text" inputmode="numeric" pattern="[0-9]*" value="[% sftp_server.port | html %]" name="sftp_port" id="sftp_port" size="20" class="required" />
314
                                    <span class="required">Required</span>
315
                                </li>
316
                                <li>
317
                                    <label for="sftp_passive">Passive mode: </label>
318
                                    <select name="sftp_passive" id="sftp_passive" disabled="disabled">
319
                                        [% IF sftp_server.passive == 1 %]
320
                                            <option value="1" selected="selected">Enabled (Recommended)</option>
321
                                        [% ELSE %]
322
                                            <option value="1">Enabled (Recommended)</option>
323
                                        [% END %]
324
                                        [% IF sftp_server.passive == 0 %]
325
                                            <option value="0" selected="selected">Disabled</option>
326
                                        [% ELSE %]
327
                                            <option value="0">Disabled</option>
328
                                        [% END %]
329
                                    </select>
330
                                    <span class="hint">Only applies to FTP connections</span>
331
                                </li>
332
                                <li>
333
                                    <label for="sftp_auth_mode">Authentication mode: </label>
334
                                    <select name="sftp_auth_mode" id="sftp_auth_mode">
335
                                        [% IF sftp_server.auth_mode == 'password' %]
336
                                            <option value="password" selected="selected">Password-based</option>
337
                                        [% ELSE %]
338
                                            <option value="password">Password-based</option>
339
                                        [% END %]
340
                                        [% IF sftp_server.auth_mode == 'key_file' %]
341
                                            <option value="key_file" selected="selected">Key file-based</option>
342
                                        [% ELSE %]
343
                                            <option value="key_file">Key file-based</option>
344
                                        [% END %]
345
                                        [% IF sftp_server.auth_mode == 'noauth' %]
346
                                            <option value="noauth" selected="selected">No authentication</option>
347
                                        [% ELSE %]
348
                                            <option value="noauth">No authentication</option>
349
                                        [% END %]
350
                                    </select>
351
                                </li>
352
                                <li>
353
                                    <label for="sftp_user_name" class="required">Username: </label>
354
                                    <input type="text" value="[% sftp_server.user_name | html %]" name="sftp_user_name" id="sftp_user_name" size="60" autocomplete="off" class="required" />
355
                                    <span class="required">Required</span>
356
                                </li>
357
                                <li>
358
                                    <label for="sftp_password">Password: </label>
359
                                    <input type="password" value="[% sftp_server_plain_text_password | html %]" name="sftp_password" id="sftp_password" size="60" autocomplete="off" />
360
                                </li>
361
                                <li>
362
                                    <label for="sftp_key_file">Key file path: </label>
363
                                    <textarea name="sftp_key_file" id="sftp_key_file" rows="10" cols="58">[% sftp_server_plain_text_key | html %]</textarea>
364
                                    <span class="hint">Only applies to SFTP connections</span>
365
                                </li>
366
                                <li>
367
                                    <label for="sftp_download_directory">Remote download directory: </label>
368
                                    <input type="text" value="[% sftp_server.download_directory | html %]" name="sftp_download_directory" id="sftp_download_directory" size="60" autocomplete="off" /><br />
369
                                    <span class="hint">The path on the remote server where we will download from</span>
370
                                </li>
371
                                <li>
372
                                    <label for="sftp_upload_directory">Remote upload directory: </label>
373
                                    <input type="text" value="[% sftp_server.upload_directory | html %]" name="sftp_upload_directory" id="sftp_upload_directory" size="60" autocomplete="off" /><br />
374
                                    <span class="hint">The path on the remote server where we will upload to</span>
375
                                </li>
376
                                <input type="hidden" value="" name="sftp_status" id="sftp_status" />
377
                                <li>
378
                                    <label for="sftp_debug_mode">Debug mode: </label>
379
                                    <select name="sftp_debug_mode" id="sftp_debug_mode">
380
                                        [% IF sftp_server.debug == 1 %]
381
                                            <option value="1" selected="selected">Enabled</option>
382
                                        [% ELSE %]
383
                                            <option value="1">Enabled</option>
384
                                        [% END %]
385
                                        [% IF sftp_server.debug == 0 %]
386
                                            <option value="0" selected="selected">Disabled</option>
387
                                        [% ELSE %]
388
                                            <option value="0">Disabled</option>
389
                                        [% END %]
390
                                    </select>
391
                                    <span class="hint">Enables additional debug output in the logs</span>
392
                                </li>
393
                            </ol>
394
                        </fieldset>
395
                        <fieldset class="action">
396
                            <a id="confirm_key_accept_submit" data-bs-target="#confirm_key_accept" class="btn btn-primary" data-bs-toggle="modal">Submit</a>
397
                            <a class="cancel" href="/cgi-bin/koha/admin/sftp_servers.pl">Cancel</a>
398
                        </fieldset>
399
                    </form>
400
                [% END %]
401
402
                [% IF op == 'list' %]
403
                    <div id="toolbar" class="btn-toolbar">
404
                        <a class="btn btn-default" id="new_sftp_server" href="/cgi-bin/koha/admin/sftp_servers.pl?op=add_form"><i class="fa fa-plus"></i> New FTP/SFTP server</a>
293
                    </div>
405
                    </div>
294
                </form>
295
            </div>
296
        </div>
297
    </div>
298
    <!-- END Modal -->
299
300
    <h1>[% tx("Modify FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %]</h1>
301
302
    <form action="/cgi-bin/koha/admin/sftp_servers.pl" id="edit_save" name="edit_save" class="validated" method="post">
303
        [% INCLUDE 'csrf-token.inc' %]
304
        <input type="hidden" name="op" value="cud-edit_save" />
305
        <input type="hidden" name="sftp_server_id" value="[%- sftp_server.id | html -%]" />
306
        <fieldset class="rows">
307
            <ol>
308
                <li>
309
                    <label for="sftp_name" class="required">Name: </label>
310
                    <input type="text" value="[% sftp_server.name | html %]" name="sftp_name" id="sftp_name" size="60" class="required focus" required="required" />
311
                    <span class="required">Required</span>
312
                </li>
313
            </ol>
314
        </fieldset>
315
316
        <fieldset class="rows">
317
            <ol>
318
                <li>
319
                    <label for="sftp_host" class="required">Host: </label>
320
                    <input type="text" value="[% sftp_server.host | html %]" name="sftp_host" id="sftp_host" size="60" class="required" />
321
                    <span class="required">Required</span>
322
                </li>
323
                <li>
324
                    <label for="sftp_port" class="required">Port: </label>
325
                    <input type="text" inputmode="numeric" pattern="[0-9]*" value="[% sftp_server.port | html %]" name="sftp_port" id="sftp_port" size="20" class="required"/>
326
                    <span class="required">Required</span>
327
                </li>
328
                <li>
329
                    <label for="sftp_transport" class="required">Transport: </label>
330
                    <select name="sftp_transport" id="sftp_transport" class="required">
331
                        [% IF sftp_server.transport == 'ftp' %]
332
                        <option value="ftp" selected="selected">FTP</option>
333
                        [% ELSE %]
334
                        <option value="ftp">FTP</option>
335
                        [% END %]
336
                        [% IF sftp_server.transport == 'sftp' %]
337
                        <option value="sftp" selected="selected">SFTP</option>
338
                        [% ELSE %]
339
                        <option value="sftp">SFTP</option>
340
                        [% END %]
341
                    </select>
342
                    <span class="required">Required</span>
343
                </li>
344
                <li>
345
                    <label for="sftp_passive">Passive mode: </label>
346
                    <select name="sftp_passive" id="sftp_passive" disabled="disabled">
347
                        [% IF sftp_server.passive == 1 %]
348
                        <option value="1" selected="selected">Enabled (Recommended)</option>
349
                        [% ELSE %]
350
                        <option value="1">Enabled (Recommended)</option>
351
                        [% END %]
352
                        [% IF sftp_server.passive == 0 %]
353
                        <option value="0" selected="selected">Disabled</option>
354
                        [% ELSE %]
355
                        <option value="0">Disabled</option>
356
                        [% END %]
357
                    </select>
358
                    <span class="hint">Only applies to FTP connections</span>
359
                </li>
360
                <li>
361
                    <label for="sftp_auth_mode">Authentication mode: </label>
362
                    <select name="sftp_auth_mode" id="sftp_auth_mode">
363
                        [% IF sftp_server.auth_mode == 'password' %]
364
                        <option value="password" selected="selected">Password-based</option>
365
                        [% ELSE %]
366
                        <option value="password">Password-based</option>
367
                        [% END %]
368
                        [% IF sftp_server.auth_mode == 'key_file' %]
369
                        <option value="key_file" selected="selected">Key file-based</option>
370
                        [% ELSE %]
371
                        <option value="key_file">Key file-based</option>
372
                        [% END %]
373
                        [% IF sftp_server.auth_mode == 'noauth' %]
374
                        <option value="noauth" selected="selected">No authentication</option>
375
                        [% ELSE %]
376
                        <option value="noauth">No authentication</option>
377
                        [% END %]
378
                    </select>
379
                </li>
380
                <li>
381
                    <label for="sftp_user_name" class="required">Username: </label>
382
                    <input type="text" value="[% sftp_server.user_name | html %]" name="sftp_user_name" id="sftp_user_name" size="60" autocomplete="off" class="required" />
383
                    <span class="required">Required</span>
384
                </li>
385
                <li>
386
                    <label for="sftp_password">Password: </label>
387
                    <input type="password" value="[% sftp_server_plain_text_password | html %]" name="sftp_password" id="sftp_password" size="60" autocomplete="off" />
388
                </li>
389
                <li>
390
                    <label for="sftp_key_file">Key file path: </label>
391
                    <textarea name="sftp_key_file" id="sftp_key_file" rows="10" cols="58">[% sftp_server_plain_text_key | html %]</textarea>
392
                    <span class="hint">Only applies to SFTP connections</span>
393
                </li>
394
                <li>
395
                    <label for="sftp_download_directory" >Remote download directory: </label>
396
                    <input type="text" value="[% sftp_server.download_directory | html %]" name="sftp_download_directory" id="sftp_download_directory" size="60" autocomplete="off" /><br />
397
                    <span class="hint">The path on the remote server where we will download from</span>
398
                </li>
399
                <li>
400
                    <label for="sftp_upload_directory" >Remote upload directory: </label>
401
                    <input type="text" value="[% sftp_server.upload_directory | html %]" name="sftp_upload_directory" id="sftp_upload_directory" size="60" autocomplete="off" /><br />
402
                    <span class="hint">The path on the remote server where we will upload to</span>
403
                </li>
404
                <input type="hidden" value="" name="sftp_status" id="sftp_status">
405
                <li>
406
                    <label for="sftp_debug_mode">Debug mode: </label>
407
                    <select name="sftp_debug_mode" id="sftp_debug_mode">
408
                        [% IF sftp_server.debug == 1 %]
409
                        <option value="1" selected="selected">Enabled</option>
410
                        [% ELSE %]
411
                        <option value="1">Enabled</option>
412
                        [% END %]
413
                        [% IF sftp_server.debug == 0 %]
414
                        <option value="0" selected="selected">Disabled</option>
415
                        [% ELSE %]
416
                        <option value="0">Disabled</option>
417
                        [% END %]
418
                    </select>
419
                    <span class="hint">Enables additional debug output in the logs</span>
420
                </li>
421
            </ol>
422
        </fieldset>
423
        <fieldset class="action">
424
            <a id="confirm_key_accept_submit" data-bs-target="#confirm_key_accept" class="btn btn-primary" data-bs-toggle="modal">Submit</a>
425
            <a class="cancel" href="/cgi-bin/koha/admin/sftp_servers.pl">Cancel</a>
426
        </fieldset>
427
    </form>
428
[% END %]
429
406
430
[% IF op == 'test_form' %]
407
                    <h1>FTP/SFTP servers</h1>
431
408
432
    <h1>[% tx("Test FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %]</h1>
409
                    [% IF servers_count < 1 %]
433
    [% IF sftp_server.id %]
410
                        <div class="alert alert-info" id="dno_servers_message">
434
    <div class="page-section">
411
                            <p>
435
        [% IF sftp_server.transport == 'sftp' %]
412
                                <em>There are no FTP/SFTP servers defined.</em><br />
436
        <div class="alert alert-warning">
413
                                To create one, use the <strong>new FTP/SFTP server</strong> button above.
437
            <strong>For your information:</strong> As the transport in use for this server is SFTP, please be aware that running a test attempt will also accept the current host key fingerprint of the destination server. If you have doubts about the authenticity of this server, remove its host key from your authorized_keys file immediately, and contact your System Administrator.
414
                            </p>
438
        </div>
439
        [% END %]
440
        <div class="row">
441
            <div class="col-12 col-lg-6 order-1 order-lg-0">
442
                <h3>Test results</h3>
443
                <div id="testOutput">
444
                    <!-- tests will appear here -->
445
                </div>
446
            </div>
447
            <div class="col-12 col-lg-6 order-0 order-lg-1">
448
                <h3>Test details</h3>
449
                <p>Connection details are as follows:</p>
450
                <table class="mx-4 mb-3">
451
                    <thead></thead>
452
                    <tbody>
453
                        <tr>
454
                            <td><strong>Host</strong></td>
455
                            <td>[% sftp_server.host | html %]</td>
456
                        </tr>
457
                        <tr>
458
                            <td><strong>Port</strong></td>
459
                            <td>[% sftp_server.port | html %]</td>
460
                        </tr>
461
                        <tr>
462
                            <td><strong>Transport</strong></td>
463
                            <td>[% sftp_server.transport FILTER upper | html %]</td>
464
                        </tr>
465
                        <tr>
466
                            <td><strong>Username</strong></td>
467
                            <td>[% sftp_server.user_name | html %]</td>
468
                        </tr>
469
                        <tr>
470
                            <td><strong>Authentication mode</strong></td>
471
                            <td>
472
                                [% IF sftp_server.auth_mode == 'password' %]
473
                                Password-based
474
                                [% ELSE %]
475
                                Key file-based
476
                                [% END %]
477
                            </td>
478
                        </tr>
479
                        <tr>
480
                            <td><strong>Status</strong></td>
481
                            <td>
482
                                [% SWITCH sftp_server.status %]
483
                                [% CASE 'tests_ok' %]
484
                                    Tests passing
485
                                [% CASE 'tests_failed' %]
486
                                    Tests failing
487
                                [% CASE %]
488
                                    <em>Never used</em>
489
                                [% END %]
490
                            </td>
491
                        </tr>
492
                    </tbody>
493
                </table>
494
            </div>
495
        </div>
496
    </div>
497
    <fieldset class="action">
498
        <a href="/cgi-bin/koha/admin/sftp_servers.pl?op=edit_form&sftp_server_id=[% sftp_server.id | uri %]" class="btn btn-primary"><i class="fa-solid fa-pencil" aria-hidden="true"></i> Edit</a>
499
        <a href="/cgi-bin/koha/admin/sftp_servers.pl?op=test_form&sftp_server_id=[% sftp_server.id | uri %]" class="cancel">Reset</a>
500
    </fieldset>
501
    [% ELSE %]
502
    <div class="page-section">
503
        <h3>Oops &ndash; Not Found</h3>
504
        <p>An FTP/SFTP server with that ID was not found. Please go back and try again.</p>
505
    </div>
506
    [% END %]
507
[% END %]
508
509
[% IF op == 'list' %]
510
511
    <div id="toolbar" class="btn-toolbar">
512
        <a class="btn btn-default" id="new_sftp_server" href="/cgi-bin/koha/admin/sftp_servers.pl?op=add_form"><i class="fa fa-plus"></i> New FTP/SFTP server</a>
513
    </div>
514
515
    <h1>FTP/SFTP servers</h1>
516
517
    [% IF servers_count < 1 %]
518
        <div class="alert alert-info" id="dno_servers_message">
519
            <p>
520
                <em>There are no FTP/SFTP servers defined.</em><br />
521
                To create one, use the <strong>new FTP/SFTP server</strong> button above.
522
            </p>
523
        </div>
524
    [% ELSE %]
525
        <div class="page-section">
526
            <table id="sftp_servers">
527
                <thead>
528
                    <tr>
529
                        <th>Name</th>
530
                        <th>Host</th>
531
                        <th>Port</th>
532
                        <th>Transport</th>
533
                        <th>Authentication mode</th>
534
                        <th>Username</th>
535
                        <th>Download directory</th>
536
                        <th>Upload directory</th>
537
                        <th>Status</th>
538
                        <th>Debug</th>
539
                        <th data-class-name="actions noExport">Actions</th>
540
                    </tr>
541
                </thead>
542
            </table>
543
        </div> <!-- /.page-section -->
544
    [% END %]
545
[% END %]
546
547
            <div id="delete_confirm_modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="delete_confirm_modal_label" aria-hidden="true">
548
                <div class="modal-dialog">
549
                    <div class="modal-content">
550
                        <div class="modal-header">
551
                            <h1 class="modal-title" id="delete_confirm_modal_label">Delete server</h1>
552
                            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
553
                        </div>
415
                        </div>
554
                        <div class="modal-body">
416
                    [% ELSE %]
555
                            <div id="delete_confirm_dialog"></div>
417
                        <div class="page-section">
418
                            <table id="sftp_servers">
419
                                <thead>
420
                                    <tr>
421
                                        <th>Name</th>
422
                                        <th>Host</th>
423
                                        <th>Port</th>
424
                                        <th>Transport</th>
425
                                        <th>Authentication mode</th>
426
                                        <th>Username</th>
427
                                        <th>Download directory</th>
428
                                        <th>Upload directory</th>
429
                                        <th>Status</th>
430
                                        <th>Debug</th>
431
                                        <th data-class-name="actions noExport">Actions</th>
432
                                    </tr>
433
                                </thead>
434
                            </table>
556
                        </div>
435
                        </div>
557
                        <div class="modal-footer">
436
                        <!-- /.page-section -->
558
                            <button type="button" class="btn btn-danger" id="delete_confirm_modal_button" data-bs-toggle="modal">Delete</button>
437
                    [% END %]
559
                            <button type="button" class="btn btn-default" data-bs-dismiss="modal">Close</button>
438
                [% END %]
439
440
                <div id="delete_confirm_modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="delete_confirm_modal_label" aria-hidden="true">
441
                    <div class="modal-dialog">
442
                        <div class="modal-content">
443
                            <div class="modal-header">
444
                                <h1 class="modal-title" id="delete_confirm_modal_label">Delete server</h1>
445
                                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
446
                            </div>
447
                            <div class="modal-body">
448
                                <div id="delete_confirm_dialog"></div>
449
                            </div>
450
                            <div class="modal-footer">
451
                                <button type="button" class="btn btn-danger" id="delete_confirm_modal_button" data-bs-toggle="modal">Delete</button>
452
                                <button type="button" class="btn btn-default" data-bs-dismiss="modal">Close</button>
453
                            </div>
560
                        </div>
454
                        </div>
561
                    </div> <!-- /.modal-content -->
455
                        <!-- /.modal-content -->
562
                </div> <!-- /.modal-dialog -->
456
                    </div>
563
            </div> <!-- #delete_confirm_modal -->
457
                    <!-- /.modal-dialog -->
564
458
                </div>
459
                <!-- #delete_confirm_modal -->
565
            </main>
460
            </main>
566
        </div> <!-- /.col-md-10.order-md-2 -->
461
        </div>
462
        <!-- /.col-md-10.order-md-2 -->
567
463
568
        <div class="col-md-2 order-sm-2 order-md-1">
464
        <div class="col-md-2 order-sm-2 order-md-1">
569
            <aside>
465
            <aside> [% INCLUDE 'admin-menu.inc' %] </aside>
570
                [% INCLUDE 'admin-menu.inc' %]
466
        </div>
571
            </aside>
467
        <!-- /.col-md-2.order-md-1 -->
572
        </div> <!-- /.col-md-2.order-md-1 -->
468
    </div>
573
     </div> <!-- /.row -->
469
    <!-- /.row -->
574
470
575
471
    [% MACRO jsinclude BLOCK %]
576
[% MACRO jsinclude BLOCK %]
472
        [% Asset.js("js/admin-menu.js") | $raw %]
577
    [% Asset.js("js/admin-menu.js") | $raw %]
473
        [% Asset.js("js/transport_status.js") | $raw %]
578
    [% INCLUDE 'datatables.inc' %]
474
        [% INCLUDE 'datatables.inc' %]
579
    <script>
475
        <script>
580
        $(document).ready(function() {
476
            $(document).ready(function() {
581
477
582
            var sftp_servers_url = '/api/v1/config/sftp_servers';
478
                var sftp_servers_url = '/api/v1/config/sftp_servers';
583
            window.sftp_servers = $("#sftp_servers").kohaTable({
479
                window.sftp_servers = $("#sftp_servers").kohaTable({
584
                "ajax": {
480
                    "ajax": {
585
                    "url": sftp_servers_url
481
                        "url": sftp_servers_url
586
                },
587
                "language": {
588
                    "emptyTable": "<div class=\"alert alert-info\">"+_("There are no FTP/SFTP servers defined.")+"</div>"
589
                },
590
                "columnDefs": [ {
591
                    "targets": [0,1],
592
                    "render": function(data, type, row, meta) {
593
                        if (type == "display") {
594
                            if(data != null) {
595
                                return data.escapeHtml();
596
                            } else {
597
                                return "Default";
598
                            }
599
                        }
600
                        return data;
601
                    }
602
                } ],
603
                "columns": [
604
                    {
605
                        "data": "name",
606
                        "searchable": true,
607
                        "orderable": true
608
                    },
609
                    {
610
                        "data": "host",
611
                        "searchable": true,
612
                        "orderable": true
613
                    },
614
                    {
615
                        "data": "port",
616
                        "searchable": true,
617
                        "orderable": false
618
                    },
482
                    },
619
                    {
483
                    "language": {
620
                        "data": "transport",
484
                        "emptyTable": "<div class=\"alert alert-info\">"+_("There are no FTP/SFTP servers defined.")+"</div>"
621
                        "render": function(data, type, row, meta) {
622
                            return data.toUpperCase();
623
                        },
624
                        "searchable": true,
625
                        "orderable": false
626
                    },
485
                    },
627
                    {
486
                    "columnDefs": [ {
628
                        "data": "auth_mode",
487
                        "targets": [0,1],
629
                        "render": function(data, type, row, meta) {
488
                        "render": function(data, type, row, meta) {
630
                            if(data == "password") {
489
                            if (type == "display") {
631
                                return _("Password-based");
490
                                if(data != null) {
632
                            } else if(data == "key_file") {
491
                                    return data.escapeHtml();
633
                                return _("Key file-based");
492
                                } else {
634
                            } else {
493
                                    return "Default";
635
                                return _("No authentication");
494
                                }
636
                            }
495
                            }
496
                            return data;
497
                        }
498
                    } ],
499
                    "columns": [
500
                        {
501
                            "data": "name",
502
                            "searchable": true,
503
                            "orderable": true
637
                        },
504
                        },
638
                        "searchable": false,
505
                        {
639
                        "orderable": false
506
                            "data": "host",
640
                    },
507
                            "searchable": true,
641
                    {
508
                            "orderable": true
642
                        "data": "user_name",
643
                        "searchable": false,
644
                        "orderable": false
645
                    },
646
                    {
647
                        "data": "download_directory",
648
                        "render": function(data, type, row, meta) {
649
                            if(data) {
650
                                return data;
651
                            } else {
652
                                return "<em>" + _("Not specified") + "</em>";
653
                            }
654
                        },
509
                        },
655
                        "searchable": false,
510
                        {
656
                        "orderable": false
511
                            "data": "port",
657
                    },
512
                            "searchable": true,
658
                    {
513
                            "orderable": false
659
                        "data": "upload_directory",
660
                        "render": function(data, type, row, meta) {
661
                            if(data) {
662
                                return data;
663
                            } else {
664
                                return "<em>" + _("Not specified") + "</em>";
665
                            }
666
                        },
514
                        },
667
                        "searchable": false,
515
                        {
668
                        "orderable": false
516
                            "data": "transport",
669
                    },
517
                            "render": function(data, type, row, meta) {
670
                    {
518
                                return data.toUpperCase();
671
                        "data": "status",
519
                            },
672
                        "render": function(data, type, row, meta) {
520
                            "searchable": true,
673
                            if (data == "tests_ok") {
521
                            "orderable": false
674
                                return _("Tests passing");
675
                            } else if (data == "tests_failed") {
676
                                return _("Tests failing");
677
                            } else {
678
                                return "<em>" + _("Never used") + "</em>";
679
                            }
680
                        },
522
                        },
681
                        "searchable": false,
523
                        {
682
                        "orderable": false
524
                            "data": "auth_mode",
683
                    },
525
                            "render": function(data, type, row, meta) {
684
                    {
526
                                if(data == "password") {
685
                        "data": "debug",
527
                                    return _("Password-based");
686
                        "render": function(data, type, row, meta) {
528
                                } else if(data == "key_file") {
687
                            if(data == true) {
529
                                    return _("Key file-based");
688
                                return "[% tp("Active", "On") | html %]";
530
                                } else {
689
                            }
531
                                    return _("No authentication");
690
                            else {
532
                                }
691
                                return _("Off");
533
                            },
692
                            }
534
                            "searchable": false,
535
                            "orderable": false
693
                        },
536
                        },
694
                        "searchable": false,
537
                        {
695
                        "orderable": false
538
                            "data": "user_name",
696
                    },
539
                            "searchable": false,
697
                    {
540
                            "orderable": false
698
                        "data": function(row, type, val, meta) {
699
                            var result = '<a class="btn btn-default btn-xs" role="button" href="/cgi-bin/koha/admin/sftp_servers.pl?op=test_form&amp;sftp_server_id='+ encodeURIComponent(row.sftp_server_id) +'"><i class="fa-solid fa-vial" aria-hidden="true"></i> '+_("Test")+'</a>'+"\n";
700
                            result += '<a class="btn btn-default btn-xs" role="button" href="/cgi-bin/koha/admin/sftp_servers.pl?op=edit_form&amp;sftp_server_id='+ encodeURIComponent(row.sftp_server_id) +'"><i class="fa-solid fa-pencil" aria-hidden="true"></i> '+_("Edit")+'</a>'+"\n";
701
                            result += '<a class="btn btn-default btn-xs delete_server" role="button" href="#" data-bs-toggle="modal" data-bs-target="#delete_confirm_modal" data-sftp-server-id="'+ encodeURIComponent(row.sftp_server_id) +'" data-sftp-server-name="'+ encodeURIComponent(row.name.escapeHtml()) +'"><i class="fa fa-trash-can" aria-hidden="true"></i> '+_("Delete")+'</a>';
702
                            return result;
703
                        },
541
                        },
704
                        "searchable": false,
542
                        {
705
                        "orderable": false
543
                            "data": "download_directory",
706
                    }
544
                            "render": function(data, type, row, meta) {
707
                ],
545
                                if(data) {
708
                createdRow: function(row, data, dataIndex) {
546
                                    return data;
709
                    if(data.is_default) {
547
                                } else {
710
                        $(row).addClass('default warn');
548
                                    return "<em>" + _("Not specified") + "</em>";
711
                    }
549
                                }
712
                    if(data.debug) {
550
                            },
713
                        $(row).addClass('debug');
551
                            "searchable": false,
714
                    }
552
                            "orderable": false
715
                },
553
                        },
716
            });
554
                        {
717
555
                            "data": "upload_directory",
718
            $('#sftp_servers').on("click", '.delete_server', function() {
556
                            "render": function(data, type, row, meta) {
719
                var sftp_server_id   = $(this).data('sftp-server-id');
557
                                if(data) {
720
                var sftp_server_name = decodeURIComponent($(this).data('sftp-server-name'));
558
                                    return data;
721
559
                                } else {
722
                $("#delete_confirm_dialog").html(
560
                                    return "<em>" + _("Not specified") + "</em>";
723
                    _("You are about to delete the '%s' FTP/SFTP server.").format(sftp_server_name)
561
                                }
724
                );
562
                            },
725
                $("#delete_confirm_modal_button").data('sftp-server-id', sftp_server_id);
563
                            "searchable": false,
726
                $("#delete_confirm_modal_button").data('sftp-server-name', sftp_server_name);
564
                            "orderable": false
727
            });
565
                        },
566
                        {
567
                            "data": "status",
568
                            "render": function(data, type, row, meta) {
569
                                let render = '';
570
                                if (data) {
571
                                    if (data.status == "ok") {
572
                                        render += '<i class="text-success fa-solid fa-circle-check"></i> ';
573
                                        render += '<span class="text-success">';
574
                                        render += _("Tests passing");
575
                                        render += '</span>'
576
                                    } else if (data.status == "errors") {
577
                                        data.operations.forEach(operation => {
578
                                            render += "<div>";
579
                                            if ( operation.status == "error" ) {
580
                                                render += '<i class="text-danger fa-solid fa-circle-xmark"></i> ';
581
                                                render += '<span class="text-danger">';
582
                                                render += operationLabels[operation.code] || operation.code;
583
                                                render += ' ' + _("failed");
584
                                                render += '</span>';
585
                                            } else {
586
                                                render += '<i class="text-success fa-solid fa-circle-check"></i> ';
587
                                                render += '<span class="text-success">';
588
                                                render += operationLabels[operation.code] || operation.code;
589
                                                render += ' ' + _("ok");
590
                                                render += '</span>';
591
                                            }
592
                                            render += "</div>";
593
                                        });
594
                                    } else {
595
                                        render += "<em>" + _("Never used") + "</em>";
596
                                    }
597
                                } else {
598
                                    render += "<em>" + _("Never used") + "</em>";
599
                                }
600
                                return render;
601
                            },
602
                            "searchable": false,
603
                            "orderable": false
604
                        },
605
                        {
606
                            "data": "debug",
607
                            "render": function(data, type, row, meta) {
608
                                if(data == true) {
609
                                    return "[% tp("Active", "On") | html %]";
610
                                }
611
                                else {
612
                                    return _("Off");
613
                                }
614
                            },
615
                            "searchable": false,
616
                            "orderable": false
617
                        },
618
                        {
619
                            "data": function(row, type, val, meta) {
620
                                let result = '<a class="btn btn-default btn-xs" role="button" href="/cgi-bin/koha/admin/sftp_servers.pl?op=edit_form&amp;sftp_server_id='+ encodeURIComponent(row.sftp_server_id) +'"><i class="fa-solid fa-pencil" aria-hidden="true"></i> '+_("Edit")+'</a>'+"\n";
621
                                result += '<a class="btn btn-default btn-xs delete_server" role="button" href="#" data-bs-toggle="modal" data-bs-target="#delete_confirm_modal" data-sftp-server-id="'+ encodeURIComponent(row.sftp_server_id) +'" data-sftp-server-name="'+ encodeURIComponent(row.name.escapeHtml()) +'"><i class="fa fa-trash-can" aria-hidden="true"></i> '+_("Delete")+'</a>';
622
                                return result;
623
                            },
624
                            "searchable": false,
625
                            "orderable": false
626
                        }
627
                    ],
628
                    createdRow: function(row, data, dataIndex) {
629
                        if(data.is_default) {
630
                            $(row).addClass('default warn');
631
                        }
632
                        if(data.debug) {
633
                            $(row).addClass('debug');
634
                        }
635
                    },
636
                });
728
637
729
            $("#delete_confirm_modal_button").on("click", function() {
638
                $('#sftp_servers').on("click", '.delete_server', function() {
639
                    var sftp_server_id   = $(this).data('sftp-server-id');
640
                    var sftp_server_name = decodeURIComponent($(this).data('sftp-server-name'));
730
641
731
                var sftp_server_id   = $(this).data('sftp-server-id');
642
                    $("#delete_confirm_dialog").html(
732
                var sftp_server_name = $(this).data('sftp-server-name');
643
                        _("You are about to delete the '%s' FTP/SFTP server.").format(sftp_server_name)
644
                    );
645
                    $("#delete_confirm_modal_button").data('sftp-server-id', sftp_server_id);
646
                    $("#delete_confirm_modal_button").data('sftp-server-name', sftp_server_name);
647
                });
733
648
734
                $.ajax({
649
                $("#delete_confirm_modal_button").on("click", function() {
735
                    method: "DELETE",
650
736
                    url: "/api/v1/config/sftp_servers/"+sftp_server_id
651
                    var sftp_server_id   = $(this).data('sftp-server-id');
737
                }).success(function() {
652
                    var sftp_server_name = $(this).data('sftp-server-name');
738
                    window.sftp_servers.api().ajax.reload(function(data) {
653
739
                        $("#sftp_action_result_dialog").hide();
654
                    $.ajax({
740
                        $("#sftp_delete_success").html(_("Server '%s' deleted successfully.").format(sftp_server_name)).show();
655
                        method: "DELETE",
656
                        url: "/api/v1/config/sftp_servers/"+sftp_server_id
657
                    }).success(function() {
658
                        window.sftp_servers.api().ajax.reload(function(data) {
659
                            $("#sftp_action_result_dialog").hide();
660
                            $("#sftp_delete_success").html(_("Server '%s' deleted successfully.").format(sftp_server_name)).show();
661
                        });
662
                    }).fail(function() {
663
                        $("#sftp_delete_error").html(_("Error deleting server '%s'. Please ensure all linked EDI accounts are unlinked or deleted. Check the logs for details.").format(sftp_server_name)).show();
664
                    }).done(function() {
665
                        $("#delete_confirm_modal").modal('hide');
741
                    });
666
                    });
742
                }).fail(function() {
743
                    $("#sftp_delete_error").html(_("Error deleting server '%s'. Please ensure all linked EDI accounts are unlinked or deleted. Check the logs for details.").format(sftp_server_name)).show();
744
                }).done(function() {
745
                    $("#delete_confirm_modal").modal('hide');
746
                });
667
                });
747
            });
748
668
749
            transportChange();
750
            $("#sftp_transport").on("change", function(event) {
751
                transportChange();
669
                transportChange();
752
            });
670
                $("#sftp_transport").on("change", function(event) {
671
                    transportChange();
672
                });
753
673
754
            authModeChange();
755
            $("#sftp_auth_mode").on("change", function(event) {
756
                authModeChange();
674
                authModeChange();
757
            });
675
                $("#sftp_auth_mode").on("change", function(event) {
676
                    authModeChange();
677
                });
758
678
759
            $('#confirm_key_accept_submit').on('click', function(event) {
679
                $('#confirm_key_accept_submit').on('click', function(event) {
760
                event.preventDefault();
680
                    event.preventDefault();
761
681
762
                if ( $('#add').length > 0 ) {
682
                    if ( $('#add').length > 0 ) {
763
                    if( $('#add').valid() == true ) {
683
                        if( $('#add').valid() == true ) {
764
                        modalChange();
684
                            modalChange();
765
                        $('#confirm_key_accept').modal('show');
685
                            $('#confirm_key_accept').modal('show');
766
                    } else {
686
                        } else {
767
                        $('#confirm_key_accept').modal('hide');
687
                            $('#confirm_key_accept').modal('hide');
688
                        }
768
                    }
689
                    }
769
                }
770
690
771
                if ( $('#edit_save').length > 0 ) {
691
                    if ( $('#edit_save').length > 0 ) {
772
                    if( $('#edit_save').valid() == true ) {
692
                        if( $('#edit_save').valid() == true ) {
773
                        modalChange();
693
                            modalChange();
774
                        $('#confirm_key_accept').modal('show');
694
                            $('#confirm_key_accept').modal('show');
775
                    } else {
695
                        } else {
776
                        $('#confirm_key_accept').modal('hide');
696
                            $('#confirm_key_accept').modal('hide');
697
                        }
777
                    }
698
                    }
778
                }
779
699
780
            });
700
                });
781
701
782
            $('#confirm_key_accept .approve').on('click', function() {
702
                $('#confirm_key_accept .approve').on('click', function() {
783
                $('#confirm_key_accept .deny').click();
703
                    $('#confirm_key_accept .deny').click();
784
704
785
                if ( $('#add').length > 0 ) {
705
                    if ( $('#add').length > 0 ) {
786
                    $('#add').submit();
706
                        $('#add').submit();
787
                }
707
                    }
708
709
                    if ( $('#edit_save').length > 0 ) {
710
                        $('#edit_save').submit();
711
                    }
712
                });
788
713
789
                if ( $('#edit_save').length > 0 ) {
790
                    $('#edit_save').submit();
791
                }
792
            });
714
            });
793
715
794
        });
716
            function transportChange() {
795
717
                let sftp_transport = $("#sftp_transport");
796
        function transportChange() {
718
797
            let sftp_transport = $("#sftp_transport");
719
                if(sftp_transport.val() == "ftp") {
798
720
                    $("#sftp_host").removeAttr("disabled");
799
            if(sftp_transport.val() == "ftp") {
721
                    $("#sftp_port").removeAttr("disabled");
800
                $("#sftp_host").removeAttr("disabled");
722
                    $("#sftp_passive").removeAttr("disabled");
801
                $("#sftp_port").removeAttr("disabled");
723
                    $("#sftp_auth_mode").removeAttr("disabled");
802
                $("#sftp_passive").removeAttr("disabled");
724
                    $("#sftp_user_name").removeAttr("disabled");
803
                $("#sftp_auth_mode").removeAttr("disabled");
725
                    $("#sftp_password").removeAttr("disabled");
804
                $("#sftp_user_name").removeAttr("disabled");
726
                    $("#sftp_key_file").attr("disabled", "disabled");
805
                $("#sftp_password").removeAttr("disabled");
727
806
                $("#sftp_key_file").attr("disabled", "disabled");
728
                    $("#sftp_auth_mode option[value='password']").removeAttr("disabled");
807
729
                    $("#sftp_auth_mode option[value='key_file']").attr("disabled", "disabled");
808
                $("#sftp_auth_mode option[value='password']").removeAttr("disabled");
730
                    $("#sftp_auth_mode option[value='noauth']").removeAttr("disabled");
809
                $("#sftp_auth_mode option[value='key_file']").attr("disabled", "disabled");
731
                    if($("#sftp_auth_mode option:selected").val() == "key_file") {
810
                $("#sftp_auth_mode option[value='noauth']").removeAttr("disabled");
732
                        $("#sftp_auth_mode option[value='password']").prop("selected", true);
811
                if($("#sftp_auth_mode option:selected").val() == "key_file") {
733
                    }
812
                    $("#sftp_auth_mode option[value='password']").prop("selected", true);
813
                }
814
734
815
                let sftp_port = $("#sftp_port").val();
735
                    let sftp_port = $("#sftp_port").val();
816
                if(sftp_port == 22) $("#sftp_port").val("21");
736
                    if(sftp_port == 22) $("#sftp_port").val("21");
817
737
818
                authModeChange();
738
                    authModeChange();
819
            } else if(sftp_transport.val() == "sftp") {
739
                } else if(sftp_transport.val() == "sftp") {
820
                $("#sftp_host").removeAttr("disabled");
740
                    $("#sftp_host").removeAttr("disabled");
821
                $("#sftp_port").removeAttr("disabled");
741
                    $("#sftp_port").removeAttr("disabled");
822
                $("#sftp_passive").attr("disabled", "disabled");
742
                    $("#sftp_passive").attr("disabled", "disabled");
823
                $("#sftp_auth_mode").removeAttr("disabled");
743
                    $("#sftp_auth_mode").removeAttr("disabled");
824
                $("#sftp_user_name").removeAttr("disabled");
744
                    $("#sftp_user_name").removeAttr("disabled");
825
                $("#sftp_password").removeAttr("disabled");
745
                    $("#sftp_password").removeAttr("disabled");
826
                $("#sftp_key_file").removeAttr("disabled");
746
                    $("#sftp_key_file").removeAttr("disabled");
827
828
                $("#sftp_auth_mode option[value='password']").removeAttr("disabled");
829
                $("#sftp_auth_mode option[value='key_file']").removeAttr("disabled");
830
                $("#sftp_auth_mode option[value='noauth']").removeAttr("disabled");
831
                $("#sftp_passive option[value='1']").prop("selected", true);
832
833
                let sftp_port = $("#sftp_port").val();
834
                if(sftp_port == 21) $("#sftp_port").val("22");
835
836
                return authModeChange();
837
            }
838
        }
839
840
        function authModeChange() {
841
            let sftp_auth_mode = $("#sftp_auth_mode").val();
842
843
            if(sftp_auth_mode == "password") {
844
                $("#sftp_password").removeAttr("disabled");
845
                $("#sftp_key_file").attr("disabled", "disabled");
846
            } else if(sftp_auth_mode == "key_file") {
847
                $("#sftp_password").attr("disabled", "disabled");
848
                $("#sftp_key_file").removeAttr("disabled");
849
            } else {
850
                $("#sftp_password").attr("disabled", "disabled");
851
                $("#sftp_key_file").attr("disabled", "disabled");
852
            }
853
        }
854
855
        function modalChange() {
856
            $('#modal_message').hide();
857
            if ( $('#sftp_transport').val() == 'sftp' ) $('#modal_message').show();
858
859
            $('#modal_host').text( $('#sftp_host').val() );
860
            $('#modal_port').text( $('#sftp_port').val() );
861
            $('#modal_transport').text( $('#sftp_transport option:selected').text() );
862
            $('#modal_user_name').text( $('#sftp_user_name').val() );
863
            $('#modal_auth_mode').text( $('#sftp_auth_mode option:selected').text() );
864
        }
865
    </script>
866
867
    [% IF op == 'test_form' %]
868
    <script>
869
        $(document).ready(function() {
870
            handleTests();
871
        });
872
873
        function handleTests() {
874
            var testOutput = $('#testOutput');
875
            var runTests   = $('#runTests');
876
877
            runTests.addClass('disabled');
878
            testOutput.html('<div class="spinner-border text-warning" style="height:1.5rem;width:1.5rem" role="status"><span class="sr-only">Loading...</span></div><span class="pending-loading">' + _("Running tests . . . ") + '</span>');
879
880
            return $.ajax({
881
                url: "/api/v1/sftp_server/[% sftp_server.id | html %]/test_connection",
882
            })
883
            .done(function(data) {
884
                testOutput.text('');
885
886
                for ( let [key, value] of Object.entries( data ) ) {
887
                    var title;
888
                    switch(key) {
889
                        case '1_sftp_conn':
890
                            title = _("Testing SFTP connectivity");
891
                            break;
892
                        case '1_ftp_conn':
893
                            title = _("Testing FTP connectivity");
894
                            break;
895
                        case '2_ftp_login':
896
                            title = _("Testing we can log in");
897
                            break;
898
                        case '2a_sftp_cwd_dl':
899
                        case '3a_ftp_cwd_dl':
900
                            title = _("Testing we can cwd to download directory");
901
                            break;
902
                        case '2b_sftp_ls_dl':
903
                        case '3b_ftp_ls_dl':
904
                            title = _("Testing we can list download directory");
905
                            break;
906
                        case '2c_sftp_cwd_ul':
907
                        case '3c_ftp_cwd_ul':
908
                            title = _("Testing we can cwd to upload directory");
909
                            break;
910
                        case '2d_sftp_ls_ul':
911
                        case '3d_ftp_ls_ul':
912
                            title = _("Testing we can list upload directory");
913
                            break;
914
                        case '3_sftp_write':
915
                        case '4_ftp_write':
916
                            title = _("Testing we can write a test file");
917
                            break;
918
                        case '4_sftp_del':
919
                        case '5_ftp_del':
920
                            title = _("Testing we can delete test file");
921
                            break;
922
                        default:
923
                            title = key
924
                    }
925
747
926
                    if ( value.passed ) {
748
                    $("#sftp_auth_mode option[value='password']").removeAttr("disabled");
927
                        testOutput.append(
749
                    $("#sftp_auth_mode option[value='key_file']").removeAttr("disabled");
928
                            '<i class="text-success fa-solid fa-circle-check"></i> '
750
                    $("#sftp_auth_mode option[value='noauth']").removeAttr("disabled");
929
                            + title
751
                    $("#sftp_passive option[value='1']").prop("selected", true);
930
                            + '... <span class="text-success">'
752
931
                            + _("Passed")
753
                    let sftp_port = $("#sftp_port").val();
932
                            + '</span><br />'
754
                    if(sftp_port == 21) $("#sftp_port").val("22");
933
                        );
755
934
                        if( value.msg ) testOutput.append( _("Message: ") + '<code>' + value.msg + '</code><br />' );
756
                    return authModeChange();
935
                    } else {
936
                        testOutput.append(
937
                            '<i class="text-danger fa-solid fa-circle-xmark"></i> '
938
                            + title
939
                            + '... <span class="text-danger">'
940
                            + _("Failed")
941
                            + '</span><br />'
942
                        );
943
                        if( value.err ) testOutput.append( _("Error message: ") + '<code>' + value.err + '</code><br />' );
944
                    }
945
                    testOutput.append( '<br />' );
946
                }
757
                }
947
            })
758
            }
948
            .fail(function(data) {
759
949
                if( data.status == 404 ) {
760
            function authModeChange() {
950
                    return testOutput.text( '<i class="text-success fa-solid fa-circle-check"></i> ' + _("FTP/SFTP Server not found") );
761
                let sftp_auth_mode = $("#sftp_auth_mode").val();
762
763
                if(sftp_auth_mode == "password") {
764
                    $("#sftp_password").removeAttr("disabled");
765
                    $("#sftp_key_file").attr("disabled", "disabled");
766
                } else if(sftp_auth_mode == "key_file") {
767
                    $("#sftp_password").attr("disabled", "disabled");
768
                    $("#sftp_key_file").removeAttr("disabled");
951
                } else {
769
                } else {
952
                    return testOutput.text( '<i class="text-success fa-solid fa-circle-check"></i> ' + _("Internal Server Error. Please check the server logs") );
770
                    $("#sftp_password").attr("disabled", "disabled");
771
                    $("#sftp_key_file").attr("disabled", "disabled");
953
                }
772
                }
954
            })
773
            }
955
            .always(function(data) {
774
956
                runTests.removeClass('disabled');
775
            function modalChange() {
957
            });
776
                $('#modal_message').hide();
958
        }
777
                if ( $('#sftp_transport').val() == 'sftp' ) $('#modal_message').show();
959
    </script>
778
779
                $('#modal_host').text( $('#sftp_host').val() );
780
                $('#modal_port').text( $('#sftp_port').val() );
781
                $('#modal_transport').text( $('#sftp_transport option:selected').text() );
782
                $('#modal_user_name').text( $('#sftp_user_name').val() );
783
                $('#modal_auth_mode').text( $('#sftp_auth_mode option:selected').text() );
784
            }
785
        </script>
960
    [% END %]
786
    [% END %]
961
[% END %]
962
787
963
[% INCLUDE 'intranet-bottom.inc' %]
788
    [% INCLUDE 'intranet-bottom.inc' %]
789
</div>
(-)a/koha-tmpl/intranet-tmpl/prog/js/transport_status.js (-1 / +7 lines)
Line 0 Link Here
0
- 
1
let operationLabels = {
2
    connection: __("Connection"),
3
    upload: __("Upload"),
4
    download: __("Download"),
5
    list: __("List Files"),
6
    change_directory: __("Change Directory"),
7
};

Return to bug 39190