Skip to content
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
340 changes: 220 additions & 120 deletions module/classes/provider/drupal7.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,58 +75,94 @@ public function getUpdateRows(){
// Return
$updates = array();

// Pull Projects
$projects = update_get_projects();
// Get update information from cache and refreshes it when necessary.
$available = update_get_available();

// Pull Available Releases
$packages =
db_select('cache_update', 'cu')
->fields('cu')
->condition('cid', 'available_releases::%', 'LIKE')
->execute()
->fetchAllAssoc('cid');
// Pull Projects
module_load_include('inc', 'update', 'update.compare');
$project_data = update_calculate_project_data($available);

// Process Rows
foreach( $packages AS $package ){

// Unserialize Store
$package->data = unserialize( $package->data );
$project = isset($projects[$package->data['short_name']]) ? $projects[$package->data['short_name']] : null;
$package_update = reset($package->data['releases']);
foreach ($project_data as $name => $project) {
// Filter out projects which are up to date already.
if ($project['status'] == UPDATE_CURRENT) {
continue;
}

// The project name to display can vary based on the info we have.
if (!empty($project['title']))
$project_name = check_plain($project['title']);
elseif (!empty($project['info']['name']))
$project_name = check_plain($project['info']['name']);
else
$project_name = check_plain($name);

if ($project['project_type'] == 'theme' || $project['project_type'] == 'theme-disabled') {
$project_name .= ' ' . '(Theme)';
}

if (empty($project['recommended'])) {
// If we don't know what to recommend they upgrade to, we should skip
// the project entirely.
continue;
}

$recommended_release = $project['releases'][$project['recommended']];

switch ($project['status']) {
case UPDATE_NOT_SECURE:
case UPDATE_REVOKED:
$project_name .= ' ' . '(Security update)';
break;

case UPDATE_NOT_SUPPORTED:
$project_name .= ' ' . '(Unsupported)';
break;

case UPDATE_UNKNOWN:
case UPDATE_NOT_FETCHED:
case UPDATE_NOT_CHECKED:
case UPDATE_NOT_CURRENT:
break;

default:
// Jump out of the switch and onto the next project in foreach.
continue 2;
}

// Push Return
switch( $project['project_type'] ){
case 'core':
/*
$updates[] = $this->caller->_newUpdateRow(array(
'update_id' => $project['project_type'].':'.$package_update['version'],
'extension_id' => 'drupal',
'name' => 'Drupal',
'element' => 'drupal',
'update_id' => $project['project_type'].':'.$project['name'].':'.$recommended_release['version'],
'extension_id' => $project['name'],
'name' => $project_name,
'description' => $project['info']['package'],
'element' => $project['info']['project'],
'type' => $project['project_type'],
'status' => 'manual',
'version' => $project['info']['version'],
'detailsurl' => $package_update['download_link'],
'infourl' => $package_update['release_link'],
'installed_version' => $project['info']['version']
'version' => $recommended_release['version'],
'detailsurl' => $project['link'],
'infourl' => $recommended_release['release_link'],
'downloadurl' => $recommended_release['download_link'],
'installed_version' => $project['info']['version'],
'status' => 'manual'
));
break;
*/
case 'module':
case 'theme':
$updates[] = $this->caller->_newUpdateRow(array(
'update_id' => $project['project_type'].':'.$project['name'].':'.$package_update['version'],
'update_id' => $project['project_type'].':'.$project['name'].':'.$recommended_release['version'],
'extension_id' => $project['name'],
'name' => $project['info']['name'],
'name' => $project_name,
'description' => $project['info']['package'],
'element' => $project['info']['project'],
'type' => $project['project_type'],
'status' => 'available',
'version' => $package_update['version'],
'detailsurl' => $package_update['download_link'],
'infourl' => $package_update['release_link'],
'version' => $recommended_release['version'],
'detailsurl' => $project['link'],
'infourl' => $recommended_release['release_link'],
'downloadurl' => $recommended_release['download_link'],
'installed_version' => $project['info']['version']
));
));
break;
default:
break;
Expand All @@ -141,95 +177,39 @@ public function getUpdateRows(){

public function doInstallUpdate( $update_row ){

// Pull Projects
$projects = update_get_projects();
module_load_include('inc', 'update', 'update.manager');

// Pull Available Releases
$packages =
db_select('cache_update', 'cu')
->fields('cu')
->condition('cid', 'available_releases::%', 'LIKE')
->execute()
->fetchAllAssoc('cid');

// Find & Process Package
foreach( $packages AS $package ){

// Unserialize Store
$package->data = unserialize( $package->data );
$project = isset($projects[$package->data['short_name']]) ? $projects[$package->data['short_name']] : null;
$package_update = reset($package->data['releases']);
$package_uid = $project['project_type'].':'.$project['name'].':'.$package_update['version'];

// Match Package
if( $update_row->update_id == $package_uid ){

// Type Switch
switch( $update_row->type ){

case 'core':
$this->caller->out(' - Error: Core Updates Not Supported');
break;

case 'module':
case 'theme':

// Download Package
if( !($local_cache = update_manager_file_get($package_update['download_link'])) ){
$this->caller->out(' - Error: Failed to download '. $project['name'] .' from '. $package_update['download_link']);
return false;
}

// Extract it.
$extract_directory = _update_manager_extract_directory();
try {
update_manager_archive_extract($local_cache, $extract_directory);
}
catch (Exception $e) {
$this->caller->out(' - Error: ' . $e->getMessage());
return false;
}

// Verify it.
$archive_errors = update_manager_archive_verify($project['name'], $local_cache, $extract_directory);
if (!empty($archive_errors)) {
foreach ($archive_errors as $key => $error) {
$this->caller->out(' - Error: ' . $error);
}
return false;
}

// Load Updater
$project_folder = $extract_directory . '/' . $project['name'];
try {
$updater = Updater::factory($project_folder);
}
catch (Exception $e) {
$this->caller->out(' - Error: ' . $e->getMessage());
return false;
}
$context = array(
'results' => array()
);

// Run Updater
update_authorize_batch_copy_project( $project['name'], get_class($updater), drupal_realpath($project_folder), new FileTransferLocal(DRUPAL_ROOT), $context );

// Verify
if( empty($context['finished']) ){
$message = isset($context['results']['log'][ $project['name'] ]) ? reset($context['results']['log'][ $project['name'] ])['message'] : 'Unknown Installer Error';
$this->caller->out(' - Error: ' . $message);
return false;
}
else {
$this->caller->out(' - ' . ucfirst($update_row->type) . ' installed successfully');
}

break;

}
/*
* TODO / Validate
if (!$this->updateAccessAllowed()) {
$this->caller->out(' - Error: Permission denied to process update.');
return false;
}
*/

}
$this->caller->out('Processing Update ID: '. $update_row->update_id);

// Type Switch
switch( $update_row->type ){

case 'core':
$this->caller->out(' - Error: Core Updates Not Supported');
break;

case 'module':
case 'theme':

// STEP 1 : Download & Extract New Package.
if ($this->doFileTransferUpdate($update_row)) {
// STEP 2 : Update database.
/*
if ($this->doDatabaseUpdate($project)) {

}
*/
}

break;

}

Expand All @@ -238,4 +218,124 @@ public function doInstallUpdate( $update_row ){

}

public function doFileTransferUpdate($update_row) {

$project = $update_row->extension_id;

// Actually try to download the file.
if (!($local_cache = update_manager_file_get($update_row->downloadurl))) {
$this->caller->out(' - Error: Failed to download ' . $project . ' from ' . $update_row->downloadurl);
return false;
}

// Extract it.
$extract_directory = _update_manager_extract_directory();
try {
update_manager_archive_extract($local_cache, $extract_directory);
}
catch (Exception $e) {
$this->caller->out(' - Error: ' . $e->getMessage());
return false;
}

$archive_errors = update_manager_archive_verify($project, $local_cache, $extract_directory);
if (!empty($archive_errors)) {
// We just need to make sure our array keys don't collide, so use the
// numeric keys from the $archive_errors array.
foreach ($archive_errors as $key => $error) {
$this->caller->out(' - Error: ' . $key . ": " . $error);
}
return false;
}

// Store maintenance_mode setting so we can restore it when done.
$maintenance_mode = variable_get('maintenance_mode', FALSE);
if ($maintenance_mode == FALSE) {
variable_set('maintenance_mode', TRUE);
}

// Make sure the Updater registry is loaded.
drupal_get_updaters();

$updates = array();
$directory = _update_manager_extract_directory();

$project_location = $directory . '/' . $project;
$updater = Updater::factory($project_location);
$project_real_location = drupal_realpath($project_location);
$updater_name = get_class($updater);
$local_url = $project_real_location;

// If the owner of the last directory we extracted is the same as the
// owner of our configuration directory (e.g. sites/default) where we're
// trying to install the code, there's no need to prompt for FTP/SSH
// credentials. Instead, we instantiate a FileTransferLocal and invoke
// update_authorize_run_update() directly.
//
// THIS PLUGIN WILL ONLY WORK IF IT IS THE SAME USER / LOCAL.
if (fileowner($project_real_location) == fileowner(conf_path())) {
module_load_include('inc', 'update', 'update.authorize');
$filetransfer = new FileTransferLocal(DRUPAL_ROOT);

// Modified version of update_authorize_run_update() without the batch process.
unset($filetransfer->connection);

$updater = new $updater_name($local_url);

try {
if ($updater->isInstalled()) {
// This is an update.
$tasks = $updater->update($filetransfer);
}
}
catch (UpdaterException $e) {
$this->caller->out(' - Error: ' . $e->getMessage());
return false;
}

$this->caller->out(' - ' . ucfirst($project) . ' installed successfully');

$offline = variable_get('maintenance_mode', FALSE);

// Now that the update completed, we need to clear the cache of available
// update data and recompute our status, so prevent show bogus results.
_update_authorize_clear_update_status();

// Take the site out of maintenance mode if it was previously that way.
if ($offline && $maintenance_mode == FALSE) {
variable_set('maintenance_mode', FALSE);
$this->caller->out(' - Your site has been taken out of maintenance mode.');
}

// File Update Completed, return True to process Database Update.
return true;
}
else {
$this->caller->out(" - Error: User doesn't have access to file transfers. FTP credentials are required.");
return false;
}
}

/**
* Copy of update_access_allowed() in update.php.
* @return bool
*/
private function updateAccessAllowed() {
global $update_free_access, $user;

// Allow the global variable in settings.php to override the access check.
if (!empty($update_free_access)) {
return TRUE;
}
// Calls to user_access() might fail during the Drupal 6 to 7 update process,
// so we fall back on requiring that the user be logged in as user #1.
try {
require_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'user') . '/user.module';
return user_access('administer software updates');
}
catch (Exception $e) {
return ($user->uid == 1);
}
}

}
Loading