Newer
Older
Randall Knutson
committed
/**
* This file handles all the functions for downloading, transferring and
* installing apps during the install process.
*/
/**
* Add install tasks to profile install tasks.
*
Randall Knutson
committed
* See apps.api.php for use.
Randall Knutson
committed
function apps_profile_install_tasks($install_state, $apps_server) {
Frank Febbraro
committed
// Need to include the apps.module file because on installs the profile
// collects all install tasks before any modules are enabled
module_load_include('module', 'apps');
// Only use apps forms during interactive installs.
$tasks = array();
if ($install_state['interactive']) {
Randall Knutson
committed
$apps_server_name = $apps_server['machine name'];
Frank Febbraro
committed
$task_screen = 'apps_profile_apps_select_form_' . $apps_server_name;
$_SESSION['apps_servers'][$task_screen] = $apps_server;
Frank Febbraro
committed
$task_screen => array(
'display_name' => apps_profile_get_server_name($apps_server),
'type' => 'form',
'function' => 'apps_profile_apps_select_form',
),
Randall Knutson
committed
'apps_profile_download_app_modules_' . $apps_server_name => array(
'display' => FALSE,
'type' => 'batch',
'run' => (isset($_SESSION['apps']))?INSTALL_TASK_RUN_IF_NOT_COMPLETED:INSTALL_TASK_SKIP,
'function' => 'apps_profile_download_app_modules',
),
// Only need this if using filetransfer authorization.
Randall Knutson
committed
'apps_profile_authorize_transfer_' . $apps_server_name => array(
Frank Febbraro
committed
'run' => (!apps_installer_has_write_access() && isset($_SESSION['apps']))?INSTALL_TASK_RUN_IF_NOT_COMPLETED:INSTALL_TASK_SKIP,
'function' => 'apps_profile_authorize_transfer',
),
Randall Knutson
committed
'apps_profile_install_app_modules_' . $apps_server_name => array(
'display' => FALSE,
'type' => 'batch',
'run' => (isset($_SESSION['apps']))?INSTALL_TASK_RUN_IF_NOT_COMPLETED:INSTALL_TASK_SKIP,
'function' => 'apps_profile_install_app_modules',
),
Randall Knutson
committed
'apps_profile_enable_app_modules_' . $apps_server_name => array(
Matthew Cheney
committed
'type' => 'batch',
'run' => (isset($_SESSION['apps']))?INSTALL_TASK_RUN_IF_NOT_COMPLETED:INSTALL_TASK_SKIP,
'function' => 'apps_profile_enable_app_modules',
),
);
}
return $tasks;
}
/**
* Apps install form
*/
function apps_profile_apps_select_form($form, $form_state, &$install_state) {
drupal_set_title(t('Install Apps'));
// Get and cache the apps manifest file.
apps_include('manifest');
// If there is no internet things get in an unfixable state. Use try->catch
try {
Frank Febbraro
committed
$apps_server = $_SESSION['apps_servers'][$install_state['active_task']];
$_SESSION['apps_server'] = $apps_server;
$_SESSION['apps_manifest'] = apps_apps($apps_server['machine name']);
}
catch (Exception $e) {
$form['info'] = array(
'#markup' => t('<h2>Error</h2><p>Unable to connect to Apps Server.</p><p>Click "Continue" to finish the installation. You can either fix your internet connection and try the installation again or install apps later from the apps config page.</p>'),
);
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Continue'),
);
return $form;
}
// Set a message if no manifest received.
if (!isset($_SESSION['apps_manifest'])) {
drupal_set_message('<b>Unable to contact Apps Server.</b><br> For some reason we were unable to contact the apps server.', 'error');
}
else {
Frank Febbraro
committed
drupal_set_title(apps_profile_get_server_name($_SESSION['apps_server']));
$form['actions'] = array('#type' => 'actions', '#weight' => 3);
foreach($_SESSION['apps_manifest'] as $name => $app) {
if ($name != '#theme') {
$options[$name] = '<strong>' . $app['name'] . '</strong>';
if (isset($app['description'])) {
$options[$name] .= '<br>' . $app['description'];
}
}
}
$form = array();
$form['apps_message'] = array(
'#markup' => t('<h2>Apps</h2><p>Apps are the next generation in usability for Drupal. They contain bundles of functionality for your website. Select any apps you want to install right now. You can add more later on the apps page.</p></p>In order to install apps, you must be able to FTP or SSH into your server. This uses the same process as the update module.</p>'),
);
$form['apps_fieldset'] = array(
'#type' => 'fieldset',
'#title' => t('Select Apps To Install'),
'#collapsible' => FALSE,
);
$form['apps_fieldset']['apps'] = array(
'#type' =>'checkboxes',
'#title' => t('Apps'),
Randall Knutson
committed
'#default_value' => $_SESSION['apps_server']['default apps'],
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
'#options' => $options,
);
$form['default_content_fieldset'] = array(
'#type' => 'fieldset',
'#title' => t('Default Content'),
'#collapsible' => FALSE,
);
$form['default_content_fieldset']['default_content'] = array(
'#type' => 'checkbox',
'#title' => t('Install default content'),
'#default_value' => TRUE,
'#description' => t('By selecting this box default content will be installed for each app. Without default content the site may look empty before you start adding to it. You can remove the default content later by going to the apps config page.'),
);
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Install Apps'),
);
}
$form['actions']['skip'] = array(
'#type' => 'submit',
'#value' => t('Skip this step'),
);
drupal_add_css("#apps-profile-apps-select-form .form-submit { display:inline; }", array('type' => 'inline'));
return $form;
}
/**
* Submit function for apps_profile_apps_select_form.
*/
function apps_profile_apps_select_form_submit($form, &$form_state) {
Frank Febbraro
committed
$_SESSION['apps'] = array();
if ($form_state['values']['op'] == t('Install Apps')) {
$_SESSION['apps'] = array_filter($form_state['values']['apps']);
$_SESSION['apps_default_content'] = $form_state['values']['default_content'];
}
}
/**
* Batch process apps download.
*/
function apps_profile_download_app_modules(&$install_state) {
apps_include('installer');
Frank Febbraro
committed
$apps = array();
foreach ($_SESSION['apps'] as $id => $name) {
$apps[] = $_SESSION['apps_manifest'][$name];
}
$batch = apps_download_apps_batch($apps);
$batch['finished'] = 'apps_profile_download_batch_finished';
return $batch;
}
/**
* Batch callback invoked when the download batch is completed.
*
* This is a copy of update_manager_download_batch_finished without the goto
* which messes up the batch during install.
*/
function apps_profile_download_batch_finished($success, $results) {
if (!empty($results['errors'])) {
$error_list = array(
'title' => t('Downloading updates failed:'),
'items' => $results['errors'],
);
drupal_set_message(theme('item_list', $error_list), 'error');
}
elseif ($success) {
drupal_set_message(t('Updates downloaded successfully.'));
Matthew Cheney
committed
$_SESSION['update_manager_update_projects'] = isset($results['projects']) ? $results['projects'] : NULL;
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
}
else {
// Ideally we're catching all Exceptions, so they should never see this,
// but just in case, we have to tell them something.
drupal_set_message(t('Fatal error trying to download.'), 'error');
}
}
/**
* Get filetransfer authorization form.
*/
function apps_profile_authorize_transfer($form, $form_state, &$install_state) {
// Set the $_SESSION variables so that authorize form knows what to do after authorization.
system_authorized_init('apps_profile_authorize_transfer_save', drupal_get_path('module', 'apps') . '/apps.profile.inc', array(), t('Apps Install Manager'));
require_once DRUPAL_ROOT . '/includes/authorize.inc';
// Get the authorize form.
$form = drupal_retrieve_form('authorize_filetransfer_form', $form_state);
// Add in the default form handlers.
$form['#validate'][] = 'authorize_filetransfer_form_validate';
$form['#submit'][] = 'authorize_filetransfer_form_submit';
return $form;
}
/**
* Callback after the authorize_filetransfer_form_submit.
*
* Save the file transfer protocol.
*/
function apps_profile_authorize_transfer_save($filetransfer, $nothing = array()) {
$_SESSION['filetransfer'] = $filetransfer;
}
/**
* Batch process apps install.
*/
function apps_profile_install_app_modules(&$install_state) {
$batch = array();
if (!empty($_SESSION['update_manager_update_projects'])) {
apps_include('installer');
// Make sure the Updater registry is loaded.
drupal_get_updaters();
$updates = array();
Randall Knutson
committed
$project_types = $_SESSION['update_manager_update_projects'];
foreach($project_types as $type => $projects) {
$directory = apps_extract_directory($type);
foreach ($projects as $project => $url) {
$project_location = $directory . '/' . $project;
$updater = Updater::factory($project_location);
$project_real_location = drupal_realpath($project_location);
$updates[] = array(
'project' => $project,
'updater_name' => get_class($updater),
'local_url' => $project_real_location,
);
}
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
}
if (isset($_SESSION['filetransfer'])) {
// We have authenticated a filetransfer so use it.
$filetransfer = $_SESSION['filetransfer'];
}
else {
// This is a local transfer because the config_path is writeable.
$filetransfer = new FileTransferLocal(DRUPAL_ROOT);
}
module_load_include('inc', 'update', 'update.authorize');
$operations = array();
foreach ($updates as $update => $update_info) {
$operations[] = array(
'update_authorize_batch_copy_project',
array(
$update_info['project'],
$update_info['updater_name'],
$update_info['local_url'],
$filetransfer,
),
);
}
$batch = array(
'title' => t('Installing updates'),
'init_message' => t('Preparing to update your site'),
'operations' => $operations,
'file' => drupal_get_path('module', 'update') . '/update.authorize.inc',
);
unset($_SESSION['update_manager_update_projects']);
}
return $batch;
}
/**
* Install downloaded apps.
*/
function apps_profile_enable_app_modules(&$install_state) {
$modules = array_keys($_SESSION['apps']);
Randall Knutson
committed
// Allow profiles to add default content.
if ($_SESSION['apps_default_content'] && isset($_SESSION['apps_server']['default content callback'])) {
$function = $_SESSION['apps_server']['default content callback'];
if (function_exists($function)) {
$function($modules);
}
}
// Do dependency checking so everything doesn't break from one missing dependency.
foreach($modules as $id => $module) {
if (!apps_profile_check_dependencies(array($module => $module))) {
// Something went wrong. Remove from queue and add error.
unset($modules[$id]);
Matthew Cheney
committed
$module_info = system_get_info('module', $module);
$module_name = isset($module_info['name']) ? $module_info['name'] : $module;
drupal_set_message(t('There was an error installing @module app.', array('@module' => $module_name)), 'error');
}
}
Matthew Cheney
committed
// Setup the batch omments
$enable_commands = array();
foreach($modules as $module) {
$module_info = system_get_info('module', $module);
$module_name = isset($module_info['name']) ? $module_info['name'] : $module;
$enable_commands[] = array('app_profile_enable_module', array($module, $module_name));
}
// Setup the batch operation
$batch = array(
'operations' => $enable_commands,
'file' => drupal_get_path('module', 'apps') . '/apps.profile.inc',
'title' => t('Enabling apps'),
'init_message' => t('Preparing to enable the needed apps'),
'finished' => 'apps_profile_enable_app_modules_finished',
);
return $batch;
Matthew Cheney
committed
}
function app_profile_enable_module($module, $module_name, &$context) {
// This is here to show the user that we are in the process of enabling
$context['message'] = t('Enabled %module app', array('%module' => $module_name));
// Enable the module and record any errors
if (!module_enable(array($module))) {
$context['results']['errors'][$module] = t('There was an error enabling @module app.', array('@module' => $module_name));
}
// Successful outcome
$context['finished'] = TRUE;
}
/**
* Batch callback invoked when enable batch is completed.
*/
function apps_profile_enable_app_modules_finished($success, $results) {
Frank Febbraro
committed
// Only display errors, on success do nothing since on success any
// message we print will show up in a weird context (next page)
Matthew Cheney
committed
if (!empty($results['errors'])) {
$error_list = array(
'title' => t('Enabling apps failed:'),
'items' => $results['errors'],
);
drupal_set_message(theme('item_list', $error_list), 'error');
}
Nedjo Rogers
committed
elseif(!$success) {
Matthew Cheney
committed
// Ideally we're catching all Exceptions, so they should never see this,
// but just in case, we have to tell them something.
drupal_set_message(t('Fatal error trying to enable apps.'), 'error');
}
Matthew Cheney
committed
// We cannot unset $_SESSION['apps'] here because it is used in the
// run' check for this task and can cause wierd interactions with
// the batch operations.
// unset($_SESSION['apps']);
unset($_SESSION['apps_default_content']);
unset($_SESSION['apps_server']);
unset($_SESSION['apps_manifest']);
}
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
/**
* Function to check and make sure all dependencies are available. This will stop stray apps
* or broken downloads from stopping the install process for everything.
*
* Copied from module_enable() in module.inc
*/
function apps_profile_check_dependencies($module_list) {
// Get all module data so we can find dependencies and sort.
$module_data = system_rebuild_module_data();
// Create an associative array with weights as values.
$module_list = array_flip(array_values($module_list));
while (list($module) = each($module_list)) {
if (!isset($module_data[$module])) {
// This module is not found in the filesystem, abort.
return FALSE;
}
if ($module_data[$module]->status) {
// Skip already enabled modules.
unset($module_list[$module]);
continue;
}
$module_list[$module] = $module_data[$module]->sort;
// Add dependencies to the list, with a placeholder weight.
// The new modules will be processed as the while loop continues.
foreach (array_keys($module_data[$module]->requires) as $dependency) {
if (!isset($module_list[$dependency])) {
$module_list[$dependency] = 0;
}
}
}
// If we make it this far then everything is good.
return TRUE;
Randall Knutson
committed
}
Frank Febbraro
committed
/**
* Generate the title of the Apps install screen
*/
function apps_profile_get_server_name($server) {
$t = get_t();
return isset($server['title'])
? $t('Install @name Apps', array('@name' => $server['title']))
: $t('Install Apps');