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

(-)a/Koha/File/Transport.pm (-21 / +218 lines)
Lines 37-43 Koha::File::Transport - Base class for file transport handling Link Here
37
37
38
=head1 DESCRIPTION
38
=head1 DESCRIPTION
39
39
40
Base class providing common functionality for FTP/SFTP file transport.
40
Base class providing common functionality for FTP/SFTP/Local file transport.
41
42
This class provides both a traditional explicit API and a simplified auto-managing API.
43
44
Traditional API requires manual connection management:
45
    $transport->connect();
46
    $transport->change_directory($path);
47
    $transport->upload_file($local, $remote);
48
    $transport->disconnect();
49
50
Simplified API handles connections automatically:
51
    $transport->upload_file($local, $remote, { path => '/custom/path' });
52
    # OR using default upload directory:
53
    $transport->upload_file($local, $remote);
41
54
42
=cut
55
=cut
43
56
Lines 92-97 sub store { Link Here
92
    return $self;
105
    return $self;
93
}
106
}
94
107
108
=head3 _ensure_connected
109
110
    $transport->_ensure_connected();
111
112
Internal method that ensures a connection exists, connecting if needed.
113
Returns true if connected, false if connection failed.
114
115
=cut
116
117
sub _ensure_connected {
118
    my ($self) = @_;
119
120
    # Check if already connected (transport-specific)
121
    return 1 if $self->_is_connected();
122
123
    # Reset directory state on new connection
124
    $self->{_user_set_directory} = 0;
125
126
    # Attempt to connect
127
    return $self->connect();
128
}
129
130
=head3 _is_connected
131
132
    my $connected = $transport->_is_connected();
133
134
Internal method to check if transport is currently connected.
135
Must be implemented by subclasses.
136
137
=cut
138
139
sub _is_connected {
140
    my ($self) = @_;
141
    die "Subclass must implement _is_connected";
142
}
143
144
=head3 _auto_change_directory
145
146
    $transport->_auto_change_directory($dir_type, $custom_path);
147
148
Internal method that automatically changes to the appropriate directory.
149
$dir_type is 'upload' or 'download', $custom_path is optional override.
150
151
=cut
152
153
sub _auto_change_directory {
154
    my ( $self, $dir_type, $custom_path ) = @_;
155
156
    my $target_dir;
157
    if ($custom_path) {
158
        $target_dir = $custom_path;
159
    } elsif ( $dir_type eq 'upload' ) {
160
        $target_dir = $self->upload_directory;
161
    } elsif ( $dir_type eq 'download' ) {
162
        $target_dir = $self->download_directory;
163
    }
164
165
    return 1 unless $target_dir;    # No directory to change to
166
167
    return $self->change_directory($target_dir);
168
}
169
95
=head3 plain_text_password
170
=head3 plain_text_password
96
171
97
    my $password = $server->plain_text_password;
172
    my $password = $server->plain_text_password;
