MorganDawe 11 years ago
parent
commit
ad2a68a95a
  1. 1
      includes/dublin_core.inc
  2. 151
      includes/solution_packs.inc
  3. 23
      includes/tuque_wrapper.inc
  4. 142
      islandora.drush.inc
  5. 30
      tests/hooks.test
  6. 4
      tests/scripts/travis_setup.sh

1
includes/dublin_core.inc

@ -133,6 +133,7 @@ class DublinCore {
$dc_array[$field]['label'] = $element_label;
$dc_array[$field]['value'] = $value;
$dc_array[$field]['class'] = drupal_strtolower(preg_replace('/[^A-Za-z0-9]/', '-', $field));
$dc_array[$field]['dcterms'] = preg_replace('/^dc/', 'dcterms', $field);
}
}
}

151
includes/solution_packs.inc

@ -5,11 +5,55 @@
* Admin and callback functions for solution pack management.
*/
/**
* Get the information about required object.
*
* @param string $module
* An optional string, identifying a module for which to get the required
* object information.
*
* @return array
* An associative array of info describing the required objects. If $module
* is not provided (or is NULL), then we provide the info for all modules. If
* $module is provided and we have info for the given module, only the info
* for that module is provided. If $module is provided and we have no info
* for the given module, we throw an exception.
*/
function islandora_solution_packs_get_required_objects($module = NULL) {
// Should make this statically cache, after figuring out how exactly it
// should be called... We occasionally load a module and attempt to install
// it's object right away (in the same request)... This would require
// resetting of the cache. Let's just not cache for now...
$required_objects = array();
if (!$required_objects) {
$connection = islandora_get_tuque_connection();
$required_objects = module_invoke_all('islandora_required_objects', $connection);
}
if ($module !== NULL) {
if (isset($required_objects[$module])) {
return $required_objects[$module];
}
else {
watchdog('islandora', 'Attempted to get required objects for %module... %module does not appear to have any required objects. Clear caches?', array(
'%module' => $module,
));
throw new Exception(t('Module "@module" has no required objects!', array(
'@module' => $module,
)));
}
}
else {
return $required_objects;
}
}
/**
* Solution pack admin page callback.
*
* @return string
* The html repersentation of all solution pack forms for required objects.
* @return array
* Renderable array of all solution pack forms for required objects.
*/
function islandora_solution_packs_admin() {
module_load_include('inc', 'islandora', 'includes/utilities');
@ -18,17 +62,15 @@ function islandora_solution_packs_admin() {
return '';
}
$connection = islandora_get_tuque_connection();
drupal_add_css(drupal_get_path('module', 'islandora') . '/css/islandora.admin.css');
$output = '';
$enabled_solution_packs = module_invoke_all('islandora_required_objects', $connection);
$output = array();
$enabled_solution_packs = islandora_solution_packs_get_required_objects();
foreach ($enabled_solution_packs as $solution_pack_module => $solution_pack_info) {
// @todo We should probably get the title of the solution pack from the
// systems table for consistency in the interface.
$solution_pack_name = $solution_pack_info['title'];
$objects = array_filter($solution_pack_info['objects']);
$form = drupal_get_form('islandora_solution_pack_form_' . $solution_pack_module, $solution_pack_module, $solution_pack_name, $objects);
$output .= drupal_render($form);
$output[$solution_pack_module] = drupal_get_form('islandora_solution_pack_form_' . $solution_pack_module, $solution_pack_module, $solution_pack_name, $objects);
}
return $output;
}
@ -161,20 +203,38 @@ function islandora_solution_pack_form(array $form, array &$form_state, $solution
*/
function islandora_solution_pack_form_submit(array $form, array &$form_state) {
$solution_pack_module = $form_state['values']['solution_pack_module'];
$objects = $form_state['values']['objects'];
$batch = islandora_solution_pack_get_batch($solution_pack_module);
batch_set($batch);
// Hook to let solution pack objects be modified.
// Not using module_invoke so solution packs can be expanded by other modules.
// @todo shouldn't we send the object list along as well?
module_invoke_all('islandora_postprocess_solution_pack', $solution_pack_module);
}
/**
* Get the batch definition to reinstall all the objects for a given module.
*
* @param string $module
* The name of the modules of which to grab the required objects for to setup
* the batch.
*
* @return array
* An array defining a batch which can be passed on to batch_set().
*/
function islandora_solution_pack_get_batch($module) {
$batch = array(
'title' => t('Installing / Updating solution pack objects'),
'file' => drupal_get_path('module', 'islandora') . '/includes/solution_packs.inc',
'operations' => array(),
);
foreach ($objects as $object) {
$info = islandora_solution_packs_get_required_objects($module);
foreach ($info['objects'] as $object) {
$batch['operations'][] = array('islandora_solution_pack_batch_operation_reingest_object', array($object));
}
batch_set($batch);
// Hook to let solution pack objects be modified.
// Not using module_invoke so solution packs can be expanded by other modules.
// @todo shouldn't we send the object list along as well?
module_invoke_all('islandora_postprocess_solution_pack', $solution_pack_module);
return $batch;
}
/**
@ -245,14 +305,16 @@ function islandora_solution_pack_batch_operation_reingest_object(AbstractObject
* install/unistall hooks.
* @param string $op
* The operation to perform, either install or uninstall.
* @param bool $force
* Force the (un)installation of object.
*
* @todo Implement hook_modules_installed/hook_modules_uninstalled instead of
* calling this function directly.
* @todo Remove the second parameter and have two seperate functions.
*/
function islandora_install_solution_pack($module, $op = 'install') {
function islandora_install_solution_pack($module, $op = 'install', $force = FALSE) {
if ($op == 'uninstall') {
islandora_uninstall_solution_pack($module);
islandora_uninstall_solution_pack($module, $force);
return;
}
@ -267,9 +329,9 @@ function islandora_install_solution_pack($module, $op = 'install') {
'!admin_link' => $admin_link,
);
module_load_include('module', 'islandora', 'islandora');
drupal_load('module', 'islandora');
module_load_include('inc', 'islandora', 'includes/utilities');
module_load_include('module', $module, $module);
drupal_load('module', $module);
$info_file = drupal_get_path('module', $module) . "/{$module}.info";
$info_array = drupal_parse_info_file($info_file);
$module_name = $info_array['name'];
@ -279,8 +341,8 @@ function islandora_install_solution_pack($module, $op = 'install') {
return;
}
$connection = islandora_get_tuque_connection();
$required_objects = module_invoke($module, 'islandora_required_objects', $connection);
$objects = $required_objects[$module]['objects'];
$required_objects = islandora_solution_packs_get_required_objects($module);
$objects = $required_objects['objects'];
$status_messages = array(
'up_to_date' => $t('The object already exists and is up-to-date.', $t_params),
'missing_datastream' => $t('The object already exists but is missing a datastream. Please reinstall the object on the !admin_link page.', $t_params),
@ -288,11 +350,13 @@ function islandora_install_solution_pack($module, $op = 'install') {
'modified_datastream' => $t('The object already exists but datastreams are modified. Please reinstall the object on the !admin_link page.', $t_params),
);
foreach ($objects as $object) {
$query = $connection->api->a->findObjects('query', 'pid=' . $object->id);
$already_exists = !empty($query['results']);
$already_exists = islandora_object_load($object->id);
$label = $object->label;
$object_link = l($label, "islandora/object/{$object->id}");
$deleted = FALSE;
if ($already_exists) {
if (!$force) {
$object_status = islandora_check_object_status($object);
$here_params = array(
'!summary' => $t("@module: Did not install !object_link.", array(
@ -301,20 +365,38 @@ function islandora_install_solution_pack($module, $op = 'install') {
'!description' => $status_messages[$object_status['status']],
);
drupal_set_message(filter_xss(format_string('!summary !description', $here_params)), 'warning');
continue;
}
else {
$deleted = islandora_delete_object($already_exists);
}
}
if ($already_exists && $deleted || !$already_exists) {
$object = islandora_add_object($object);
if ($object) {
if ($deleted) {
drupal_set_message(filter_xss($t('@module: Successfully reinstalled. !object_link.', array(
'!object_link' => $object_link,
) + $t_params)), 'status');
}
else {
drupal_set_message(filter_xss($t('@module: Successfully installed. !object_link.', array(
'!object_link' => $object_link,
) + $t_params)), 'status');
}
}
else {
drupal_set_message($t('@module: Failed to install. @label.', array(
'@label' => $label,
) + $t_params), 'warning');
}
}
else {
drupal_set_message($t('@module: "@label" already exists and failed to be deleted.', array(
'@label' => $label,
) + $t_params), 'warning');
}
}
}
@ -323,15 +405,17 @@ function islandora_install_solution_pack($module, $op = 'install') {
*
* @param string $module
* The solution pack to uninstall.
* @param bool $force
* Force the objects to be removed.
*
* @todo Implement hook_modules_uninstalled instead of calling this function
* directly for each solution pack.
*/
function islandora_uninstall_solution_pack($module) {
function islandora_uninstall_solution_pack($module, $force = FALSE) {
$t = get_t();
module_load_include('module', 'islandora', 'islandora');
drupal_load('module', 'islandora');
module_load_include('inc', 'islandora', 'includes/utilities');
module_load_include('module', $module, $module);
drupal_load('module', $module);
$config_link = l($t('Islandora configuration'), 'admin/islandora/configure');
$info_file = drupal_get_path('module', $module) . "/{$module}.info";
$info_array = drupal_parse_info_file($info_file);
@ -345,14 +429,16 @@ function islandora_uninstall_solution_pack($module) {
return;
}
$connection = islandora_get_tuque_connection();
$required_objects = module_invoke($module, 'islandora_required_objects', $connection);
$objects = $required_objects[$module]['objects'];
$required_objects = islandora_solution_packs_get_required_objects($module);
$objects = $required_objects['objects'];
$filter = function($o) use($connection) {
$param = "pid={$o->id}";
$query = $connection->api->a->findObjects('query', $param);
return !empty($query['results']);
};
$existing_objects = array_filter($objects, $filter);
if (!$force) {
foreach ($existing_objects as $object) {
$object_link = l($object->label, "islandora/object/{$object->id}");
$msg = $t('@module: Did not remove !object_link. It may be used by other sites.', array(
@ -362,6 +448,17 @@ function islandora_uninstall_solution_pack($module) {
drupal_set_message(filter_xss($msg), 'warning');
}
}
else {
foreach ($existing_objects as $object) {
$params = array(
'@id' => $object->id,
'@module' => $module_name,
);
islandora_delete_object($object);
drupal_set_message($t('@module: Deleted @id.', $params));
}
}
}
/**

23
includes/tuque_wrapper.inc

@ -82,13 +82,26 @@ class IslandoraFedoraRepository extends FedoraRepository {
* @see FedoraRepository::ingestObject()
*/
public function ingestObject(NewFedoraObject &$object) {
$context = array(
try {
foreach ($object as $dsid => $datastream) {
$datastream_context = array(
'action' => 'ingest',
'block' => FALSE,
);
islandora_alter_object($object, $context);
try {
if ($context['block']) {
islandora_alter_datastream($object, $datastream, $datastream_context);
if ($datastream_context['block']) {
throw new Exception(t('Object ingest blocked due to ingest of @dsid being blocked.', array(
'@dsid' => $dsid,
)));
}
}
$object_context = array(
'action' => 'ingest',
'block' => FALSE,
);
islandora_alter_object($object, $object_context);
if ($object_context['block']) {
throw new Exception('Ingest Object was blocked.');
}
$ret = parent::ingestObject($object);
@ -269,7 +282,6 @@ class IslandoraFedoraApiM extends FedoraApiM {
switch ($action) {
case 'block':
throw new Exception('Purge Datastream was blocked.');
break;
case 'delete':
$object[$dsid]->state = 'D';
@ -313,7 +325,6 @@ class IslandoraFedoraApiM extends FedoraApiM {
switch ($action) {
case 'block':
throw new Exception('Purge object was blocked.');
break;
case 'delete':
$object->state = 'D';

142
islandora.drush.inc

@ -0,0 +1,142 @@
<?php
/**
* @file
* Drush command/hook implementation.
*/
/**
* Implements hook_drush_command().
*/
function islandora_drush_command() {
$commands = array();
$commands['islandora-solution-pack-install-required-objects'] = array(
'description' => dt('Install Solution Pack objects.'),
'options' => array(
'module' => array(
'description' => dt('The module for which to install the required objects.'),
'required' => TRUE,
),
'force' => array(
'description' => dt('Force reinstallation of the objects.'),
),
),
'aliases' => array('ispiro'),
'drupal dependencies' => array(
'islandora',
),
'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_LOGIN,
);
$commands['islandora-solution-pack-uninstall-required-objects'] = array(
'description' => dt('Uninstall Solution Pack objects.'),
'options' => array(
'module' => array(
'description' => dt('The module for which to uninstall the required objects.'),
'required' => TRUE,
),
'force' => array(
'description' => dt('Force reinstallation of the objects.'),
),
),
'aliases' => array('ispuro'),
'drupal dependencies' => array(
'islandora',
),
'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_LOGIN,
);
$commands['islandora-solution-pack-required-objects-status'] = array(
'description' => dt('Get Solution Pack object status.'),
'options' => array(
'module' => array(
'description' => dt('The module for which to get the status of the required objects.'),
),
),
'aliases' => array('ispros'),
'drupal dependencies' => array(
'islandora',
),
'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_LOGIN,
);
return $commands;
}
/**
* Command callback to install required objects.
*/
function drush_islandora_solution_pack_install_required_objects() {
module_load_include('inc', 'islandora', 'includes/solution_packs');
$module = drush_get_option('module');
if (module_exists($module)) {
islandora_install_solution_pack(
$module,
'install',
drush_get_option('force', FALSE)
);
}
else {
drush_log(dt('"@module" is not installed/enabled?...', array(
'@module' => $module,
)));
}
}
/**
* Command callback to uninstall required objects.
*/
function drush_islandora_solution_pack_uninstall_required_objects() {
module_load_include('inc', 'islandora', 'includes/solution_packs');
$module = drush_get_option('module');
if (module_exists($module)) {
islandora_uninstall_solution_pack(
$module,
drush_get_option('force', FALSE)
);
}
else {
drush_log(dt('"@module" is not installed/enabled?...', array(
'@module' => $module,
)));
}
}
/**
* Command callback for object status.
*/
function drush_islandora_solution_pack_required_objects_status() {
module_load_include('inc', 'islandora', 'includes/solution_packs');
$module = drush_get_option('module', FALSE);
$required_objects = array();
if ($module && module_exists($module)) {
$required_objects[$module] = islandora_solution_packs_get_required_objects($module);
}
elseif ($module === FALSE) {
$required_objects = islandora_solution_packs_get_required_objects();
}
else {
drush_log(dt('"@module" is not installed/enabled?...', array(
'@module' => $module,
)));
return;
}
$header = array('PID', 'Machine Status', 'Readable Status');
$widths = array(30, 20, 20);
foreach ($required_objects as $module => $info) {
$rows = array();
drush_print($info['title']);
foreach ($info['objects'] as $object) {
$status = islandora_check_object_status($object);
$rows[] = array(
$object->id,
$status['status'],
$status['status_friendly'],
);
}
drush_print_table($rows, $header, $widths);
}
}

30
tests/hooks.test

@ -75,6 +75,8 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase {
/**
* Test ALL THE HOOKS!.
*
* Covers the majority of cases...
*/
public function testHooks() {
// Test ingesting with FedoraRepository::ingestObject().
@ -85,6 +87,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase {
$this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_INGESTED_HOOK], 'Called "hook_islandora_object_alter" when ingesting via FedoraRepository::ingestObject.');
$this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_INGESTED_HOOK], 'Called ISLANDORA_OBJECT_INGESTED_HOOK when ingesting via FedoraRepository::ingestObject.');
$this->repository->purgeObject($object->id);
// Test blocking the ingest.
$object = $this->repository->constructObject('test:testIngestedObjectHook');
$object->label = 'block';
@ -100,6 +103,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase {
$this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_INGESTED_HOOK], 'Called "hook_islandora_object_alter" when blocking ingesting via FedoraRepository::ingestObject.');
$this->assertFalse($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_INGESTED_HOOK], 'Did not called ISLANDORA_OBJECT_INGESTED_HOOK when blocking ingesting via FedoraRepository::ingestObject.');
}
// Test modifying via set magic functions.
$object = $this->repository->constructObject('test:testModifiedObjectHook');
$this->repository->ingestObject($object);
@ -108,6 +112,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase {
$object->label = "New Label!";
$this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_MODIFIED_HOOK], 'Called "hook_islandora_object_alter" when modifying via set magic functions.');
$this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_MODIFIED_HOOK], 'Called ISLANDORA_OBJECT_MODIFIED_HOOK when modifying via set magic functions.');
// Test blocking the modification.
try {
$_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_MODIFIED_HOOK] = FALSE;
@ -122,6 +127,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase {
$this->assertFALSE($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_MODIFIED_HOOK], 'Called ISLANDORA_OBJECT_MODIFIED_HOOK when blocking modifying via set magic functions.');
}
$this->repository->purgeObject($object->id);
// Test purging with FedoraRepository::purgeObject().
$object = $this->repository->constructObject('test:testPurgedObjectHook');
$this->repository->ingestObject($object);
@ -130,6 +136,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase {
$this->repository->purgeObject($object->id);
$this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called "hook_islandora_object_alter" when purging via FedoraRepository::purgeObject.');
$this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called ISLANDORA_OBJECT_PURGED_HOOK when purging via FedoraRepository::purgeObject.');
// Test deleting.
$object = $this->repository->constructObject('test:testPurgedObjectHook');
$this->repository->ingestObject($object);
@ -139,6 +146,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase {
$this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called "hook_islandora_object_alter" when deleting via FedoraObject::delete.');
$this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called ISLANDORA_OBJECT_PURGED_HOOK when purging via FedoraObject::delete.');
$this->repository->purgeObject($object->id);
// Test alter blocking.
$object = $this->repository->constructObject('test:testPurgedObjectHook');
$this->repository->ingestObject($object);
@ -154,6 +162,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase {
$this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called "hook_islandora_object_alter" when blocking purge via FedoraRepository::purgeObject.');
$this->assertFalse($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called ISLANDORA_OBJECT_PURGED_HOOK when blocking purge via FedoraRepository::purgeObject.');
}
// Test alter delete.
$_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_PURGED_HOOK] = FALSE;
$_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_PURGED_HOOK] = FALSE;
@ -164,6 +173,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase {
$this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called ISLANDORA_OBJECT_PURGED_HOOK when preventing purge and deleting.');
$object->label = 'Something other than delete';
$this->repository->purgeObject($object->id);
// Test ingesting with FedoraRepository::ingestObject().
$object = $this->repository->constructObject('test:testIngestedDatastreamHook');
$this->repository->ingestObject($object);
@ -174,6 +184,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase {
$this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_INGESTED_HOOK], 'Called "hook_islandora_datastream_alter" when ingesting via FedoraObject::ingestDatastream.');
$this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_INGESTED_HOOK], 'Called ISLANDORA_DATASTREAM_INGESTED_HOOK when ingesting via FedoraObject::ingestDatastream.');
$this->repository->purgeObject($object->id);
// Test modifying a datastream.
$object = $this->repository->constructObject('test:testModifiedDatastreamHook');
$this->repository->ingestObject($object);
@ -184,6 +195,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase {
$ds->label = "New Label!";
$this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_MODIFIED_HOOK], 'Called "hook_islandora_datastream_alter" when modifying via set magic functions.');
$this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_MODIFIED_HOOK], 'Called ISLANDORA_DATASTREAM_MODIFIED_HOOK when modifying via set magic functions.');
// Test blocking modifying.
try {
$_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_MODIFIED_HOOK] = FALSE;
@ -197,6 +209,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase {
$this->assertFALSE($_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_MODIFIED_HOOK], 'Called ISLANDORA_DATASTREAM_MODIFIED_HOOK when blocking modifying via set magic functions.');
}
$this->repository->purgeObject($object->id);
// Test purging with FedoraRepository::purgeObject().
$object = $this->repository->constructObject('test:testPurgedDatastreamHook');
$this->repository->ingestObject($object);
@ -210,4 +223,21 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase {
$this->repository->purgeObject($object->id);
}
/**
* Test ALL THE HOOKS!.
*
* Ensure hooks are triggered properly in "New" objects.
*/
public function testNewIngestHooks() {
// Test ingesting with FedoraRepository::ingestObject().
$object = $this->repository->constructObject('test:testIngestedDatastreamHook');
$ds = $object->constructDatastream('TEST');
$_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_INGESTED_HOOK] = FALSE;
$_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_INGESTED_HOOK] = FALSE;
$object->ingestDatastream($ds);
$this->repository->ingestObject($object);
$this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_INGESTED_HOOK], 'Called "hook_islandora_datastream_alter" when ingesting via FedoraObject::ingestDatastream.');
$this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_INGESTED_HOOK], 'Called ISLANDORA_DATASTREAM_INGESTED_HOOK when ingesting via FedoraObject::ingestDatastream.');
$this->repository->purgeObject($object->id);
}
}

4
tests/scripts/travis_setup.sh

@ -11,8 +11,8 @@ cd islandora_tomcat
export CATALINA_HOME='.'
./bin/startup.sh
cd $HOME
pear upgrade force Console_Getopt
pear upgrade force pear
pear upgrade --force Console_Getopt
pear upgrade --force pear
pear upgrade-all
pear channel-discover pear.drush.org
pear channel-discover pear.drush.org

Loading…
Cancel
Save