Skip to content

New step move sftp 405 #953

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: MOODLE_405_STABLE
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ Dataflows is a generic workflow and processing engine which can be configured to

| Moodle version | Branch | PHP |
|----------------|-------------------|-----------|
| Moodle 4.1-4.2 | MOODLE_401_STABLE | 7.4 |
| Moodle 4.5 | MOODLE_405_STABLE | 8.1 - 8.3 |
| Moodle 4.1-4.2 | MOODLE_401_STABLE | 7.4 |
| Totara 10+ | MOODLE_35_STABLE | 7.1 - 7.4 |

Note: Moodle 402 is supported with PHP 8.0 maximum at the moment
Expand Down
129 changes: 129 additions & 0 deletions classes/local/step/connector_sftp_delete_file.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?php
// This file is part of Moodle - https://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

namespace tool_dataflows\local\step;

use tool_dataflows\helper;

/**
* SFTP connector step type.
*
* Uses phpseclib. See https://phpseclib.com
*
* @package tool_dataflows
* @author Guillaume Barat <guillaumebarat@catalyst-au.net>
* @copyright 2025, Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class connector_sftp_delete_file extends connector_sftp {
use sftp_trait{
sftp_trait::form_define_fields as sftp_form_define_fields;
sftp_trait::form_add_custom_inputs as sftp_form_add_custom_inputs;
sftp_trait::validate_for_run as sftp_validate_for_run;
sftp_trait::validate_config as sftp_validate_config;


}

/**
* Return the definition of the fields available in this form.
*
* @param string $behaviour 'delete' or something else.
* @return array
*/
public static function form_define_fields($behaviour = 'delete'): array {
$fields = self::sftp_form_define_fields('delete');
return $fields;
}

/**
* Custom elements for editing the step.
*
* @param \MoodleQuickForm $mform
* @param string $behaviour default to the step.
*/
public function form_add_custom_inputs(\MoodleQuickForm &$mform, $behaviour = 'delete') {
$this->sftp_form_add_custom_inputs($mform, 'delete');
}

/**
* Executes the step
*
* Performs an SFTP call according to config parameters.
*
* @param mixed|null $input
* @return mixed
*/
public function execute($input = null) {
$stepvars = $this->get_variables();
$config = $stepvars->get('config');

// At this point we need to disconnect once we are finished.
try {
// Skip if it is a dry run.
if ($this->is_dry_run() && $this->has_side_effect()) {
return $input;
}

$sftp = $this->init_sftp($config);

$sourceisremote = helper::path_is_scheme($config->source, self::$sftpprefix);
$sourcepath = $this->resolve_path($config->source);

// Deleting from remote.
if ($sourceisremote) {
$this->delete_from_remote($sftp, $sourcepath);
return $input;
}
} catch (\Throwable $e) {
$this->enginestep->log->error($e->getMessage());
if (isset($sftp)) {
$sftp->disconnect();
}
throw new \moodle_exception($e->getMessage(), 'tool_dataflows');
}

return $input;
}


/**
* Perform any extra validation that is required only for runs.
*
* @param string $behaviour
* @return true|array Will return true or an array of errors.
*/
public function validate_for_run($behaviour = 'copy') {
$sftperrors = $this->sftp_validate_for_run('delete');
if ($sftperrors === true) {
$sftperrors = [];
}
return $sftperrors ?: true;
}

/**
* Validate the configuration settings.
*
* @param object $config
* @param string $behaviour
* @return true|\lang_string[] true if valid, an array of errors otherwise
*/
public function validate_config($config, $behaviour = 'delete') {
$errors = $this->sftp_validate_config($config, 'delete');
return $errors ?: true;
}

}
77 changes: 67 additions & 10 deletions classes/local/step/sftp_trait.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ public static function form_define_fields($behaviour = 'copy'): array {
'target' => ['type' => PARAM_TEXT, 'required' => true],
]);
}
if ($behaviour === 'delete') {
$fields = array_merge($fields, [
'source' => ['type' => PARAM_TEXT, 'required' => true],
]);
}

return $fields;
}
Expand Down Expand Up @@ -131,6 +136,16 @@ public function form_add_custom_inputs(\MoodleQuickForm &$mform, $behaviour = 'c
)
);
}
if ($behaviour === 'delete') {
$mform->addElement('text', 'config_source', get_string('connector_sftp:source', 'tool_dataflows'));
$mform->addElement('static', 'config_source_desc', '', get_string('connector_sftp:source_desc', 'tool_dataflows').
\html_writer::nonempty_tag(
'pre',
get_string('connector_sftp:path_example', 'tool_dataflows').
get_string('path_help_examples', 'tool_dataflows')
)
);
}
}

