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 / +701 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">Because you are using the SFTP transport, please run a test of this connection to accept the server's 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>Host</strong></td>
174
            <ol>
100
                                                <td id="modal_host"></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>Port</strong></td>
178
                    <span class="required">Required</span>
104
                                                <td id="modal_port"></td>
179
                </li>
105
                                            </tr>
180
                <li>
106
                                            <tr>
181
                    <label for="sftp_port" class="required">Port: </label>
107
                                                <td><strong>Transport</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_transport"></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_transport" class="required">Transport: </label>
168
                                    <select name="sftp_transport" id="sftp_transport" class="required">
169
                                        <option value="ftp">FTP</option>
170
                                        <option value="sftp" selected="selected">SFTP</option>
171
                                    </select>
172
                                    <span class="required">Required</span>
173
                                </li>
174
                                <li>
175
                                    <label for="sftp_passive">Passive mode: </label>
176
                                    <select name="sftp_passive" id="sftp_passive" disabled="disabled">
177
                                        <option value="1" selected="selected">On (Recommended)</option>
178
                                        <option value="0">Off</option>
179
                                    </select>
180
                                    <span class="hint">Only applies to FTP connections</span>
181
                                </li>
182
                                <li>
183
                                    <label for="sftp_auth_mode">Authentication mode: </label>
184
                                    <select name="sftp_auth_mode" id="sftp_auth_mode">
185
                                        <option value="password" selected="selected">Password-based</option>
186
                                        <option value="key_file">Key file-based</option>
187
                                        <option value="noauth">No authentication</option>
188
                                    </select>
189
                                </li>
190
                                <li>
191
                                    <label for="sftp_user_name" class="required">Username: </label>
192
                                    <input type="text" name="sftp_user_name" id="sftp_user_name" size="60" autocomplete="off" class="required" />
193
                                    <span class="required">Required</span>
194
                                </li>
195
                                <li>
196
                                    <label for="sftp_password">Password: </label>
197
                                    <input type="password" name="sftp_password" id="sftp_password" size="60" autocomplete="off" />
198
                                </li>
199
                                <li>
200
                                    <label for="sftp_key_file">Key file: </label>
201
                                    <textarea name="sftp_key_file" id="sftp_key_file" rows="10" cols="58"></textarea>
202
                                    <span class="hint">Only applies to SFTP connections</span>
203
                                </li>
204
                                <li>
205
                                    <label for="sftp_download_directory">Remote download directory: </label>
206
                                    <input type="text" name="sftp_download_directory" id="sftp_download_directory" size="60" autocomplete="off" /><br />
207
                                    <span class="hint">The path on the remote server where we will download from</span>
208
                                </li>
209
                                <li>
210
                                    <label for="sftp_upload_directory">Remote upload directory: </label>
211
                                    <input type="text" name="sftp_upload_directory" id="sftp_upload_directory" size="60" autocomplete="off" /><br />
212
                                    <span class="hint">The path on the remote server where we will upload to</span>
213
                                </li>
214
                                <input type="hidden" value="" name="sftp_status" id="sftp_status" />
215
                                <li>
216
                                    <label for="sftp_debug_mode">Debug mode: </label>
217
                                    <select name="sftp_debug_mode" id="sftp_debug_mode">
218
                                        <option value="1">Enabled</option>
219
                                        <option value="0" selected="selected">Disabled</option>
220
                                    </select>
221
                                    <span class="hint">Enables additional debug output in the logs</span>
222
                                </li>
223
                            </ol>
224
                        </fieldset>
225
                        <fieldset class="action">
226
                            <a id="confirm_key_accept_submit" data-bs-target="#confirm_key_accept" class="btn btn-primary" data-bs-toggle="modal">Submit</a>
227
                            <a class="cancel" href="/cgi-bin/koha/admin/sftp_servers.pl">Cancel</a>
228
                        </fieldset>
229
                    </form>
230
                [% END %]
231
232
                [% IF op == 'edit_form' && !messages %]
233
                    <!-- Modal -->
234
                    <div id="confirm_key_accept" class="modal" tabindex="-1" role="dialog" aria-labelledby="confirm_key_accept_submit" aria-hidden="true">
235
                        <div class="modal-dialog modal-lg">
236
                            <div class="modal-content modal-lg">
237
                                <div class="modal-header">
238
                                    <h1 class="modal-title">Are you sure?</h1>
239
                                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
240
                                </div>
241
                                <div class="modal-body">
242
                                    <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>
243
                                    <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>