Lines 201-215 sub connect { Link Here
201
276
202
=head3 upload_file
277
=head3 upload_file
203
278
204
    $transport->upload_file($file);
279
    # Traditional explicit API:
280
    $transport->upload_file($local_file, $remote_file);
281
282
    # Simplified auto-managing API:
283
    $transport->upload_file($local_file, $remote_file, { path => '/custom/path' });
205
284
206
Method for uploading a file to the current file server
285
Method for uploading a file to the current file server.
286
287
When called with options hashref, automatically handles connection and directory management.
288
When called without options, uses traditional explicit workflow.
207
289
208
=cut
290
=cut
209
291
210
sub upload_file {
292
sub upload_file {
211
    my ( $self, $local_file, $remote_file ) = @_;
293
    my ( $self, $local_file, $remote_file, $options ) = @_;
212
    die "Subclass must implement upload_file";
294
295
    return unless $self->_ensure_connected();
296
297
    # Only auto-change directory if:
298
    # 1. Options provided with custom path (simplified API), OR
299
    # 2. No explicit directory set by user AND default upload_directory exists (traditional API)
300
    if ( $options && $options->{path} ) {
301
302
        # Simplified API - use custom path
303
        return unless $self->_auto_change_directory( 'upload', $options->{path} );
304
    } elsif ( !$self->{_user_set_directory} ) {
305
306
        # Traditional API - use default directory only if user hasn't set one
307
        return unless $self->_auto_change_directory( 'upload', undef );
308
    }
309
310
    return $self->_upload_file( $local_file, $remote_file );
311
}
312
313
=head3 _upload_file
314
315
    $transport->_upload_file($local_file, $remote_file);
316
317
Internal method that performs the protocol-specific upload operation.
318
Must be implemented by subclasses. Called by upload_file after connection
319
and directory management.
320
321
=cut
322
323
sub _upload_file {
324
    my ($self) = @_;
325
    die "Subclass must implement _upload_file";
213
}
326
}
214
327
215
=head3 download_file
328
=head3 download_file
Lines 221-241 Method for downloading a file from the current file server Link Here
221
=cut
334
=cut
222
335
223
sub download_file {
336
sub download_file {
224
    my ( $self, $remote_file, $local_file ) = @_;
337
    my ( $self, $remote_file, $local_file, $options ) = @_;
225
    die "Subclass must implement download_file";
338
339
    return unless $self->_ensure_connected();
340
341
    # Only auto-change directory if:
342
    # 1. Options provided with custom path (simplified API), OR
343
    # 2. No explicit directory set by user AND default download_directory exists (traditional API)
344
    if ( $options && $options->{path} ) {
345
346
        # Simplified API - use custom path
347
        return unless $self->_auto_change_directory( 'download', $options->{path} );
348
    } elsif ( !$self->{_user_set_directory} ) {
349
350
        # Traditional API - use default directory only if user hasn't set one
351
        return unless $self->_auto_change_directory( 'download', undef );
352
    }
353
354
    return $self->_download_file( $remote_file, $local_file );
226
}
355
}
227
356
228
=head3 change_directory
357
=head3 _download_file
229
358
230
    my $files = $transport->change_directory($path);
359
    $transport->_download_file($remote_file, $local_file);
231
360
232
Method for changing the current directory on the connected file server
361
Internal method that performs the protocol-specific download operation.
362
Must be implemented by subclasses. Called by download_file after connection
363
and directory management.
233
364
234
=cut
365
=cut
235
366
236
sub change_directory {
367
sub _download_file {
237
    my ( $self, $path ) = @_;
368
    my ($self) = @_;
238
    die "Subclass must implement change_directory";
369
    die "Subclass must implement _download_file";
370
}
371
372
=head3 rename_file
373
374
    my $success = $transport->rename_file($old_name, $new_name);
375
376
Method for renaming a file on the current file server
377
378
=cut
379
380
sub rename_file {
381
    my ( $self, $old_name, $new_name ) = @_;
382
383
    return unless $self->_ensure_connected();
384
385
    return $self->_rename_file( $old_name, $new_name );
386
}
387
388
=head3 _rename_file
389
390
    $transport->_rename_file($old_name, $new_name);
391
392
Internal method that performs the protocol-specific file rename operation.
393
Must be implemented by subclasses. Called by rename_file after connection
394
verification.
395
396
=cut
397
398
sub _rename_file {
399
    my ($self) = @_;
400
    die "Subclass must implement _rename_file";
239
}
401
}
240
402
241
=head3 list_files
403
=head3 list_files
Lines 247-267 Method for listing files in the current directory of the connected file server Link Here
247
=cut
409
=cut
248
410
249
sub list_files {
411
sub list_files {
250
    my ( $self, $path ) = @_;
412
    my ( $self, $options ) = @_;
251
    die "Subclass must implement list_files";
413
414
    return unless $self->_ensure_connected();
415
416
    # Only auto-change directory if:
417
    # 1. Options provided with custom path (simplified API), OR
418
    # 2. No explicit directory set by user AND default download_directory exists (traditional API)
419
    if ( $options && $options->{path} ) {
420
421
        # Simplified API - use custom path
422
        return unless $self->_auto_change_directory( 'download', $options->{path} );
423
    } elsif ( !$self->{_user_set_directory} ) {
424
425
        # Traditional API - use default directory only if user hasn't set one
426
        return unless $self->_auto_change_directory( 'download', undef );
427
    }
428
429
    return $self->_list_files();
252
}
430
}
253
431
254
=head3 rename_file
432
=head3 _list_files
255
433
256
    my $success = $transport->rename_file($old_name, $new_name);
434
    my $files = $transport->_list_files();
257
435
258
Method for renaming a file on the current file server
436
Internal method that performs the protocol-specific file listing operation.
437
Must be implemented by subclasses. Called by list_files after connection
438
and directory management.
259
439
260
=cut
440
=cut
261
441
262
sub rename_file {
442
sub _list_files {
263
    my ( $self, $old_name, $new_name ) = @_;
443
    my ($self) = @_;
264
    die "Subclass must implement rename_file";
444
    die "Subclass must implement _list_files";
265
}
445
}
266
446
267
=head3 disconnect
447
=head3 disconnect
Lines 272-277 Method for disconnecting from the current file server Link Here
272
452
273
=cut
453
=cut
274
454
455
=head3 change_directory
456
457
    my $files = $transport->change_directory($path);
458
459
Method for changing the current directory on the connected file server
460
461
=cut
462
463
sub change_directory {
464
    my ( $self, $path ) = @_;
465
466
    # Mark that user has explicitly set a directory
467
    $self->{_user_set_directory} = 1;
468
469
    die "Subclass must implement change_directory";
470
}
471
275
sub disconnect {
472
sub disconnect {
276
    my ($self) = @_;
473
    my ($self) = @_;
277
    die "Subclass must implement disconnect";
474
    die "Subclass must implement disconnect";
(-)a/Koha/File/Transport/FTP.pm (-20 / +25 lines)
Lines 64-80 sub connect { Link Here
64
    return 1;
64
    return 1;
65
}
65
}
66
66
67
=head3 upload_file
67
=head3 _upload_file
68
68
69
    my $success =  $transport->upload_file($fh);
69
Internal method that performs the FTP-specific upload operation.
70
71
Passed a filehandle, this will upload the file to the current directory of the server connection.
72
70
73
Returns true on success or undefined on failure.
71
Returns true on success or undefined on failure.
74
72
75
=cut
73
=cut
76
74
77
sub upload_file {
75
sub _upload_file {
78
    my ( $self, $local_file, $remote_file ) = @_;
76
    my ( $self, $local_file, $remote_file ) = @_;
79
    my $operation = "upload";
77
    my $operation = "upload";
80
78
Lines 95-111 sub upload_file { Link Here
95
    return 1;
93
    return 1;
96
}
94
}
97
95
98
=head3 download_file
96
=head3 _download_file
99
100
    my $success =  $transport->download_file($filename);
101
97
102
Passed a filename, this will download the file from the current directory of the server connection.
98
Internal method that performs the FTP-specific download operation.
103
99
104
Returns true on success or undefined on failure.
100
Returns true on success or undefined on failure.
105
101
106
=cut
102
=cut
107
103
108
sub download_file {
104
sub _download_file {
109
    my ( $self, $remote_file, $local_file ) = @_;
105
    my ( $self, $remote_file, $local_file ) = @_;
110
    my $operation = "download";
106
    my $operation = "download";
111
107
Lines 156-171 sub change_directory { Link Here
156
    return 1;
152
    return 1;
157
}
153
}
158
154
159
=head3 list_files
155
=head3 _list_files
160
161
    my $files = $server->list_files;
162
156
163
Returns an array reference of hashrefs with file information found in the current directory of the server connection.
157
Internal method that performs the FTP-specific file listing operation.
158
Returns an array reference of hashrefs with file information.
164
Each hashref contains: filename, longname, size, perms.
159
Each hashref contains: filename, longname, size, perms.
165
160
166
=cut
161
=cut
167
162
168
sub list_files {
163
sub _list_files {
169
    my ($self) = @_;
164
    my ($self) = @_;
170
    my $operation = "list";
165
    my $operation = "list";
171
166
Lines 207-223 sub list_files { Link Here
207
    return \@file_list;
202
    return \@file_list;
208
}
203
}
209
204
210
=head3 rename_file
205
=head3 _rename_file
211
206
212
    my $success = $server->rename_file($old_name, $new_name);
207
Internal method that performs the FTP-specific file rename operation.
213
214
Renames a file on the server connection.
215
208
216
Returns true on success or undefined on failure.
209
Returns true on success or undefined on failure.
217
210
218
=cut
211
=cut
219
212
220
sub rename_file {
213
sub _rename_file {
221
    my ( $self, $old_name, $new_name ) = @_;
214
    my ( $self, $old_name, $new_name ) = @_;
222
    my $operation = "rename";
215
    my $operation = "rename";
223
216
Lines 253-258 sub disconnect { Link Here
253
    return 1;
246
    return 1;
254
}
247
}
255
248
249
=head3 _is_connected
250
251
Internal method to check if transport is currently connected.
252
253
=cut
254
255
sub _is_connected {
256
    my ($self) = @_;
257
258
    return $self->{connection} && $self->{connection}->pwd();
259
}
260
256
sub _abort_operation {
261
sub _abort_operation {
257
    my ( $self, $operation ) = @_;
262
    my ( $self, $operation ) = @_;
258
263
(-)a/Koha/File/Transport/Local.pm (-20 / +26 lines)
Lines 117-133 sub connect { Link Here
117
    return 1;
117
    return 1;
118
}
118
}
119
119
120
=head3 upload_file
120
=head3 _upload_file
121
121
122
    my $success =  $transport->upload_file($local_file, $remote_file);
122
Internal method that performs the local file system upload operation.
123
124
Copies a local file to the upload directory.
125
123
126
Returns true on success or undefined on failure.
124
Returns true on success or undefined on failure.
127
125
128
=cut
126
=cut
129
127
130
sub upload_file {
128
sub _upload_file {
131
    my ( $self, $local_file, $remote_file ) = @_;
129
    my ( $self, $local_file, $remote_file ) = @_;
132
    my $operation = "upload";
130
    my $operation = "upload";
133
131
Lines 159-175 sub upload_file { Link Here
159
    return 1;
157
    return 1;
160
}
158
}
161
159
162
=head3 download_file
160
=head3 _download_file
163
164
    my $success =  $transport->download_file($remote_file, $local_file);
165
161
166
Copies a file from the download directory to a local file.
162
Internal method that performs the local file system download operation.
167
163
168
Returns true on success or undefined on failure.
164
Returns true on success or undefined on failure.
169
165
170
=cut
166
=cut
171
167
172
sub download_file {
168
sub _download_file {
173
    my ( $self, $remote_file, $local_file ) = @_;
169
    my ( $self, $remote_file, $local_file ) = @_;
174
    my $operation = 'download';
170
    my $operation = 'download';
175
171
Lines 257-272 sub change_directory { Link Here
257
    return 1;
253
    return 1;
258
}
254
}
259
255
260
=head3 list_files
256
=head3 _list_files
261
262
    my $files = $server->list_files;
263
257
264
Returns an array reference of hashrefs with file information found in the current directory.
258
Internal method that performs the local file system file listing operation.
259
Returns an array reference of hashrefs with file information.
265
Each hashref contains: filename, longname, size, perms, mtime.
260
Each hashref contains: filename, longname, size, perms, mtime.
266
261
267
=cut
262
=cut
268
263
269
sub list_files {
264
sub _list_files {
270
    my ($self) = @_;
265
    my ($self) = @_;
271
    my $operation = "list";
266
    my $operation = "list";
272
267
Lines 340-356 sub list_files { Link Here
340
    return \@files;
335
    return \@files;
341
}
336
}
342
337
343
=head3 rename_file
338
=head3 _rename_file
344
339
345
    my $success = $server->rename_file($old_name, $new_name);
340
Internal method that performs the local file system file rename operation.
346
347
Renames a file in the current directory.
348
341
349
Returns true on success or undefined on failure.
342
Returns true on success or undefined on failure.
350
343
351
=cut
344
=cut
352
345
353
sub rename_file {
346
sub _rename_file {
354
    my ( $self, $old_name, $new_name ) = @_;
347
    my ( $self, $old_name, $new_name ) = @_;
355
    my $operation = "rename";
348
    my $operation = "rename";
356
349
Lines 397-402 sub rename_file { Link Here
397
    return 1;
390
    return 1;
398
}
391
}
399
392
393
=head3 _is_connected
394
395
Internal method to check if transport is currently connected.
396
For local transport, always returns true as local filesystem is always accessible.
397
398
=cut
399
400
sub _is_connected {
401
    my ($self) = @_;
402
403
    return 1;    # Local filesystem is always "connected"
404
}
405
400
=head3 disconnect
406
=head3 disconnect
401
407
402
    $server->disconnect();
408
    $server->disconnect();
(-)a/Koha/File/Transport/SFTP.pm (-21 / +25 lines)
Lines 76-92 sub connect { Link Here
76
    return 1;
76
    return 1;
77
}
77
}
78
78
79
=head3 upload_file
79
=head3 _upload_file
80
80
81
    my $success =  $transport->upload_file($fh);
81
Internal method that performs the SFTP-specific upload operation.
82
83
Passed a filehandle, this will upload the file to the current directory of the server connection.
84
82
85
Returns true on success or undefined on failure.
83
Returns true on success or undefined on failure.
86
84
87
=cut
85
=cut
88
86
89
sub upload_file {
87
sub _upload_file {
90
    my ( $self, $local_file, $remote_file ) = @_;
88
    my ( $self, $local_file, $remote_file ) = @_;
91
    my $operation = "upload";
89
    my $operation = "upload";
92
90
Lines 107-123 sub upload_file { Link Here
107
    return 1;
105
    return 1;
108
}
106
}
109
107
110
=head3 download_file
108
=head3 _download_file
111
112
    my $success =  $transport->download_file($filename);
113
109
114
Passed a filename, this will download the file from the current directory of the server connection.
110
Internal method that performs the SFTP-specific download operation.
115
111
116
Returns true on success or undefined on failure.
112
Returns true on success or undefined on failure.
117
113
118
=cut
114
=cut
119
115
120
sub download_file {
116
sub _download_file {
121
    my ( $self, $remote_file, $local_file ) = @_;
117
    my ( $self, $remote_file, $local_file ) = @_;
122
    my $operation = 'download';
118
    my $operation = 'download';
123
119
Lines 169-184 sub change_directory { Link Here
169
    return 1;
165
    return 1;
170
}
166
}
171
167
172
=head3 list_files
168
=head3 _list_files
173
174
    my $files = $server->list_files;
175
169
176
Returns an array reference of hashrefs with file information found in the current directory of the server connection.
170
Internal method that performs the SFTP-specific file listing operation.
171
Returns an array reference of hashrefs with file information.
177
Each hashref contains: filename, longname, a (attributes).
172
Each hashref contains: filename, longname, a (attributes).
178
173
179
=cut
174
=cut
180
175
181
sub list_files {
176
sub _list_files {
182
    my ($self) = @_;
177
    my ($self) = @_;
183
    my $operation = "list";
178
    my $operation = "list";
184
179
Lines 199-215 sub list_files { Link Here
199
    return $file_list;
194
    return $file_list;
200
}
195
}
201
196
202
=head3 rename_file
197
=head3 _rename_file
203
198
204
    my $success = $server->rename_file($old_name, $new_name);
199
Internal method that performs the SFTP-specific file rename operation.
205
206
Renames a file on the server connection.
207
200
208
Returns true on success or undefined on failure.
201
Returns true on success or undefined on failure.
209
202
210
=cut
203
=cut
211
204
212
sub rename_file {
205
sub _rename_file {
213
    my ( $self, $old_name, $new_name ) = @_;
206
    my ( $self, $old_name, $new_name ) = @_;
214
    my $operation = "rename";
207
    my $operation = "rename";
215
208
Lines 232-237 sub rename_file { Link Here
232
    return 1;
225
    return 1;
233
}
226
}
234
227
228
=head3 _is_connected
229
230
Internal method to check if transport is currently connected.
231
232
=cut
233
234
sub _is_connected {
235
    my ($self) = @_;
236
237
    return $self->{connection} && !$self->{connection}->error;
238
}
239
235
=head3 disconnect
240
=head3 disconnect
236
241
237
    $server->disconnect();
242
    $server->disconnect();
238
- 

Return to bug 40811