/**
Expand Down Expand Up @@ -190,19 +205,42 @@ public function validate_config($config, $behaviour = 'copy') {
);
}
}
// Delete step checks.
if ($behaviour === 'delete') {
if (empty($config->source)) {
$errors['config_source'] = get_string(
'config_field_missing',
'tool_dataflows',
get_string('connector_sftp:source', 'tool_dataflows'),
true
);
}
}

$hasremote = true;
// Check that at least one file config has an sftp:// scheme.
if (!empty($config->source) && !empty($config->target)) {
// Check if the source or target is an expression, and evaluate it if required.
$sourceremote = helper::path_has_scheme($config->source, self::$sftpprefix);
$targetremote = helper::path_has_scheme($config->target, self::$sftpprefix);
$hasremote = $sourceremote || $targetremote;
}
if (!$hasremote) {
$errormsg = get_string('connector_sftp:missing_remote', 'tool_dataflows', null, true);
$errors['config_source'] = $errors['config_source'] ?? $errormsg;
$errors['config_target'] = $errors['config_target'] ?? $errormsg;
if ($behaviour === 'delete') {
if (!empty($config->source)) {
// Check if the source or target is an expression, and evaluate it if required.
$sourceremote = helper::path_has_scheme($config->source, self::$sftpprefix);
$hasremote = $sourceremote;
}
if (!$hasremote) {
$errormsg = get_string('connector_sftp:missing_remote', 'tool_dataflows', null, true);
$errors['config_source'] = $errors['config_source'] ?? $errormsg;
}
} else {
if (!empty($config->source) && !empty($config->target)) {
// Check if the source or target is an expression, and evaluate it if required.
$sourceremote = helper::path_has_scheme($config->source, self::$sftpprefix);
$targetremote = helper::path_has_scheme($config->target, self::$sftpprefix);
$hasremote = $sourceremote || $targetremote;
}
if (!$hasremote) {
$errormsg = get_string('connector_sftp:missing_remote', 'tool_dataflows', null, true);
$errors['config_source'] = $errors['config_source'] ?? $errormsg;
$errors['config_target'] = $errors['config_target'] ?? $errormsg;
}
}

return empty($errors) ? true : $errors;
Expand Down Expand Up @@ -230,6 +268,12 @@ public function validate_for_run($behaviour = 'copy') {
$errors['config_target'] = $error;
}
}
if ($behaviour === 'delete') {
$error = helper::path_validate($config->source);
if ($error !== true) {
$errors['config_source'] = $error;
}
}
if (!empty($config->privkeyfile)) {
$error = helper::path_validate($config->privkeyfile);
if ($error !== true) {
Expand Down Expand Up @@ -399,6 +443,19 @@ private function copy_remote_to_remote(SFTP $sftp, string $sourcepath, string $t
$this->upload($sftp, $tmppath, $targetpath);
}

/**
* Delete a file from one remote source
*
* @param SFTP $sftp
* @param string $sourcepath
*/
private function delete_from_remote(SFTP $sftp, string $sourcepath) {
$this->log("Deleting from '$sourcepath'");
if (!$sftp->delete($sourcepath, false)) {
throw new \moodle_exception('connector_sftp:delete_fail', 'tool_dataflows', '', $sftp->getLastSFTPError());
}
}

/**
* Lists files in a directory
*
Expand Down
2 changes: 2 additions & 0 deletions lang/en/tool_dataflows.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@
$string['step_name_connector_set_variable'] = 'Set variable';
$string['step_name_connector_set_multiple_variables'] = 'Set multiple variables';
$string['step_name_connector_sftp'] = 'SFTP file copy';
$string['step_name_connector_sftp_delete_file'] = 'SFTP file to delete';
$string['step_name_connector_sns_notify'] = 'AWS-SNS Notification';
$string['step_name_connector_wait'] = 'Wait';
$string['step_name_flow_abort'] = 'Abort';
Expand Down Expand Up @@ -485,6 +486,7 @@
$string['connector_sftp:hostpubkey'] = 'Host public key';
$string['connector_sftp:hostpubkey_desc'] = 'Public key that must match the one returned by the host. If empty, it will be set on the first connection.';
$string['connector_sftp:copy_fail'] = 'Copy failed: \'{$a}\'.';
$string['connector_sftp:delete_fail'] = 'Delete failed: \'{$a}\'.';
$string['connector_sftp:missing_remote'] = 'At least one of source/target must be remote.';
$string['connector_sftp:pubkeyfile'] = 'Public key file';
$string['connector_sftp:privkeyfile'] = 'Private key file';
Expand Down
1 change: 1 addition & 0 deletions lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ function tool_dataflows_step_types() {
new step\connector_s3,
new step\connector_set_variable,
new step\connector_sftp,
new step\connector_sftp_delete_file,
new step\connector_sql,
new step\connector_update_user,
new step\connector_sftp_directory_file_list,
Expand Down
2 changes: 1 addition & 1 deletion version.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

defined('MOODLE_INTERNAL') || die();

$plugin->version = 2025021100;
$plugin->version = 2025033100;
$plugin->release = 2025021100;
$plugin->requires = 2024100700; // Our lowest supported Moodle (4.5.0).
$plugin->supported = [405, 405];
Expand Down
Loading