244
                                    <table class="mx-4 mb-3">
245
                                        <thead></thead>
246
                                        <tbody>
247
                                            <tr>
248
                                                <td><strong>Host</strong></td>
249
                                                <td id="modal_host"></td>
250
                                            </tr>
251
                                            <tr>
252
                                                <td><strong>Port</strong></td>
253
                                                <td id="modal_port"></td>
254
                                            </tr>
255
                                            <tr>
256
                                                <td><strong>Transport</strong></td>
257
                                                <td id="modal_transport"></td>
258
                                            </tr>
259
                                            <tr>
260
                                                <td><strong>Username</strong></td>
261
                                                <td id="modal_user_name"></td>
262
                                            </tr>
263
                                            <tr>
264
                                                <td><strong>Authentication mode</strong></td>
265
                                                <td id="modal_auth_mode"></td>
266
                                            </tr>
267
                                        </tbody>
268
                                    </table>
269
                                    <p>If you are ready to progress with these details, please click Save.</p>
270
                                </div>
271
                                <div class="modal-footer">
272
                                    <button class="btn btn-default approve" type="submit"><i class="fa fa-check"></i> Save</button>
273
                                    <button class="btn btn-default deny cancel" type="button" data-bs-dismiss="modal"><i class="fa fa-times"></i> Cancel</button>
274
                                </div>
275
                            </div>
276
                        </div>
289
                    </div>
277
                    </div>
290
                    <div class="modal-footer">
278
                    <!-- END Modal -->
291
                        <button class="btn btn-default approve" type="submit"><i class="fa fa-check"></i> Save</button>
279
292
                        <button class="btn btn-default deny cancel" type="button" data-bs-dismiss="modal"><i class="fa fa-times"></i> Cancel</button>
