Skip to content

Commit 9f5cd53

Browse files
committed
#609772 by dww, JacobSingh, ksenzee: Fixed Impossible to extend the FileTransfer class system in contrib
1 parent 5c3fdc3 commit 9f5cd53

11 files changed

Lines changed: 353 additions & 134 deletions

File tree

‎authorize.php‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ function authorize_access_allowed() {
125125
// Clear the session out.
126126
unset($_SESSION['authorize_results']);
127127
unset($_SESSION['authorize_operation']);
128-
unset($_SESSION['authorize_filetransfer_backends']);
128+
unset($_SESSION['authorize_filetransfer_info']);
129129

130130
if (!empty($results['page_title'])) {
131131
drupal_set_title(check_plain($results['page_title']));
@@ -154,7 +154,7 @@ function authorize_access_allowed() {
154154
$output = _batch_page();
155155
}
156156
else {
157-
if (empty($_SESSION['authorize_operation']) || empty($_SESSION['authorize_filetransfer_backends'])) {
157+
if (empty($_SESSION['authorize_operation']) || empty($_SESSION['authorize_filetransfer_info'])) {
158158
$output = t('It appears you have reached this page in error.');
159159
}
160160
elseif (!$batch = batch_get()) {

‎includes/authorize.inc‎

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,11 @@ function authorize_filetransfer_form($form_state) {
2121
$form['#attached']['js'][] = $base_url . '/misc/authorize.js';
2222

2323
// Get all the available ways to transfer files.
24-
if (empty($_SESSION['authorize_filetransfer_backends'])) {
24+
if (empty($_SESSION['authorize_filetransfer_info'])) {
2525
drupal_set_message(t('Unable to continue, no available methods of file transfer'), 'error');
2626
return array();
2727
}
28-
$available_backends = $_SESSION['authorize_filetransfer_backends'];
29-
uasort($available_backends, 'drupal_sort_weight');
28+
$available_backends = $_SESSION['authorize_filetransfer_info'];
3029

3130
if (!$is_https) {
3231
drupal_set_message(t('WARNING: You are not using an encrypted connection, so your password will be sent in plain text. <a href="@https-link">Learn more</a>.', array('@https-link' => 'http://drupal.org/https-information')), 'error');
@@ -87,8 +86,7 @@ function authorize_filetransfer_form($form_state) {
8786
'#title' => t('@backend connection settings', array('@backend' => $backend['title'])),
8887
);
8988

90-
$current_settings = variable_get('authorize_filetransfer_connection_settings_' . $name, array());
91-
$form['connection_settings'][$name] += system_get_filetransfer_settings_form($name, $current_settings);
89+
$form['connection_settings'][$name] += _authorize_filetransfer_connection_settings($name);
9290

9391
// Start non-JS code.
9492
if (isset($form_state['values']['connection_settings']['authorize_filetransfer_default']) && $form_state['values']['connection_settings']['authorize_filetransfer_default'] == $name) {
@@ -121,6 +119,65 @@ function authorize_filetransfer_form($form_state) {
121119
return $form;
122120
}
123121

122+
/**
123+
* Generate the Form API array for the settings for a given connection backend.
124+
*
125+
* @param $backend
126+
* The name of the backend (e.g. 'ftp', 'ssh', etc).
127+
* @return
128+
* Form API array of connection settings for the given backend.
129+
*
130+
* @see hook_filetransfer_backends()
131+
*/
132+
function _authorize_filetransfer_connection_settings($backend) {
133+
$defaults = variable_get('authorize_filetransfer_connection_settings_' . $backend, array());
134+
$form = array();
135+
136+
// Create an instance of the file transfer class to get its settings form.
137+
$filetransfer = authorize_get_filetransfer($backend);
138+
if ($filetransfer) {
139+
$form = $filetransfer->getSettingsForm();
140+
}
141+
// Fill in the defaults based on the saved settings, if any.
142+
_authorize_filetransfer_connection_settings_set_defaults($form, NULL, $defaults);
143+
return $form;
144+
}
145+
146+
/**
147+
* Recursively fill in the default settings on a file transfer connection form.
148+
*
149+
* The default settings for the file transfer connection forms are saved in
150+
* the database. The settings are stored as a nested array in the case of a
151+
* settings form that has fieldsets or otherwise uses a nested structure.
152+
* Therefore, to properly add defaults, we need to walk through all the
153+
* children form elements and process those defaults recursively.
154+
*
155+
* @param &$element
156+
* Reference to the Form API form element we're operating on.
157+
* @param $key
158+
* The key for our current form element, if any.
159+
* @param array $defaults
160+
* The default settings for the file transfer backend we're operating on.
161+
* @return
162+
* Nothing, this function just sets $element['#default_value'] if needed.
163+
*/
164+
function _authorize_filetransfer_connection_settings_set_defaults(&$element, $key, array $defaults) {
165+
// If we're operating on a form element which isn't a fieldset, and we have
166+
// a default setting saved, stash it in #default_value.
167+
if (!empty($key) && isset($defaults[$key]) && isset($element['#type']) && $element['#type'] != 'fieldset') {
168+
$element['#default_value'] = $defaults[$key];
169+
}
170+
// Now, we walk through all the child elements, and recursively invoke
171+
// ourself on each one. Since the $defaults settings array can be nested
172+
// (because of #tree, any values inside fieldsets will be nested), if
173+
// there's a subarray of settings for the form key we're currently
174+
// processing, pass in that subarray to the recursive call. Otherwise, just
175+
// pass on the whole $defaults array.
176+
foreach (element_children($element) as $child_key) {
177+
_authorize_filetransfer_connection_settings_set_defaults($element[$child_key], $child_key, ((isset($defaults[$key]) && is_array($defaults[$key])) ? $defaults[$key] : $defaults));
178+
}
179+
}
180+
124181
/**
125182
* Validate callback for the filetransfer authorization form.
126183
*
@@ -235,9 +292,17 @@ function authorize_run_operation($filetransfer) {
235292
*/
236293
function authorize_get_filetransfer($backend, $settings = array()) {
237294
$filetransfer = FALSE;
238-
if (!empty($_SESSION['authorize_filetransfer_backends'][$backend])) {
239-
$filetransfer = call_user_func_array(array($_SESSION['authorize_filetransfer_backends'][$backend]['class'], 'factory'), array(DRUPAL_ROOT, $settings));
295+
if (!empty($_SESSION['authorize_filetransfer_info'][$backend])) {
296+
$backend_info = $_SESSION['authorize_filetransfer_info'][$backend];
297+
if (!empty($backend_info['file'])) {
298+
$file = $backend_info['file path'] . '/' . $backend_info['file'];
299+
require_once $file;
300+
}
301+
if (class_exists($backend_info['class'])) {
302+
// PHP 5.2 doesn't support $class::factory() syntax, so we have to
303+
// use call_user_func_array() until we can require PHP 5.3.
304+
$filetransfer = call_user_func_array(array($backend_info['class'], 'factory'), array(DRUPAL_ROOT, $settings));
305+
}
240306
}
241307
return $filetransfer;
242308
}
243-

‎includes/common.inc‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7679,3 +7679,39 @@ function drupal_get_updaters() {
76797679
}
76807680
return $updaters;
76817681
}
7682+
7683+
/**
7684+
* Drupal FileTransfer registry.
7685+
*
7686+
* @return
7687+
* Returns the Drupal FileTransfer class registry.
7688+
*
7689+
* @see FileTransfer
7690+
* @see hook_filetransfer_info()
7691+
* @see hook_filetransfer_info_alter()
7692+
*/
7693+
function drupal_get_filetransfer_info() {
7694+
$info = &drupal_static(__FUNCTION__);
7695+
if (!isset($info)) {
7696+
// Since we have to manually set the 'file path' default for each
7697+
// module separately, we can't use module_invoke_all().
7698+
$info = array();
7699+
foreach (module_implements('filetransfer_info') as $module) {
7700+
$function = $module . '_filetransfer_info';
7701+
if (function_exists($function)) {
7702+
$result = $function();
7703+
if (isset($result) && is_array($result)) {
7704+
foreach ($result as &$values) {
7705+
if (empty($values['file path'])) {
7706+
$values['file path'] = drupal_get_path('module', $module);
7707+
}
7708+
}
7709+
$info = array_merge_recursive($info, $result);
7710+
}
7711+
}
7712+
}
7713+
drupal_alter('filetransfer_info', $info);
7714+
uasort($info, 'drupal_sort_weight');
7715+
}
7716+
return $info;
7717+
}

‎includes/filetransfer/filetransfer.inc‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,42 @@ abstract class FileTransfer {
313313
$this->chroot = $this->findChroot();
314314
$this->jail = $this->fixRemotePath($this->jail);
315315
}
316+
317+
/**
318+
* Returns a form to collect connection settings credentials.
319+
*
320+
* Implementing classes can either extend this form with fields collecting the
321+
* specific information they need, or override it entirely.
322+
*/
323+
public function getSettingsForm() {
324+
$form['username'] = array(
325+
'#type' => 'textfield',
326+
'#title' => t('Username'),
327+
);
328+
$form['password'] = array(
329+
'#type' => 'password',
330+
'#title' => t('Password'),
331+
'#description' => t('Your password is not saved in the database and is only used to establish a connection.'),
332+
);
333+
$form['advanced'] = array(
334+
'#type' => 'fieldset',
335+
'#title' => t('Advanced settings'),
336+
'#collapsible' => TRUE,
337+
'#collapsed' => TRUE,
338+
);
339+
$form['advanced']['hostname'] = array(
340+
'#type' => 'textfield',
341+
'#title' => t('Host'),
342+
'#default_value' => 'localhost',
343+
'#description' => t('The connection will be created between your web server and the machine hosting the web server files. In the vast majority of cases, this will be the same machine, and "localhost" is correct.'),
344+
);
345+
$form['advanced']['port'] = array(
346+
'#type' => 'textfield',
347+
'#title' => t('Port'),
348+
'#default_value' => NULL,
349+
);
350+
return $form;
351+
}
316352
}
317353

318354
/**

‎includes/filetransfer/ftp.inc‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ abstract class FileTransferFTP extends FileTransfer {
2424
* options. If the FTP PHP extension is available, use it.
2525
*/
2626
static function factory($jail, $settings) {
27+
$settings['username'] = empty($settings['username']) ? '' : $settings['username'];
28+
$settings['password'] = empty($settings['password']) ? '' : $settings['password'];
2729
$settings['hostname'] = empty($settings['hostname']) ? 'localhost' : $settings['hostname'];
2830
$settings['port'] = empty($settings['port']) ? 21 : $settings['port'];
2931

@@ -36,6 +38,15 @@ abstract class FileTransferFTP extends FileTransfer {
3638

3739
return new $class($jail, $settings['username'], $settings['password'], $settings['hostname'], $settings['port']);
3840
}
41+
42+
/**
43+
* Returns the form to configure the FileTransfer class for FTP.
44+
*/
45+
public function getSettingsForm() {
46+
$form = parent::getSettingsForm();
47+
$form['advanced']['port']['#default_value'] = 21;
48+
return $form;
49+
}
3950
}
4051

4152
class FileTransferFTPExtension extends FileTransferFTP implements FileTransferChmodInterface {

‎includes/filetransfer/ssh.inc‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ class FileTransferSSH extends FileTransfer implements FileTransferChmodInterface
2525
}
2626

2727
static function factory($jail, $settings) {
28+
$settings['username'] = empty($settings['hostname']) ? '' : $settings['username'];
29+
$settings['password'] = empty($settings['password']) ? '' : $settings['password'];
2830
$settings['hostname'] = empty($settings['hostname']) ? 'localhost' : $settings['hostname'];
2931
$settings['port'] = empty($settings['port']) ? 22 : $settings['port'];
3032
return new FileTransferSSH($jail, $settings['username'], $settings['password'], $settings['hostname'], $settings['port']);
@@ -95,4 +97,13 @@ class FileTransferSSH extends FileTransfer implements FileTransferChmodInterface
9597
throw new FileTransferException('Cannot change permissions of @path.', NULL, array('@path' => $path));
9698
}
9799
}
100+
101+
/**
102+
* Returns the form to configure the FileTransfer class for SSH.
103+
*/
104+
public function getSettingsForm() {
105+
$form = parent::getSettingsForm();
106+
$form['advanced']['port']['#default_value'] = 22;
107+
return $form;
108+
}
98109
}

‎modules/simpletest/tests/system_test.module‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ function system_test_menu() {
1616
'access callback' => TRUE,
1717
'type' => MENU_CALLBACK,
1818
);
19+
$items['system-test/authorize-init/%'] = array(
20+
'page callback' => 'system_test_authorize_init_page',
21+
'page arguments' => array(2),
22+
'access arguments' => array('administer software updates'),
23+
'type' => MENU_CALLBACK,
24+
);
1925
$items['system-test/redirect/%'] = array(
2026
'title' => 'Redirect',
2127
'page callback' => 'system_test_redirect',
@@ -311,3 +317,45 @@ function _system_test_second_shutdown_function($arg1, $arg2) {
311317
throw new Exception('Drupal is <blink>awesome</blink>.');
312318
}
313319

320+
/**
321+
* Implements hook_filetransfer_info().
322+
*/
323+
function system_test_filetransfer_info() {
324+
return array(
325+
'system_test' => array(
326+
'title' => t('System Test FileTransfer'),
327+
'file' => 'system_test.module', // Should be a .inc, but for test, ok.
328+
'class' => 'SystemTestFileTransfer',
329+
'weight' => -10,
330+
),
331+
);
332+
}
333+
334+
/**
335+
* Mock FileTransfer object to test the settings form functionality.
336+
*/
337+
class SystemTestFileTransfer {
338+
public static function factory() {
339+
return new SystemTestFileTransfer;
340+
}
341+
342+
public function getSettingsForm() {
343+
$form = array();
344+
$form['system_test_username'] = array(
345+
'#type' => 'textfield',
346+
'#title' => t('System Test Username'),
347+
);
348+
return $form;
349+
}
350+
}
351+
352+
/**
353+
* Page callback to initialize authorize.php during testing.
354+
*
355+
* @see system_authorized_init().
356+
*/
357+
function system_test_authorize_init_page($page_title) {
358+
$authorize_url = $GLOBALS['base_url'] . '/authorize.php';
359+
system_authorized_init('system_test_authorize_run', drupal_get_path('module', 'system_test') . '/system_test.module', array(), $page_title);
360+
drupal_goto($authorize_url);
361+
}

0 commit comments

Comments
 (0)