Koha::File::Transport - Base class for file transport handling
use Koha::File::Transports;
# Create/retrieve a transport (polymorphic - returns SFTP/FTP/Local subclass)
my $transport = Koha::File::Transports->find($id);
# SIMPLIFIED API (recommended) - connection and directory management are automatic
# Connections are established on-demand and directories are managed automatically
# Example 1: Upload to custom path (connection happens automatically)
$transport->upload_file('/local/file.txt', 'remote.txt', { path => '/custom/dir/' });
# Example 2: Download using configured download_directory (auto-managed)
$transport->download_file('remote.txt', '/local/file.txt');
# Example 3: List files in custom directory (one-shot operation)
my $files = $transport->list_files({ path => '/some/directory/' });
# TRADITIONAL API - manual connection and directory management
# Useful when you need fine-grained control or want to perform multiple
# operations in the same directory without repeating the path
$transport->connect(); # Optional - will auto-connect if omitted
$transport->change_directory('/work/dir/'); # Sets working directory
$transport->upload_file('/local/1.txt', '1.txt');
$transport->upload_file('/local/2.txt', '2.txt'); # Uses same directory
my $files = $transport->list_files(); # Lists /work/dir/
$transport->rename_file('1.txt', '1_old.txt');
$transport->download_file('2.txt', '/local/2.txt');
$transport->disconnect(); # Optional - cleaned up automatically
# HYBRID APPROACH - mixing both APIs
# Once you explicitly set a directory, auto-management is disabled
$transport->change_directory('/work/dir/'); # Explicit directory change
$transport->upload_file('/local/file.txt', 'file.txt'); # Uses /work/dir/
$transport->list_files(); # Still uses /work/dir/
# The configured upload_directory/download_directory won't be used anymore
Base class providing common functionality for FTP/SFTP/Local file transport.
This class supports two distinct usage patterns:
The simplified API automatically manages connections and directories:
You never need to call connect() or disconnect(). Connections are established on-demand when you call file operation methods and are automatically cleaned up when the object is destroyed.
Each file operation (upload_file, download_file, list_files) accepts an optional options hashref with a 'path' key to specify a custom directory for that operation:
$transport->upload_file($local, $remote, { path => '/custom/dir/' });
If no path is provided, the transport uses its configured upload_directory (for uploads) or download_directory (for downloads/listings).
Each operation is independent. The transport doesn't remember which directory you used in previous operations, making the API stateless and safe for concurrent usage patterns.
The traditional API provides manual control over connection and directory state:
While connect() and disconnect() are still available and can be called explicitly, they are entirely optional. The simplified API manages connections automatically.
Once you call change_directory() explicitly, the transport switches to "manual mode" and remembers your working directory. Subsequent operations will use this directory and automatic directory management is disabled:
$transport->change_directory('/work/');
$transport->upload_file($local, $remote); # Uses /work/, not upload_directory
$transport->list_files(); # Lists /work/, not download_directory
This is useful when you need to perform multiple operations in the same directory without repeating the path parameter each time.
The implementation uses a _user_set_directory flag to track which mode is active:
This design allows you to mix both approaches in the same codebase, choosing the right pattern for each use case.
$server->store;
Overloaded store method that ensures directory paths end with a forward slash.
$transport->_ensure_connected();
Internal method that ensures a connection exists, connecting if needed. Returns true if connected, false if connection failed.
my $connected = $transport->_is_connected();
Internal method to check if transport is currently connected. Must be implemented by subclasses.
$transport->_auto_change_directory($dir_type, $custom_path);
Internal method that automatically changes to the appropriate directory. $dir_type is 'upload' or 'download', $custom_path is optional override.
my $password = $server->plain_text_password;
Returns the decrypted plaintext password.
my $key = $server->plain_text_key;
Returns the decrypted plaintext key file.
my $json = $transport->to_api;
Returns a JSON representation of the object suitable for API output, excluding sensitive data.
This method returns the mapping for representing a Koha::File::Transport object on the API.
$transport->test_connection
Method to test the connection for the configuration of the current file server
Interface methods that must be implemented by subclasses
my $success = $transport->connect();
Establishes a connection to the remote server.
Note: Calling this method is entirely optional. All file operations (upload_file, download_file, list_files, etc.) automatically establish a connection if one doesn't already exist. You only need to call this explicitly if you want to verify connectivity before performing operations.
Returns: True on success, undef on failure. Check object_messages() for details.
my $success = $transport->_connect();
Internal method that performs the protocol-specific connection operation. Must be implemented by subclasses. Called by connect() after resetting directory state.
# Signature:
my $success = $transport->upload_file($local_file, $remote_file, \%options);
Uploads a file to the remote server. Automatically establishes a connection if needed.
Parameters:
$local_file - Path to local file to upload (required)$remote_file - Remote filename (not a path, just the filename) (required)\%options - Optional hashref with keys:
path - Directory path to upload to. If provided, uses this directory for this operation only (simplified API). If omitted, behavior depends on whether change_directory() has been called explicitly (see DESCRIPTION).Usage Patterns:
# Pattern 1: Simplified API with custom path
$transport->upload_file('/tmp/data.csv', 'data.csv', { path => '/uploads/' });
# Pattern 2: Simplified API with configured upload_directory
$transport->upload_file('/tmp/data.csv', 'data.csv');
# Pattern 3: Traditional API with explicit directory
$transport->change_directory('/uploads/');
$transport->upload_file('/tmp/data.csv', 'data.csv');
Returns: True on success, undef on failure. Check object_messages() for details.
$transport->_upload_file($local_file, $remote_file);
Internal method that performs the protocol-specific upload operation. Must be implemented by subclasses. Called by upload_file after connection and directory management.
# Signature:
my $success = $transport->download_file($remote_file, $local_file, \%options);
Downloads a file from the remote server. Automatically establishes a connection if needed.
Parameters:
$remote_file - Remote filename (not a path, just the filename) (required)$local_file - Path where the downloaded file should be saved (required)\%options - Optional hashref with keys:
path - Directory path to download from. If provided, uses this directory for this operation only (simplified API). If omitted, behavior depends on whether change_directory() has been called explicitly (see DESCRIPTION).Usage Patterns:
# Pattern 1: Simplified API with custom path
$transport->download_file('data.csv', '/tmp/data.csv', { path => '/downloads/' });
# Pattern 2: Simplified API with configured download_directory
$transport->download_file('data.csv', '/tmp/data.csv');
# Pattern 3: Traditional API with explicit directory
$transport->change_directory('/downloads/');
$transport->download_file('data.csv', '/tmp/data.csv');
Returns: True on success, undef on failure. Check object_messages() for details.
$transport->_download_file($remote_file, $local_file);
Internal method that performs the protocol-specific download operation. Must be implemented by subclasses. Called by download_file after connection and directory management.
my $success = $transport->rename_file($old_name, $new_name);
Method for renaming a file on the current file server
$transport->_rename_file($old_name, $new_name);
Internal method that performs the protocol-specific file rename operation. Must be implemented by subclasses. Called by rename_file after connection verification.
# Signature:
my $files = $transport->list_files(\%options);
Lists files in a directory on the remote server. Automatically establishes a connection if needed.
Parameters:
\%options - Optional hashref with keys:
path - Directory path to list files from. If provided, uses this directory for this operation only (simplified API). If omitted, behavior depends on whether change_directory() has been called explicitly (see DESCRIPTION).Usage Patterns:
# Pattern 1: Simplified API with custom path
my $files = $transport->list_files({ path => '/incoming/' });
# Pattern 2: Simplified API with configured download_directory
my $files = $transport->list_files();
# Pattern 3: Traditional API with explicit directory
$transport->change_directory('/incoming/');
my $files = $transport->list_files();
Returns: Arrayref of hashrefs on success, undef on failure. Each hashref contains file metadata (filename, size, permissions, etc.). The exact structure varies by transport type but always includes a 'filename' key.
my $files = $transport->_list_files();
Internal method that performs the protocol-specific file listing operation. Must be implemented by subclasses. Called by list_files after connection and directory management.
$transport->disconnect();
Closes the connection to the remote server.
Note: Calling this method is entirely optional. Connections are automatically cleaned up when the transport object is destroyed (goes out of scope). You only need to call this explicitly if you want to free resources before the object is destroyed, such as in long-running processes.
Returns: True on success, undef on failure.
$transport->_disconnect();
Internal method that performs the protocol-specific disconnection operation. Must be implemented by subclasses. Called by disconnect() after resetting directory state.
my $success = $transport->change_directory($path);
Changes the current working directory on the remote server.
Important: Calling this method explicitly switches the transport to "manual mode" and disables automatic directory management. After calling this method, all subsequent file operations will use this directory (or relative paths from it) until you call change_directory() again or create a new connection.
Parameters:
$path - Directory path to change to (required)Example:
# After calling change_directory explicitly:
$transport->change_directory('/work/dir/');
$transport->upload_file($local, $remote); # Uses /work/dir/, not upload_directory
$transport->list_files(); # Lists /work/dir/, not download_directory
Returns: True on success, undef on failure. Check object_messages() for details.
my $success = $transport->_change_directory($path);
Internal method that performs the protocol-specific directory change operation. Must be implemented by subclasses. Called by change_directory() after setting the _user_set_directory flag.
$server->_post_store_trigger;
Method triggered by parent store to allow local additions to the store call
Handle encryption of sensitive data
Return a CR-free string from an input
Return type of Object relating to Schema Result