280
                    <h1>[% tx("Modify FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %]</h1>
281
282
                    <form action="/cgi-bin/koha/admin/sftp_servers.pl" id="edit_save" name="edit_save" class="validated" method="post">
283
                        [% INCLUDE 'csrf-token.inc' %]
284
                        <input type="hidden" name="op" value="cud-edit_save" />
285
                        <input type="hidden" name="sftp_server_id" value="[%- sftp_server.id | html -%]" />
286
                        <fieldset class="rows">
287
                            <ol>
288
                                <li>
289
                                    <label for="sftp_name" class="required">Name: </label>
290
                                    <input type="text" value="[% sftp_server.name | html %]" name="sftp_name" id="sftp_name" size="60" class="required focus" required="required" />
291
                                    <span class="required">Required</span>
292
                                </li>
293
                            </ol>
294
                        </fieldset>
295
296
                        <fieldset class="rows">
297
                            <ol>
298
                                <li>
299
                                    <label for="sftp_host" class="required">Host: </label>
300
                                    <input type="text" value="[% sftp_server.host | html %]" name="sftp_host" id="sftp_host" size="60" class="required" />
301
                                    <span class="required">Required</span>
302
                                </li>
303
                                <li>
304
                                    <label for="sftp_port" class="required">Port: </label>
305
                                    <input type="text" inputmode="numeric" pattern="[0-9]*" value="[% sftp_server.port | html %]" name="sftp_port" id="sftp_port" size="20" class="required" />
306
                                    <span class="required">Required</span>
307
                                </li>
308
                                <li>
309
                                    <label for="sftp_transport" class="required">Transport: </label>
310
                                    <select name="sftp_transport" id="sftp_transport" class="required">
311
                                        [% IF sftp_server.transport == 'ftp' %]
312
                                            <option value="ftp" selected="selected">FTP</option>
313
                                        [% ELSE %]
314
                                            <option value="ftp">FTP</option>
315
                                        [% END %]
316
                                        [% IF sftp_server.transport == 'sftp' %]
317
                                            <option value="sftp" selected="selected">SFTP</option>
318
                                        [% ELSE %]
319
                                            <option value="sftp">SFTP</option>
320
                                        [% END %]
321
                                    </select>
322
                                    <span class="required">Required</span>
323
                                </li>
324
                                <li>
325
                                    <label for="sftp_passive">Passive mode: </label>
326
                                    <select name="sftp_passive" id="sftp_passive" disabled="disabled">
327
                                        [% IF sftp_server.passive == 1 %]
328
                                            <option value="1" selected="selected">Enabled (Recommended)</option>
329
                                        [% ELSE %]
330
                                            <option value="1">Enabled (Recommended)</option>
331
                                        [% END %]
332
                                        [% IF sftp_server.passive == 0 %]
333
                                            <option value="0" selected="selected">Disabled</option>
334
                                        [% ELSE %]
335
                                            <option value="0">Disabled</option>
336
                                        [% END %]
337
                                    </select>
338
                                    <span class="hint">Only applies to FTP connections</span>
339
                                </li>
340
                                <li>
341
                                    <label for="sftp_auth_mode">Authentication mode: </label>
342
                                    <select name="sftp_auth_mode" id="sftp_auth_mode">
343
                                        [% IF sftp_server.auth_mode == 'password' %]
344
                                            <option value="password" selected="selected">Password-based</option>
345
                                        [% ELSE %]
346
                                            <option value="password">Password-based</option>
347
                                        [% END %]
348
                                        [% IF sftp_server.auth_mode == 'key_file' %]
349
                                            <option value="key_file" selected="selected">Key file-based</option>
350
                                        [% ELSE %]
351
                                            <option value="key_file">Key file-based</option>
352
                                        [% END %]
353
                                        [% IF sftp_server.auth_mode == 'noauth' %]
354
                                            <option value="noauth" selected="selected">No authentication</option>
355
                                        [% ELSE %]
356
                                            <option value="noauth">No authentication</option>
357
                                        [% END %]
358
                                    </select>
359
                                </li>
360
                                <li>
361
                                    <label for="sftp_user_name" class="required">Username: </label>
362
                                    <input type="text" value="[% sftp_server.user_name | html %]" name="sftp_user_name" id="sftp_user_name" size="60" autocomplete="off" class="required" />
363
                                    <span class="required">Required</span>
364
                                </li>
365
                                <li>
366
                                    <label for="sftp_password">Password: </label>
367
                                    <input type="password" value="[% sftp_server_plain_text_password | html %]" name="sftp_password" id="sftp_password" size="60" autocomplete="off" />
368
                                </li>
369
                                <li>
370
                                    <label for="sftp_key_file">Key file path: </label>
371
                                    <textarea name="sftp_key_file" id="sftp_key_file" rows="10" cols="58">[% sftp_server_plain_text_key | html %]</textarea>
372
                                    <span class="hint">Only applies to SFTP connections</span>
373
                                </li>
374
                                <li>
375
                                    <label for="sftp_download_directory">Remote download directory: </label>
376
                                    <input type="text" value="[% sftp_server.download_directory | html %]" name="sftp_download_directory" id="sftp_download_directory" size="60" autocomplete="off" /><br />
377
                                    <span class="hint">The path on the remote server where we will download from</span>
378
                                </li>
379
                                <li>
380
                                    <label for="sftp_upload_directory">Remote upload directory: </label>
381
                                    <input type="text" value="[% sftp_server.upload_directory | html %]" name="sftp_upload_directory" id="sftp_upload_directory" size="60" autocomplete="off" /><br />
382
                                    <span class="hint">The path on the remote server where we will upload to</span>
383
                                </li>
384
                                <input type="hidden" value="" name="sftp_status" id="sftp_status" />
385
                                <li>
386
                                    <label for="sftp_debug_mode">Debug mode: </label>
387
                                    <select name="sftp_debug_mode" id="sftp_debug_mode">
388
                                        [% IF sftp_server.debug == 1 %]
389
                                            <option value="1" selected="selected">Enabled</option>
390
                                        [% ELSE %]
391
                                            <option value="1">Enabled</option>
392
                                        [% END %]
393
                                        [% IF sftp_server.debug == 0 %]
394
                                            <option value="0" selected="selected">Disabled</option>
395
                                        [% ELSE %]
396
                                            <option value="0">Disabled</option>
397
                                        [% END %]
398
                                    </select>
399
                                    <span class="hint">Enables additional debug output in the logs</span>
400
                                </li>
401
                            </ol>
402
                        </fieldset>
403
                        <fieldset class="action">
404
                            <a id="confirm_key_accept_submit" data-bs-target="#confirm_key_accept" class="btn btn-primary" data-bs-toggle="modal">Submit</a>
405
                            <a class="cancel" href="/cgi-bin/koha/admin/sftp_servers.pl">Cancel</a>
406
                        </fieldset>
407
                    </form>
408
                [% END %]
409
410
                [% IF op == 'list' %]
411
                    <div id="toolbar" class="btn-toolbar">
412
                        <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>
413
                    </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
414
430
[% IF op == 'test_form' %]
415
                    <h1>FTP/SFTP servers</h1>
431
416
432
    <h1>[% tx("Test FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %]</h1>
417
                    [% IF servers_count < 1 %]
433
    [% IF sftp_server.id %]
418
                        <div class="alert alert-info" id="dno_servers_message">
434
    <div class="page-section">
419
                            <p>
435
        [% IF sftp_server.transport == 'sftp' %]
420
                                <em>There are no FTP/SFTP servers defined.</em><br />
436
        <div class="alert alert-warning">
421
                                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.
422
                            </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>
423
                        </div>
554
                        <div class="modal-body">
424
                    [% ELSE %]
555
                            <div id="delete_confirm_dialog"></div>
425
                        <div class="page-section">
426
                            <table id="sftp_servers">
427
                                <thead>
428
                                    <tr>
429
                                        <th>Name</th>
430
                                        <th>Host</th>
431
                                        <th>Port</th>
432
                                        <th>Transport</th>
433
                                        <th>Authentication mode</th>
434
                                        <th>Username</th>
435
                                        <th>Download directory</th>
436
                                        <th>Upload directory</th>
437
                                        <th>Status</th>
438
                                        <th>Debug</th>
439
                                        <th data-class-name="actions noExport">Actions</th>
440
                                    </tr>
441
                                </thead>
442
                            </table>
556
                        </div>
443
                        </div>
557
                        <div class="modal-footer">
444
                        <!-- /.page-section -->
558
                            <button type="button" class="btn btn-danger" id="delete_confirm_modal_button" data-bs-toggle="modal">Delete</button>
445
                    [% END %]
559
                            <button type="button" class="btn btn-default" data-bs-dismiss="modal">Close</button>
446
                [% END %]
447
448
                <div id="delete_confirm_modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="delete_confirm_modal_label" aria-hidden="true">
449
                    <div class="modal-dialog">
450
                        <div class="modal-content">
451
                            <div class="modal-header">
452
                                <h1 class="modal-title" id="delete_confirm_modal_label">Delete server</h1>
453
                                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
454
                            </div>
455
                            <div class="modal-body">
456
                                <div id="delete_confirm_dialog"></div>
457
                            </div>
458
                            <div class="modal-footer">
459
                                <button type="button" class="btn btn-danger" id="delete_confirm_modal_button" data-bs-toggle="modal">Delete</button>
460
                                <button type="button" class="btn btn-default" data-bs-dismiss="modal">Close</button>
461
                            </div>
560
                        </div>
462
                        </div>
561
                    </div> <!-- /.modal-content -->
463
                        <!-- /.modal-content -->
562
                </div> <!-- /.modal-dialog -->
464
                    </div>
563
            </div> <!-- #delete_confirm_modal -->
465
                    <!-- /.modal-dialog -->
564
466
                </div>
467
                <!-- #delete_confirm_modal -->
565
            </main>
468
            </main>
566
        </div> <!-- /.col-md-10.order-md-2 -->
469
        </div>
470
        <!-- /.col-md-10.order-md-2 -->
567
471
568
        <div class="col-md-2 order-sm-2 order-md-1">
472
        <div class="col-md-2 order-sm-2 order-md-1">
569
            <aside>
473
            <aside> [% INCLUDE 'admin-menu.inc' %] </aside>
570
                [% INCLUDE 'admin-menu.inc' %]
474
        </div>
571
            </aside>
475
        <!-- /.col-md-2.order-md-1 -->
572
        </div> <!-- /.col-md-2.order-md-1 -->
476
    </div>
573
     </div> <!-- /.row -->
477
    <!-- /.row -->
574
478
575
479
    [% MACRO jsinclude BLOCK %]
576
[% MACRO jsinclude BLOCK %]
480
        [% Asset.js("js/admin-menu.js") | $raw %]
577
    [% Asset.js("js/admin-menu.js") | $raw %]
481
        [% Asset.js("js/transport_status.js") | $raw %]
578
    [% INCLUDE 'datatables.inc' %]
482
        [% INCLUDE 'datatables.inc' %]
579
    <script>
483
        <script>
580
        $(document).ready(function() {
484
            $(document).ready(function() {
581
485
582
            var sftp_servers_url = '/api/v1/config/sftp_servers';
486
                var sftp_servers_url = '/api/v1/config/sftp_servers';
583
            window.sftp_servers = $("#sftp_servers").kohaTable({
487
                window.sftp_servers = $("#sftp_servers").kohaTable({
584
                "ajax": {
488
                    "ajax": {
585
                    "url": sftp_servers_url
489
                        "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
                    },
490
                    },
619
                    {
491
                    "language": {
620
                        "data": "transport",
492
                        "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
                    },
493
                    },
627
                    {
494
                    "columnDefs": [ {
628
                        "data": "auth_mode",
495
                        "targets": [0,1],
629
                        "render": function(data, type, row, meta) {
496
                        "render": function(data, type, row, meta) {
630
                            if(data == "password") {
497
                            if (type == "display") {
631
                                return _("Password-based");
498
                                if(data != null) {
632
                            } else if(data == "key_file") {
499
                                    return data.escapeHtml();
633
                                return _("Key file-based");
500
                                } else {
634
                            } else {
501
                                    return "Default";
635
                                return _("No authentication");
502
                                }
636
                            }
503
                            }
504
                            return data;
505
                        }
506
                    } ],
507
                    "columns": [
508
                        {
509
                            "data": "name",
510
                            "searchable": true,
511
                            "orderable": true
637
                        },
512
                        },
638
                        "searchable": false,
513
                        {
639
                        "orderable": false
514
                            "data": "host",
640
                    },
515
                            "searchable": true,
641
                    {
516
                            "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
                        },
517
                        },
655
                        "searchable": false,
518
                        {
656
                        "orderable": false
519
                            "data": "port",
657
                    },
520
                            "searchable": true,
658
                    {
521
                            "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
                        },
522
                        },
667
                        "searchable": false,
523
                        {
668
                        "orderable": false
524
                            "data": "transport",
669
                    },
525
                            "render": function(data, type, row, meta) {
670
                    {
526
                                return data.toUpperCase();
671
                        "data": "status",
527
                            },
672
                        "render": function(data, type, row, meta) {
528
                            "searchable": true,
673
                            if (data == "tests_ok") {
529
                            "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
                        },
530
                        },
681
                        "searchable": false,
531
                        {
682
                        "orderable": false
532
                            "data": "auth_mode",
683
                    },
533
                            "render": function(data, type, row, meta) {
684
                    {
534
                                if(data == "password") {
685
                        "data": "debug",
535
                                    return _("Password-based");
686
                        "render": function(data, type, row, meta) {
536
                                } else if(data == "key_file") {
687
                            if(data == true) {
537
                                    return _("Key file-based");
688
                                return "[% tp("Active", "On") | html %]";
538
                                } else {
689
                            }
539
                                    return _("No authentication");
690
                            else {
540
                                }
691
                                return _("Off");
541
                            },
692
                            }
542
                            "searchable": false,
543
                            "orderable": false
693
                        },
544
                        },
694
                        "searchable": false,
545
                        {
695
                        "orderable": false
546
                            "data": "user_name",
696
                    },
547
                            "searchable": false,
697
                    {
548
                            "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
                        },
549
                        },
704
                        "searchable": false,
550
                        {
705
                        "orderable": false
551
                            "data": "download_directory",
706
                    }
552
                            "render": function(data, type, row, meta) {
707
                ],
553
                                if(data) {
708
                createdRow: function(row, data, dataIndex) {
554
                                    return data;
709
                    if(data.is_default) {
555
                                } else {
710
                        $(row).addClass('default warn');
556
                                    return "<em>" + _("Not specified") + "</em>";
711
                    }
557
                                }
712
                    if(data.debug) {
558
                            },
713
                        $(row).addClass('debug');
559
                            "searchable": false,
714
                    }
560
                            "orderable": false
715
                },
561
                        },
716
            });
562
                        {
717
563
                            "data": "upload_directory",
718
            $('#sftp_servers').on("click", '.delete_server', function() {
564
                            "render": function(data, type, row, meta) {
719
                var sftp_server_id   = $(this).data('sftp-server-id');
565
                                if(data) {
720
                var sftp_server_name = decodeURIComponent($(this).data('sftp-server-name'));
566
                                    return data;
721
567
                                } else {
722
                $("#delete_confirm_dialog").html(
568
                                    return "<em>" + _("Not specified") + "</em>";
723
                    _("You are about to delete the '%s' FTP/SFTP server.").format(sftp_server_name)
569
                                }
724
                );
570
                            },
725
                $("#delete_confirm_modal_button").data('sftp-server-id', sftp_server_id);
571
                            "searchable": false,
726
                $("#delete_confirm_modal_button").data('sftp-server-name', sftp_server_name);
572
                            "orderable": false
727
            });
573
                        },
574
                        {
575
                            "data": "status",
576
                            "render": function(data, type, row, meta) {
577
                                let render = '';
578
                                if (data) {
579
                                    if (data.status == "ok") {
580
                                        render += '<i class="text-success fa-solid fa-circle-check"></i> ';
581
                                        render += '<span class="text-success">';
582
                                        render += _("Tests passing");
583
                                        render += '</span>'
584
                                    } else if (data.status == "errors") {
585
                                        data.operations.forEach(operation => {
586
                                            render += "<div>";
587
                                            if ( operation.status == "error" ) {
588
                                                render += '<i class="text-danger fa-solid fa-circle-xmark"></i> ';
589
                                                render += '<span class="text-danger">';
590
                                                render += operationLabels[operation.code] || operation.code;
591
                                                render += ' ' + _("failed");
592
                                                render += '</span>';
593
                                            } else {
594
                                                render += '<i class="text-success fa-solid fa-circle-check"></i> ';
595
                                                render += '<span class="text-success">';
596
                                                render += operationLabels[operation.code] || operation.code;
597
                                                render += ' ' + _("ok");
598
                                                render += '</span>';
599
                                            }
600
                                            render += "</div>";
601
                                        });
602
                                    } else {
603
                                        render += "<em>" + _("Never used") + "</em>";
604
                                    }
605
                                } else {
606
                                    render += "<em>" + _("Never used") + "</em>";
607
                                }
608
                                return render;
609
                            },
610
                            "searchable": false,
611
                            "orderable": false
612
                        },
613
                        {
614
                            "data": "debug",
615
                            "render": function(data, type, row, meta) {
616
                                if(data == true) {
617
                                    return "[% tp("Active", "On") | html %]";
618
                                }
619
                                else {
620
                                    return _("Off");
621
                                }
622
                            },
623
                            "searchable": false,
624
                            "orderable": false
625
                        },
626
                        {
627
                            "data": function(row, type, val, meta) {
628
                                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";
629
                                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>';
630
                                return result;
631
                            },
632
                            "searchable": false,
633
                            "orderable": false
634
                        }
635
                    ],
636
                    createdRow: function(row, data, dataIndex) {
637
                        if(data.is_default) {
638
                            $(row).addClass('default warn');
639
                        }
640
                        if(data.debug) {
641
                            $(row).addClass('debug');
642
                        }
643
                    },
644
                });
728
645
729
            $("#delete_confirm_modal_button").on("click", function() {
646
                $('#sftp_servers').on("click", '.delete_server', function() {
647
                    var sftp_server_id   = $(this).data('sftp-server-id');
648
                    var sftp_server_name = decodeURIComponent($(this).data('sftp-server-name'));
730
649
731
                var sftp_server_id   = $(this).data('sftp-server-id');
650
                    $("#delete_confirm_dialog").html(
732
                var sftp_server_name = $(this).data('sftp-server-name');
651
                        _("You are about to delete the '%s' FTP/SFTP server.").format(sftp_server_name)
652
                    );
653
                    $("#delete_confirm_modal_button").data('sftp-server-id', sftp_server_id);
654
                    $("#delete_confirm_modal_button").data('sftp-server-name', sftp_server_name);
655
                });
733
656
734
                $.ajax({
657
                $("#delete_confirm_modal_button").on("click", function() {
735
                    method: "DELETE",
658
736
                    url: "/api/v1/config/sftp_servers/"+sftp_server_id
659
                    var sftp_server_id   = $(this).data('sftp-server-id');
737
                }).success(function() {
660
                    var sftp_server_name = $(this).data('sftp-server-name');
738
                    window.sftp_servers.api().ajax.reload(function(data) {
661
739
                        $("#sftp_action_result_dialog").hide();
662
                    $.ajax({
740
                        $("#sftp_delete_success").html(_("Server '%s' deleted successfully.").format(sftp_server_name)).show();
663
                        method: "DELETE",
664
                        url: "/api/v1/config/sftp_servers/"+sftp_server_id
665
                    }).success(function() {
666
                        window.sftp_servers.api().ajax.reload(function(data) {
667
                            $("#sftp_action_result_dialog").hide();
668
                            $("#sftp_delete_success").html(_("Server '%s' deleted successfully.").format(sftp_server_name)).show();
669
                        });
670
                    }).fail(function() {
671
                        $("#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();
672
                    }).done(function() {
673
                        $("#delete_confirm_modal").modal('hide');
741
                    });
674
                    });
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
                });
675
                });
747
            });
748
676
749
            transportChange();
750
            $("#sftp_transport").on("change", function(event) {
751
                transportChange();
677
                transportChange();
752
            });
678
                $("#sftp_transport").on("change", function(event) {
679
                    transportChange();
680
                });
753
681
754
            authModeChange();
755
            $("#sftp_auth_mode").on("change", function(event) {
756
                authModeChange();
682
                authModeChange();
757
            });
683
                $("#sftp_auth_mode").on("change", function(event) {
684
                    authModeChange();
685
                });
758
686
759
            $('#confirm_key_accept_submit').on('click', function(event) {
687
                $('#confirm_key_accept_submit').on('click', function(event) {
760
                event.preventDefault();
688
                    event.preventDefault();
761
689
762
                if ( $('#add').length > 0 ) {
690
                    if ( $('#add').length > 0 ) {
763
                    if( $('#add').valid() == true ) {
691
                        if( $('#add').valid() == true ) {
764
                        modalChange();
692
                            modalChange();
765
                        $('#confirm_key_accept').modal('show');
693
                            $('#confirm_key_accept').modal('show');
766
                    } else {
694
                        } else {
767
                        $('#confirm_key_accept').modal('hide');
695
                            $('#confirm_key_accept').modal('hide');
696
                        }
768
                    }
697
                    }
769
                }
770
698
771
                if ( $('#edit_save').length > 0 ) {
699
                    if ( $('#edit_save').length > 0 ) {
772
                    if( $('#edit_save').valid() == true ) {
700
                        if( $('#edit_save').valid() == true ) {
773
                        modalChange();
701
                            modalChange();
774
                        $('#confirm_key_accept').modal('show');
702
                            $('#confirm_key_accept').modal('show');
775
                    } else {
703
                        } else {
776
                        $('#confirm_key_accept').modal('hide');
704
                            $('#confirm_key_accept').modal('hide');
705
                        }
777
                    }
706
                    }
778
                }
779
707
780
            });
708
                });
781
709
782
            $('#confirm_key_accept .approve').on('click', function() {
710
                $('#confirm_key_accept .approve').on('click', function() {
783
                $('#confirm_key_accept .deny').click();
711
                    $('#confirm_key_accept .deny').click();
784
712
785
                if ( $('#add').length > 0 ) {
713
                    if ( $('#add').length > 0 ) {
786
                    $('#add').submit();
714
                        $('#add').submit();
787
                }
715
                    }
716
717
                    if ( $('#edit_save').length > 0 ) {
718
                        $('#edit_save').submit();
719
                    }
720
                });
788
721
789
                if ( $('#edit_save').length > 0 ) {
790
                    $('#edit_save').submit();
791
                }
792
            });
722
            });
793
723
794
        });
724
            function transportChange() {
795
725
                let sftp_transport = $("#sftp_transport");
796
        function transportChange() {
726
797
            let sftp_transport = $("#sftp_transport");
727
                if(sftp_transport.val() == "ftp") {
798
728
                    $("#sftp_host").removeAttr("disabled");
799
            if(sftp_transport.val() == "ftp") {
729
                    $("#sftp_port").removeAttr("disabled");
800
                $("#sftp_host").removeAttr("disabled");
730
                    $("#sftp_passive").removeAttr("disabled");
801
                $("#sftp_port").removeAttr("disabled");
731
                    $("#sftp_auth_mode").removeAttr("disabled");
802
                $("#sftp_passive").removeAttr("disabled");
732
                    $("#sftp_user_name").removeAttr("disabled");
803
                $("#sftp_auth_mode").removeAttr("disabled");
733
                    $("#sftp_password").removeAttr("disabled");
804
                $("#sftp_user_name").removeAttr("disabled");
734
                    $("#sftp_key_file").attr("disabled", "disabled");
805
                $("#sftp_password").removeAttr("disabled");
735
806
                $("#sftp_key_file").attr("disabled", "disabled");
736
                    $("#sftp_auth_mode option[value='password']").removeAttr("disabled");
807
737
                    $("#sftp_auth_mode option[value='key_file']").attr("disabled", "disabled");
808
                $("#sftp_auth_mode option[value='password']").removeAttr("disabled");
738
                    $("#sftp_auth_mode option[value='noauth']").removeAttr("disabled");
809
                $("#sftp_auth_mode option[value='key_file']").attr("disabled", "disabled");
739
                    if($("#sftp_auth_mode option:selected").val() == "key_file") {
810
                $("#sftp_auth_mode option[value='noauth']").removeAttr("disabled");
740
                        $("#sftp_auth_mode option[value='password']").prop("selected", true);
811
                if($("#sftp_auth_mode option:selected").val() == "key_file") {
741
                    }
812
                    $("#sftp_auth_mode option[value='password']").prop("selected", true);
813
                }
814
742
815
                let sftp_port = $("#sftp_port").val();
743
                    let sftp_port = $("#sftp_port").val();
816
                if(sftp_port == 22) $("#sftp_port").val("21");
744
                    if(sftp_port == 22) $("#sftp_port").val("21");
817
745
818
                authModeChange();
746
                    authModeChange();
819
            } else if(sftp_transport.val() == "sftp") {
747
                } else if(sftp_transport.val() == "sftp") {
820
                $("#sftp_host").removeAttr("disabled");
748
                    $("#sftp_host").removeAttr("disabled");
821
                $("#sftp_port").removeAttr("disabled");
749
                    $("#sftp_port").removeAttr("disabled");
822
                $("#sftp_passive").attr("disabled", "disabled");
750
                    $("#sftp_passive").attr("disabled", "disabled");
823
                $("#sftp_auth_mode").removeAttr("disabled");
751
                    $("#sftp_auth_mode").removeAttr("disabled");
824
                $("#sftp_user_name").removeAttr("disabled");
752
                    $("#sftp_user_name").removeAttr("disabled");
825
                $("#sftp_password").removeAttr("disabled");
753
                    $("#sftp_password").removeAttr("disabled");
826
                $("#sftp_key_file").removeAttr("disabled");
754
                    $("#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
755
926
                    if ( value.passed ) {
756
                    $("#sftp_auth_mode option[value='password']").removeAttr("disabled");
927
                        testOutput.append(
757
                    $("#sftp_auth_mode option[value='key_file']").removeAttr("disabled");
928
                            '<i class="text-success fa-solid fa-circle-check"></i> '
758
                    $("#sftp_auth_mode option[value='noauth']").removeAttr("disabled");
929
                            + title
759
                    $("#sftp_passive option[value='1']").prop("selected", true);
930
                            + '... <span class="text-success">'
760
931
                            + _("Passed")
761
                    let sftp_port = $("#sftp_port").val();
932
                            + '</span><br />'
762
                    if(sftp_port == 21) $("#sftp_port").val("22");
933
                        );
763
934
                        if( value.msg ) testOutput.append( _("Message: ") + '<code>' + value.msg + '</code><br />' );
764
                    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
                }
765
                }
947
            })
766
            }
948
            .fail(function(data) {
767
949
                if( data.status == 404 ) {
768
            function authModeChange() {
950
                    return testOutput.text( '<i class="text-success fa-solid fa-circle-check"></i> ' + _("FTP/SFTP Server not found") );
769
                let sftp_auth_mode = $("#sftp_auth_mode").val();
770
771
                if(sftp_auth_mode == "password") {
772
                    $("#sftp_password").removeAttr("disabled");
773
                    $("#sftp_key_file").attr("disabled", "disabled");
774
                } else if(sftp_auth_mode == "key_file") {
775
                    $("#sftp_password").attr("disabled", "disabled");
776
                    $("#sftp_key_file").removeAttr("disabled");
951
                } else {
777
                } else {
952
                    return testOutput.text( '<i class="text-success fa-solid fa-circle-check"></i> ' + _("Internal Server Error. Please check the server logs") );
778
                    $("#sftp_password").attr("disabled", "disabled");
779
                    $("#sftp_key_file").attr("disabled", "disabled");
953
                }
780
                }
954
            })
781
            }
955
            .always(function(data) {
782
956
                runTests.removeClass('disabled');
783
            function modalChange() {
957
            });
784
                $('#modal_message').hide();
958
        }
785
                if ( $('#sftp_transport').val() == 'sftp' ) $('#modal_message').show();
959
    </script>
786
787
                $('#modal_host').text( $('#sftp_host').val() );
788
                $('#modal_port').text( $('#sftp_port').val() );
789
                $('#modal_transport').text( $('#sftp_transport option:selected').text() );
790
                $('#modal_user_name').text( $('#sftp_user_name').val() );
791
                $('#modal_auth_mode').text( $('#sftp_auth_mode option:selected').text() );
792
            }
793
        </script>
960
    [% END %]
794
    [% END %]
961
[% END %]
962
795
963
[% INCLUDE 'intranet-bottom.inc' %]
796
    [% INCLUDE 'intranet-bottom.inc' %]
797
</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