|
1 <?php |
|
2 |
|
3 /** |
|
4 * @file |
|
5 * Helper functions and form handlers used for the authorize.php script. |
|
6 */ |
|
7 |
|
8 /** |
|
9 * Form constructor for the file transfer authorization form. |
|
10 * |
|
11 * Allows the user to choose a FileTransfer type and supply credentials. |
|
12 * |
|
13 * @see authorize_filetransfer_form_validate() |
|
14 * @see authorize_filetransfer_form_submit() |
|
15 * @ingroup forms |
|
16 */ |
|
17 function authorize_filetransfer_form($form, &$form_state) { |
|
18 global $base_url, $is_https; |
|
19 $form = array(); |
|
20 |
|
21 // If possible, we want to post this form securely via HTTPS. |
|
22 $form['#https'] = TRUE; |
|
23 |
|
24 // CSS we depend on lives in modules/system/maintenance.css, which is loaded |
|
25 // via the default maintenance theme. |
|
26 $form['#attached']['js'][] = $base_url . '/misc/authorize.js'; |
|
27 |
|
28 // Get all the available ways to transfer files. |
|
29 if (empty($_SESSION['authorize_filetransfer_info'])) { |
|
30 drupal_set_message(t('Unable to continue, no available methods of file transfer'), 'error'); |
|
31 return array(); |
|
32 } |
|
33 $available_backends = $_SESSION['authorize_filetransfer_info']; |
|
34 |
|
35 if (!$is_https) { |
|
36 $form['information']['https_warning'] = array( |
|
37 '#prefix' => '<div class="messages error">', |
|
38 '#markup' => 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')), |
|
39 '#suffix' => '</div>', |
|
40 ); |
|
41 } |
|
42 |
|
43 // Decide on a default backend. |
|
44 if (isset($form_state['values']['connection_settings']['authorize_filetransfer_default'])) { |
|
45 $authorize_filetransfer_default = $form_state['values']['connection_settings']['authorize_filetransfer_default']; |
|
46 } |
|
47 elseif ($authorize_filetransfer_default = variable_get('authorize_filetransfer_default', NULL)); |
|
48 else { |
|
49 $authorize_filetransfer_default = key($available_backends); |
|
50 } |
|
51 |
|
52 $form['information']['main_header'] = array( |
|
53 '#prefix' => '<h3>', |
|
54 '#markup' => t('To continue, provide your server connection details'), |
|
55 '#suffix' => '</h3>', |
|
56 ); |
|
57 |
|
58 $form['connection_settings']['#tree'] = TRUE; |
|
59 $form['connection_settings']['authorize_filetransfer_default'] = array( |
|
60 '#type' => 'select', |
|
61 '#title' => t('Connection method'), |
|
62 '#default_value' => $authorize_filetransfer_default, |
|
63 '#weight' => -10, |
|
64 ); |
|
65 |
|
66 /* |
|
67 * Here we create two submit buttons. For a JS enabled client, they will |
|
68 * only ever see submit_process. However, if a client doesn't have JS |
|
69 * enabled, they will see submit_connection on the first form (when picking |
|
70 * what filetransfer type to use, and submit_process on the second one (which |
|
71 * leads to the actual operation). |
|
72 */ |
|
73 $form['submit_connection'] = array( |
|
74 '#prefix' => "<br style='clear:both'/>", |
|
75 '#name' => 'enter_connection_settings', |
|
76 '#type' => 'submit', |
|
77 '#value' => t('Enter connection settings'), |
|
78 '#weight' => 100, |
|
79 ); |
|
80 |
|
81 $form['submit_process'] = array( |
|
82 '#name' => 'process_updates', |
|
83 '#type' => 'submit', |
|
84 '#value' => t('Continue'), |
|
85 '#weight' => 100, |
|
86 '#attributes' => array('style' => 'display:none'), |
|
87 ); |
|
88 |
|
89 // Build a container for each connection type. |
|
90 foreach ($available_backends as $name => $backend) { |
|
91 $form['connection_settings']['authorize_filetransfer_default']['#options'][$name] = $backend['title']; |
|
92 $form['connection_settings'][$name] = array( |
|
93 '#type' => 'container', |
|
94 '#attributes' => array('class' => array("filetransfer-$name", 'filetransfer')), |
|
95 ); |
|
96 // We can't use #prefix on the container itself since then the header won't |
|
97 // be hidden and shown when the containers are being manipulated via JS. |
|
98 $form['connection_settings'][$name]['header'] = array( |
|
99 '#markup' => '<h4>' . t('@backend connection settings', array('@backend' => $backend['title'])) . '</h4>', |
|
100 ); |
|
101 |
|
102 $form['connection_settings'][$name] += _authorize_filetransfer_connection_settings($name); |
|
103 |
|
104 // Start non-JS code. |
|
105 if (isset($form_state['values']['connection_settings']['authorize_filetransfer_default']) && $form_state['values']['connection_settings']['authorize_filetransfer_default'] == $name) { |
|
106 |
|
107 // If the user switches from JS to non-JS, Drupal (and Batch API) will |
|
108 // barf. This is a known bug: http://drupal.org/node/229825. |
|
109 setcookie('has_js', '', time() - 3600, '/'); |
|
110 unset($_COOKIE['has_js']); |
|
111 |
|
112 // Change the submit button to the submit_process one. |
|
113 $form['submit_process']['#attributes'] = array(); |
|
114 unset($form['submit_connection']); |
|
115 |
|
116 // Activate the proper filetransfer settings form. |
|
117 $form['connection_settings'][$name]['#attributes']['style'] = 'display:block'; |
|
118 // Disable the select box. |
|
119 $form['connection_settings']['authorize_filetransfer_default']['#disabled'] = TRUE; |
|
120 |
|
121 // Create a button for changing the type of connection. |
|
122 $form['connection_settings']['change_connection_type'] = array( |
|
123 '#name' => 'change_connection_type', |
|
124 '#type' => 'submit', |
|
125 '#value' => t('Change connection type'), |
|
126 '#weight' => -5, |
|
127 '#attributes' => array('class' => array('filetransfer-change-connection-type')), |
|
128 ); |
|
129 } |
|
130 // End non-JS code. |
|
131 } |
|
132 return $form; |
|
133 } |
|
134 |
|
135 /** |
|
136 * Generates the Form API array for a given connection backend's settings. |
|
137 * |
|
138 * @param $backend |
|
139 * The name of the backend (e.g. 'ftp', 'ssh', etc). |
|
140 * |
|
141 * @return |
|
142 * Form API array of connection settings for the given backend. |
|
143 * |
|
144 * @see hook_filetransfer_backends() |
|
145 */ |
|
146 function _authorize_filetransfer_connection_settings($backend) { |
|
147 $defaults = variable_get('authorize_filetransfer_connection_settings_' . $backend, array()); |
|
148 $form = array(); |
|
149 |
|
150 // Create an instance of the file transfer class to get its settings form. |
|
151 $filetransfer = authorize_get_filetransfer($backend); |
|
152 if ($filetransfer) { |
|
153 $form = $filetransfer->getSettingsForm(); |
|
154 } |
|
155 // Fill in the defaults based on the saved settings, if any. |
|
156 _authorize_filetransfer_connection_settings_set_defaults($form, NULL, $defaults); |
|
157 return $form; |
|
158 } |
|
159 |
|
160 /** |
|
161 * Sets the default settings on a file transfer connection form recursively. |
|
162 * |
|
163 * The default settings for the file transfer connection forms are saved in |
|
164 * the database. The settings are stored as a nested array in the case of a |
|
165 * settings form that has fieldsets or otherwise uses a nested structure. |
|
166 * Therefore, to properly add defaults, we need to walk through all the |
|
167 * children form elements and process those defaults recursively. |
|
168 * |
|
169 * @param $element |
|
170 * Reference to the Form API form element we're operating on. |
|
171 * @param $key |
|
172 * The key for our current form element, if any. |
|
173 * @param array $defaults |
|
174 * The default settings for the file transfer backend we're operating on. |
|
175 */ |
|
176 function _authorize_filetransfer_connection_settings_set_defaults(&$element, $key, array $defaults) { |
|
177 // If we're operating on a form element which isn't a fieldset, and we have |
|
178 // a default setting saved, stash it in #default_value. |
|
179 if (!empty($key) && isset($defaults[$key]) && isset($element['#type']) && $element['#type'] != 'fieldset') { |
|
180 $element['#default_value'] = $defaults[$key]; |
|
181 } |
|
182 // Now, we walk through all the child elements, and recursively invoke |
|
183 // ourself on each one. Since the $defaults settings array can be nested |
|
184 // (because of #tree, any values inside fieldsets will be nested), if |
|
185 // there's a subarray of settings for the form key we're currently |
|
186 // processing, pass in that subarray to the recursive call. Otherwise, just |
|
187 // pass on the whole $defaults array. |
|
188 foreach (element_children($element) as $child_key) { |
|
189 _authorize_filetransfer_connection_settings_set_defaults($element[$child_key], $child_key, ((isset($defaults[$key]) && is_array($defaults[$key])) ? $defaults[$key] : $defaults)); |
|
190 } |
|
191 } |
|
192 |
|
193 /** |
|
194 * Form validation handler for authorize_filetransfer_form(). |
|
195 * |
|
196 * @see authorize_filetransfer_form() |
|
197 * @see authorize_filetransfer_submit() |
|
198 */ |
|
199 function authorize_filetransfer_form_validate($form, &$form_state) { |
|
200 // Only validate the form if we have collected all of the user input and are |
|
201 // ready to proceed with updating or installing. |
|
202 if ($form_state['triggering_element']['#name'] != 'process_updates') { |
|
203 return; |
|
204 } |
|
205 |
|
206 if (isset($form_state['values']['connection_settings'])) { |
|
207 $backend = $form_state['values']['connection_settings']['authorize_filetransfer_default']; |
|
208 $filetransfer = authorize_get_filetransfer($backend, $form_state['values']['connection_settings'][$backend]); |
|
209 try { |
|
210 if (!$filetransfer) { |
|
211 throw new Exception(t('Error, this type of connection protocol (%backend) does not exist.', array('%backend' => $backend))); |
|
212 } |
|
213 $filetransfer->connect(); |
|
214 } |
|
215 catch (Exception $e) { |
|
216 // The format of this error message is similar to that used on the |
|
217 // database connection form in the installer. |
|
218 form_set_error('connection_settings', t('Failed to connect to the server. The server reports the following message: !message For more help installing or updating code on your server, see the <a href="@handbook_url">handbook</a>.', array( |
|
219 '!message' => '<p class="error">' . $e->getMessage() . '</p>', |
|
220 '@handbook_url' => 'http://drupal.org/documentation/install/modules-themes', |
|
221 ))); |
|
222 } |
|
223 } |
|
224 } |
|
225 |
|
226 /** |
|
227 * Form submission handler for authorize_filetransfer_form(). |
|
228 * |
|
229 * @see authorize_filetransfer_form() |
|
230 * @see authorize_filetransfer_validate() |
|
231 */ |
|
232 function authorize_filetransfer_form_submit($form, &$form_state) { |
|
233 global $base_url; |
|
234 switch ($form_state['triggering_element']['#name']) { |
|
235 case 'process_updates': |
|
236 |
|
237 // Save the connection settings to the DB. |
|
238 $filetransfer_backend = $form_state['values']['connection_settings']['authorize_filetransfer_default']; |
|
239 |
|
240 // If the database is available then try to save our settings. We have |
|
241 // to make sure it is available since this code could potentially (will |
|
242 // likely) be called during the installation process, before the |
|
243 // database is set up. |
|
244 try { |
|
245 $connection_settings = array(); |
|
246 foreach ($form_state['values']['connection_settings'][$filetransfer_backend] as $key => $value) { |
|
247 // We do *not* want to store passwords in the database, unless the |
|
248 // backend explicitly says so via the magic #filetransfer_save form |
|
249 // property. Otherwise, we store everything that's not explicitly |
|
250 // marked with #filetransfer_save set to FALSE. |
|
251 if (!isset($form['connection_settings'][$filetransfer_backend][$key]['#filetransfer_save'])) { |
|
252 if ($form['connection_settings'][$filetransfer_backend][$key]['#type'] != 'password') { |
|
253 $connection_settings[$key] = $value; |
|
254 } |
|
255 } |
|
256 // The attribute is defined, so only save if set to TRUE. |
|
257 elseif ($form['connection_settings'][$filetransfer_backend][$key]['#filetransfer_save']) { |
|
258 $connection_settings[$key] = $value; |
|
259 } |
|
260 } |
|
261 // Set this one as the default authorize method. |
|
262 variable_set('authorize_filetransfer_default', $filetransfer_backend); |
|
263 // Save the connection settings minus the password. |
|
264 variable_set('authorize_filetransfer_connection_settings_' . $filetransfer_backend, $connection_settings); |
|
265 |
|
266 $filetransfer = authorize_get_filetransfer($filetransfer_backend, $form_state['values']['connection_settings'][$filetransfer_backend]); |
|
267 |
|
268 // Now run the operation. |
|
269 authorize_run_operation($filetransfer); |
|
270 } |
|
271 catch (Exception $e) { |
|
272 // If there is no database available, we don't care and just skip |
|
273 // this part entirely. |
|
274 } |
|
275 |
|
276 break; |
|
277 |
|
278 case 'enter_connection_settings': |
|
279 $form_state['rebuild'] = TRUE; |
|
280 break; |
|
281 |
|
282 case 'change_connection_type': |
|
283 $form_state['rebuild'] = TRUE; |
|
284 unset($form_state['values']['connection_settings']['authorize_filetransfer_default']); |
|
285 break; |
|
286 } |
|
287 } |
|
288 |
|
289 /** |
|
290 * Runs the operation specified in $_SESSION['authorize_operation']. |
|
291 * |
|
292 * @param $filetransfer |
|
293 * The FileTransfer object to use for running the operation. |
|
294 */ |
|
295 function authorize_run_operation($filetransfer) { |
|
296 $operation = $_SESSION['authorize_operation']; |
|
297 unset($_SESSION['authorize_operation']); |
|
298 |
|
299 if (!empty($operation['page_title'])) { |
|
300 drupal_set_title($operation['page_title']); |
|
301 } |
|
302 |
|
303 require_once DRUPAL_ROOT . '/' . $operation['file']; |
|
304 call_user_func_array($operation['callback'], array_merge(array($filetransfer), $operation['arguments'])); |
|
305 } |
|
306 |
|
307 /** |
|
308 * Gets a FileTransfer class for a specific transfer method and settings. |
|
309 * |
|
310 * @param $backend |
|
311 * The FileTransfer backend to get the class for. |
|
312 * @param $settings |
|
313 * Array of settings for the FileTransfer. |
|
314 * |
|
315 * @return |
|
316 * An instantiated FileTransfer object for the requested method and settings, |
|
317 * or FALSE if there was an error finding or instantiating it. |
|
318 */ |
|
319 function authorize_get_filetransfer($backend, $settings = array()) { |
|
320 $filetransfer = FALSE; |
|
321 if (!empty($_SESSION['authorize_filetransfer_info'][$backend])) { |
|
322 $backend_info = $_SESSION['authorize_filetransfer_info'][$backend]; |
|
323 if (!empty($backend_info['file'])) { |
|
324 $file = $backend_info['file path'] . '/' . $backend_info['file']; |
|
325 require_once $file; |
|
326 } |
|
327 if (class_exists($backend_info['class'])) { |
|
328 // PHP 5.2 doesn't support $class::factory() syntax, so we have to |
|
329 // use call_user_func_array() until we can require PHP 5.3. |
|
330 $filetransfer = call_user_func_array(array($backend_info['class'], 'factory'), array(DRUPAL_ROOT, $settings)); |
|
331 } |
|
332 } |
|
333 return $filetransfer; |
|
334 } |