Merge branch 'master-upstream' into master-1005480 master-1005480
authorHerman van Rink
Tue, 31 Jan 2012 10:08:46 +0000 (11:08 +0100)
committerHerman van Rink
Tue, 31 Jan 2012 10:08:46 +0000 (11:08 +0100)
103 files changed:
.travis.yml
README.txt
commands/core/archive.drush.inc
commands/core/cache.drush.inc
commands/core/core.drush.inc
commands/core/docs.drush.inc
commands/core/drupal/environment.inc
commands/core/drupal/environment_6.inc
commands/core/drupal/site_install.inc
commands/core/drupal/site_install_6.inc
commands/core/drupal/update_6.inc
commands/core/drupal/update_7.inc
commands/core/field.drush.inc
commands/core/help.drush.inc
commands/core/queue.drush.inc [deleted file]
commands/core/rsync.core.inc
commands/core/site_install.drush.inc
commands/core/ssh.drush.inc
commands/core/test.drush.inc
commands/core/usage.drush.inc
commands/core/variable.drush.inc
commands/core/watchdog.drush.inc
commands/entity/entity.drush.inc [new file with mode: 0644]
commands/make/EXAMPLE.make [moved from examples/example.make with 98% similarity]
commands/make/README.txt [moved from docs/make.txt with 90% similarity]
commands/make/generate.make.inc
commands/make/make.download.inc
commands/make/make.drush.inc
commands/make/make.project.inc
commands/make/make.utilities.inc
commands/pm/download.pm.inc
commands/pm/info.pm.inc
commands/pm/package_handler/git_drupalorg.inc
commands/pm/package_handler/wget.inc
commands/pm/pm.drush.inc
commands/pm/release_info/updatexml.inc
commands/pm/update_info/drupal.inc
commands/pm/update_info/drupal_6.inc
commands/pm/updatecode.pm.inc
commands/runserver/runserver-drupal.inc
commands/runserver/runserver-prepend.php
commands/runserver/runserver.drush.inc
commands/sql/sql.drush.inc
commands/sql/sync.sql.inc
commands/user/user.drush.inc
docs/commands.html
docs/drush.api.php
docs/strict-options.html [deleted file]
drush.bat
drush.info
drush.php
examples/example.aliases.drushrc.php
examples/example.bashrc
examples/example.drushrc.php
examples/policy.drush.inc
examples/sandwich.drush.inc
examples/sync_enable.drush.inc [deleted file]
examples/sync_via_http.drush.inc [deleted file]
includes/backend.inc
includes/bootstrap.inc
includes/cache.inc
includes/command.inc
includes/complete.inc
includes/context.inc
includes/dbtng.inc
includes/drupal.inc
includes/drush.inc
includes/environment.inc
includes/exec.inc
includes/filesystem.inc
includes/output.inc
includes/sitealias.inc
tests/COVERAGE.txt
tests/archiveDumpTest.php
tests/backendTest.php
tests/commandSpecificTest.php [deleted file]
tests/commandTest.php
tests/completeTest.php
tests/contextTest.php
tests/coreTest.php
tests/drush_testcase.inc
tests/entityTest.php [new file with mode: 0644]
tests/filesystemTest.php [deleted file]
tests/makeTest.php
tests/makefiles/file-extract.make [deleted file]
tests/makefiles/file.make
tests/makefiles/get.make
tests/makefiles/git.make
tests/makefiles/limited-projects-libraries.make [deleted file]
tests/makefiles/recursion.make
tests/makefiles/subtree.make [deleted file]
tests/makefiles/translations-inside7.make [deleted file]
tests/phpunit.xml.dist
tests/queueTest.php [deleted file]
tests/releaseInfoTest.php
tests/resources/create_node_types.php [deleted file]
tests/resources/queue_script.php [deleted file]
tests/shellAliasTest.php
tests/siteSshTest.php
tests/sqlConnectTest.php [deleted file]
tests/sqlSyncTest.php
tests/unit.drush.inc
tests/userTest.php

index d18b645..3f1d254 100644 (file)
@@ -4,12 +4,10 @@ php:
   - 5.3
   - 5.4
 env:
-  - UNISH_DB_URL=mysql://root:@127.0.0.1 PHPUNIT_ARGS=--exclude-group=make
-  - UNISH_DB_URL=mysql://root:@127.0.0.1 PHPUNIT_ARGS=--group=make
-  - UNISH_DB_URL=pgsql://postgres:@127.0.0.1 PHPUNIT_ARGS=--exclude-group=make
-  - UNISH_DB_URL=pgsql://postgres:@127.0.0.1 PHPUNIT_ARGS=--group=make
+  - UNISH_DB_URL=mysql://root:@127.0.0.1
+  - UNISH_DB_URL=pgsql://postgres:@127.0.0.1
   - UNISH_DB_URL=sqlite://none/of/this/matters
 before_script:
   - export UNISH_DRUSH="${PWD}/drush" && cd tests
-script: phpunit $PHPUNIT_ARGS
+script: phpunit --verbose
 
index 9712a51..545db85 100644 (file)
@@ -22,7 +22,7 @@ REQUIREMENTS
 
 * Drush is designed for a Unix-like OS (Linux, OS X)
 
-* Most Drush commands run on Windows.  See INSTALLING DRUSH ON WINDOWS, below.
+* Some Drush commands run on Windows.  See INSTALLING DRUSH ON WINDOWS, below.
 
 * Drush works with Drupal 6, Drupal 7, and usually Drupal 8.
 
@@ -83,11 +83,10 @@ below.
    and usage-send commands to more carefully send data.
 
 6. Optional. See examples/example.bashrc for instructions on how to add some
-   useful shell aliases that provides even tighter integration between
-   drush and bash. You may source this file directly into your shell by adding to
-   your .bashrc (rr equivalent): source /path/to/drush/examples/example.bashrc
+   very useful shell aliases that provides even tighter integration between
+   drush and bash.
 
-7. Optional. If you didn't source it in Step 6 above, see top of
+7. Optional. If you didn't source it in Step 5 above, see top of
    drush.complete.sh file for instructions adding bash completion for drush
    command to your shell.  Once configured, completion works for site aliases,
    command names, shell aliases, global options, and command-specific options.
@@ -197,7 +196,7 @@ disable_classes are empty.
 INSTALLING DRUSH ON WINDOWS:
 ----------------------------
 
-Windows support has improved, but is still lagging. Consider using on
+Windows support is improving, but is still lacking! Consider using on
 Linux/Unix/OSX using Virtualbox or other virtual machine.
 
 There is a Windows msi installer for drush available at:
@@ -343,4 +342,4 @@ CREDITS
 * Originally developed by Arto Bendiken <http://bendiken.net/> for Drupal 4.7.
 * Redesigned by Franz Heinzmann (frando) <http://unbiskant.org/> in May 2007 for Drupal 5.
 * Maintained by Moshe Weitzman <http://drupal.org/moshe> with much help from
-  Owen Barton, greg.1.anderson, jonhattan, Mark Sonnabaum, and Jonathan Hedstrom.
+  Owen Barton, Adrian Rossouw, greg.1.anderson, jonhattan, Jonathan Hedstrom.
index c22962a..9e3c74c 100644 (file)
@@ -97,11 +97,9 @@ function drush_archive_dump($sites_subdirs = '@self') {
   // Create the unique temporary name.
   $date = gmdate('Ymd_his');
   $prefix = 'none';
-  if (!empty($full)) {
+  if (!empty($full) && !empty($first['databases']['default']['default']['database'])) {
     $first = current($full);
-    if ( !empty($first['databases']['default']['default']['database']) ) {
-      $prefix = count($full) > 1 ? 'multiple_sites' : str_replace('/', '-', $first['databases']['default']['default']['database']);
-    }
+    $prefix = count($sites_subdirs) > 1 ? 'multiple_sites' : str_replace('/', '-', $first['databases']['default']['default']['database']);
   }
   $temp_dest_name = "$prefix.$date.tar";
 
@@ -168,10 +166,6 @@ function drush_archive_dump($sites_subdirs = '@self') {
   drush_shell_cd_and_exec($workdir, "$tar --exclude \"{$docroot}/sites\" {$dereference}-cf %s %s", $destination, $docroot);
   // Add sites/all to the same archive.
   drush_shell_cd_and_exec($workdir, "$tar {$dereference}-rf %s %s", $destination, "{$docroot}/sites/all");
-  // Add sites/sites.php to the same archive.
-  if (file_exists("{$docroot}/sites/sites.php")) {
-    drush_shell_cd_and_exec($workdir, "$tar {$dereference}-rf %s %s", $destination, "{$docroot}/sites/sites.php");
-  }
 
   $tmp = drush_tempdir();
   $all_dbs = array();
@@ -190,9 +184,7 @@ function drush_archive_dump($sites_subdirs = '@self') {
       );
       $table_selection = drush_sql_get_table_selection();
       list($dump_exec, $dump_file) = drush_sql_build_dump_command($table_selection, $db, $result_file);
-      if (!drush_shell_exec($dump_exec)) {
-        return drush_set_error('DRUSH_SQL_DUMP_FAIL', 'Database dump failed.');
-      }
+      drush_shell_exec($dump_exec);
       drush_shell_cd_and_exec($tmp, "$tar --dereference -rf %s %s", $destination, basename($result_file));
     }
   }
@@ -244,13 +236,7 @@ function drush_archive_dump($sites_subdirs = '@self') {
 
   // Ensure that default/default.settings.php is in the archive. This is needed
   // by site-install when restoring a site without any DB.
-  // NOTE: Windows tar file replace operation is broken so we have to check if file already exists.
-  // Otherwise it will corrupt the archive.
-  $res = drush_shell_cd_and_exec($workdir, "$tar -tzf %s %s", $destination, $docroot . '/sites/default/default.settings.php');
-  $output = drush_shell_exec_output();
-  if (!$res || !isset($output[0]) || empty($output[0])) {
-    drush_shell_cd_and_exec($workdir, "$tar --dereference -vrf %s %s", $destination, $docroot . '/sites/default/default.settings.php');
-  }
+  drush_shell_cd_and_exec($workdir, "$tar --dereference -vrf %s %s", $destination, $docroot . '/sites/default/default.settings.php');
 
   // Compress the archive
   drush_shell_exec("gzip --no-name -f %s", $destination);
index ce6fcc9..5848301 100644 (file)
@@ -77,29 +77,14 @@ function drush_cache_command_clear($type = NULL) {
 
   if ($type) {
     drush_op($types[$type]);
-    if ($type == 'all' && !drush_has_boostrapped(DRUSH_BOOTSTRAP_DRUPAL_FULL)) {
-      $type = 'drush';
-    }
+    drush_log(dt("'!name' cache was cleared", array('!name' => $type)), 'success');
   }
   else {
-    // Don't offer 'all' unless Drush has bootstrapped the Drupal site
-    if (!drush_has_boostrapped(DRUSH_BOOTSTRAP_DRUPAL_FULL)) {
-      unset($types['all']);
-    }
-    $type = drush_choice($types, 'Enter a number to choose which cache to clear.', '!key');
-    if ($type !== FALSE) {
-      call_user_func($types[$type]);
-    }
-  }
-  if ($type !== FALSE) {
-    $site_label = '';
-    if ($type != 'drush') {
-      $self_name = drush_sitealias_bootstrapped_site_name();
-      if (isset($self_name)) {
-        $site_label = dt(' in !name', array('!name' => $self_name));
-      }
+    $choice = drush_choice($types, 'Enter a number to choose which cache to clear.', '!key');
+    if ($choice !== FALSE) {
+      call_user_func($types[$choice]);
+      drush_log(dt("'!name' cache was cleared", array('!name' => $choice)), 'success');
     }
-    drush_log(dt("'!name' cache was cleared!insitename", array('!name' => $type, '!insitename' => $site_label)), 'success');
   }
 }
 
index 02c2868..05276c3 100644 (file)
@@ -26,11 +26,7 @@ function core_drush_command() {
     'bootstrap' => DRUSH_BOOTSTRAP_DRUSH, // No bootstrap.
     'options' => array(
       'sort' => 'Sort commands in alphabetical order. drush waits for full bootstrap before printing any commands when this option is used.',
-      'filter' => array(
-        'description' => 'Restrict command list to those commands defined in the specified file. Omit value to choose from a list of names.',
-        'example-value' => 'category',
-        'value' => 'optional',
-      ),
+      'filter' => 'Restrict command list to those commands defined in the specified file.',
       'html' => 'Print help for all commands in HTML format.',
       'pipe' => 'A list of available commands, one per line.',
     ),
@@ -109,11 +105,7 @@ function core_drush_command() {
       'drush core-requirements --pipe' => 'Print out a short report in the format "identifier: severity", where severity 2=error, 1=warning, and 0/-1=OK',
     ),
     'options' => array(
-      'severity' => array(
-        'description' => 'Only show status report messages with a severity greater than or equal to the specified value.',
-        'value' => 'required',
-        'example-value' => '3',
-      ),
+      'severity' => 'Only show status report messages with a severity greater than or equal to the specified value.',
       'ignore' => 'Comma-separated list of requirements to remove from output. Run with --pipe to see key values to use.',
     ),
   );
@@ -143,10 +135,7 @@ function core_drush_command() {
       'filename' => 'Optional. The file you wish to execute (without extension). If omitted, list files ending in .php in the current working directory and specified script-path. Some might not be real drush scripts. Beware.',
     ),
     'options' => array(
-      'script-path' => array(
-        'description' => "Additional paths to search for scripts, separated by : (Unix-based systems) or ; (Windows).",
-        'example-value' => '~/scripts',
-      ),
+      'script-path' => "Additional paths to search for scripts, separated by : (Unix-based systems) or ; (Windows).",
     ),
     'allow-additional-options' => TRUE,
     'bootstrap' => DRUSH_BOOTSTRAP_MAX,
@@ -163,7 +152,7 @@ function core_drush_command() {
     'required-arguments' => TRUE,
     'allow-additional-options' => TRUE,
     'handle-remote-commands' => TRUE,
-    'strict-option-handling' => TRUE,
+    'mask-local-cli-options' => TRUE,
     'examples' => array(
       'drush core-execute git pull origin rebase' => 'Retrieve latest code from git',
     ),
@@ -178,7 +167,7 @@ function core_drush_command() {
       'destination' => 'May be rsync path or site alias. See rsync documentation and example.aliases.drushrc.php.',
     ),
     'options' => array(
-      'mode' => 'The unary flags to pass to rsync; --mode=rultz implies rsync -rultz.  Default is -akz.',
+      'mode' => 'The unary flags to pass to rsync; --mode=rultz implies rsync -rultz.  Default is -az.',
       'RSYNC-FLAG' => 'Most rsync flags passed to drush sync will be passed on to rsync.  See rsync documentation.',
       'exclude-conf' => 'Excludes settings.php from being rsynced.  Default.',
       'include-conf' => 'Allow settings.php to be rsynced. Default is to exclude settings.php.',
@@ -189,7 +178,7 @@ function core_drush_command() {
       'exclude-paths' => 'List of paths to exclude, seperated by : (Unix-based systems) or ; (Windows).',
       'include-paths' => 'List of paths to include, seperated by : (Unix-based systems) or ; (Windows).',
     ),
-    'strict-option-handling' => TRUE,
+    'allow-additional-options' => TRUE,
     'examples' => array(
       'drush rsync @dev @stage' => 'Rsync Drupal root from dev to stage (one of which must be local).',
       'drush rsync ./ @stage:%files/img' => 'Rsync all files in the current directory to the \'img\' directory in the file storage folder on stage.',
@@ -204,28 +193,18 @@ function core_drush_command() {
       'key=value...' => 'any additional settings you wish to pass to the profile. Fully supported on D7+, partially supported on D6 (single step configure forms only). The key is in the form [form name].[parameter name] on D7 or just [parameter name] on D6.',
     ),
     'options' => array(
-      'db-url' => array(
-        'description' => 'A Drupal 6 style database URL. Only required for initial install - not re-install.',
-        'example-value' => 'mysql://root:pass@127.0.0.1/db',
-        ),
+      'db-url' => 'A Drupal 6 style database URL. Only required for initial install - not re-install.',
       'db-prefix' => 'An optional table prefix to use for initial install.  Can be a key-value array of tables/prefixes in a drushrc file (not the command line).',
       'db-su' => 'Account to use when creating a new database. Must have Grant permission (mysql only). Optional.',
       'db-su-pw' => 'Password for the "db-su" account. Optional.',
       'account-name' => 'uid1 name. Defaults to admin',
       'account-pass' => 'uid1 pass. Defaults to a randomly generated password. If desired, set a fixed password in drushrc.php.',
       'account-mail' => 'uid1 email. Defaults to admin@example.com',
-      'locale' => array(
-        'description' => 'A short language code. Sets the default site language. Language files must already be present. You may use download command to get them.',
-        'example-value' => 'en-GB',
-      ),
+      'locale' => 'A short language code. Sets the default site language. Language files must already be present. You may use download command to get them.',
       'clean-url'=> 'Defaults to 1',
       'site-name' => 'Defaults to Site-Install',
       'site-mail' => 'From: for system mailings. Defaults to admin@example.com',
-      'sites-subdir' => array(
-        'description' => "Name of directory under 'sites' which should be created. Only needed when the subdirectory does not already exist. Defaults to 'default'",
-        'value' => 'required',
-        'example-value' => 'directory_name',
-      ),
+      'sites-subdir' => "Name of directory under 'sites' which should be created. Only needed when the subdirectory does not already exist. Defaults to 'default'",
     ),
     'examples' => array(
       'drush site-install expert --locale=uk' => '(Re)install using the expert install profile. Set default language to Ukranian.',
@@ -292,7 +271,7 @@ function core_drush_command() {
     ),
     'examples' => array(
       'drush qd' => 'Download and install stable release of Drupal into a timestamped directory, start server, and open the site logged in as admin.',
-      'drush qd --profile=minimal --dev --cache --core=drupal-8.x --yes' => 'Fire up dev release of Drupal site with minimal install profile.',
+      'drush qd --profile=minimal --dev --cache --default-major=8 --yes' => 'Fire up dev release of Drupal site with minimal install profile.',
       'drush qd testsite devel --server=:8081/admin --browser=firefox --cache --yes' => 'Fire up stable release (using the cache) of Drupal site called "testsite", download and enable devel module, start a server on port 8081 and open /admin in firefox.',
       'drush qd commercesite --core=commerce_kickstart --profile=commerce_kickstart --cache --yes --watchdog' => 'Download and install the "Commerce Kickstart" distribution/install profile, display watchdog messages on the server console.',
     ),
@@ -552,7 +531,7 @@ function _core_site_status_table($project = '', $full = FALSE) {
       $status_table['Drush alias files'] = dt("There are !count alias files. Run with --full to see the full list.", array('!count' => count($alias_files)));
     }
   }
-
+  
   // None of the Status keys are in dt(); this helps with machine-parsing of status?
   $path_names['root'] = 'Drupal root';
   $path_names['site'] = 'Site path';
@@ -769,8 +748,6 @@ function drush_core_global_options() {
   $fake = drush_global_options_command(FALSE);
   $global_option_rows = drush_format_help_section($fake, 'options');
   drush_print_table($global_option_rows);
-  drush_print();
-  drush_print("See also: `drush topic docs-strict-options`");
 }
 
 function _drush_core_is_named_in_array($key, $the_array) {
@@ -882,9 +859,6 @@ function drush_core_quick_drupal_options(&$items) {
   );
   $pm = pm_drush_command();
   foreach ($pm['pm-download']['options'] as $option => $description) {
-    if (is_array($description)) {
-      $description = $description['description'];
-    }
     $options[$option] = 'Download option: ' . $description;
   }
   // Unset a few options that are not usable here, as we control them ourselves
@@ -894,9 +868,6 @@ function drush_core_quick_drupal_options(&$items) {
   unset($options['default-major']);
   unset($options['use-site-dir']);
   foreach ($items['site-install']['options'] as $option => $description) {
-    if (is_array($description)) {
-      $description = $description['description'];
-    }
     $options[$option] = 'Site install option: ' . $description;
   }
   unset($options['sites-subdir']);
@@ -1100,11 +1071,12 @@ function drush_core_drupal_directory($target = 'root') {
   // any keys are -not- replaced in _drush_core_directory.
   if ($path && (strpos($path, '%') === FALSE)) {
     drush_print($path);
-    return $path;
   }
   else {
     return drush_set_error('DRUSH_TARGET_NOT_FOUND', dt("Target '!target' not found.", array('!target' => $target)));
   }
+
+  return TRUE;
 }
 
 /**
@@ -1136,8 +1108,8 @@ function drush_core_find_project_path($target) {
  * that start with !.
  */
 function drush_core_execute() {
-  // Get all of the args and options that appear after the command name.
-  $args = drush_get_original_cli_args_and_options();
+  // The DRUSH_COMMAND_ARGS contain all args and options that appear after the command name.
+  $args = drush_get_context('DRUSH_COMMAND_ARGS', array());
   for ($x = 0; $x < sizeof($args); $x++) {
     // escape all args except for command separators.
     if (!in_array($args[$x], array('&&', '||', ';'))) {
index 91e12dd..aad949b 100644 (file)
@@ -159,22 +159,6 @@ function docs_drush_command() {
     'callback' => 'drush_print_file',
     'callback arguments' => array($docs_dir . '/examples/sandwich.drush.inc'),
   );
-  $items['docs-example-sync-extension'] = array(
-    'description' => 'Example Drush commandfile that extends sql-sync to enable development modules in the post-sync hook.',
-    'hidden' => TRUE,
-    'topic' => TRUE,
-    'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
-    'callback' => 'drush_print_file',
-    'callback arguments' => array($docs_dir . '/examples/sync_enable.drush.inc'),
-  );
-  $items['docs-example-sync-via-http'] = array(
-    'description' => 'Example Drush commandfile that extends sql-sync to allow transfer of the sql dump file via http rather than ssh and rsync.',
-    'hidden' => TRUE,
-    'topic' => TRUE,
-    'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
-    'callback' => 'drush_print_file',
-    'callback arguments' => array($docs_dir . '/examples/sync_via_http.drush.inc'),
-  );
   $items['docs-policy'] = array(
     'description' => 'Example policy file.',
     'hidden' => TRUE,
@@ -183,14 +167,6 @@ function docs_drush_command() {
     'callback' => 'drush_print_file',
     'callback arguments' => array($docs_dir . '/examples/policy.drush.inc'),
   );
-  $items['docs-strict-options'] = array(
-    'description' => 'Strict option handling, and how commands that use it differ from regular Drush commands.',
-    'hidden' => TRUE,
-    'topic' => TRUE,
-    'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
-    'callback' => 'drush_print_file',
-    'callback arguments' => array($docs_dir . '/docs/strict-options.html'),
-  );
   return $items;
 }
 
index a74549f..bb0371e 100644 (file)
  * @return
  *   An array containing module info for all available modules.
  */
-function drush_get_modules($include_hidden = TRUE) {
-  $modules = system_rebuild_module_data();
-  if (!$include_hidden) {
-    foreach ($modules as $key => $module) {
-      if (isset($module->info['hidden'])) {
-        unset($modules[$key]);
-      }
-    }
-  }
-
-  return $modules;
+function drush_get_modules() {
+  return system_rebuild_module_data();
 }
 
 /**
@@ -138,22 +129,30 @@ function drush_module_uninstall($modules) {
 }
 
 /**
+ * Submit the system modules form.
+ *
+ * The modules should already be fully enabled/disabled before calling this
+ * function. Calling this function just makes sure any activities triggered by
+ * the form submit (such as admin_role) are completed.
+ */
+function drush_system_modules_form_submit($active_modules) {
+  module_load_include('inc', 'system', 'system.admin');
+  $form_state = array('values' => array('status' => $active_modules));
+  drupal_form_submit('system_modules', $form_state);
+  // Because normally system_modules_submit would call this function if modules
+  // had been changed, in this case we are submitting the module form without
+  // any changes, so we need to clear caches manually.
+  drupal_flush_all_caches();
+}
+
+/**
  * Get complete information for all available themes.
  *
  * @return
  *   An array containing theme info for all available themes.
  */
-function drush_get_themes($include_hidden = TRUE) {
-  $themes = system_rebuild_theme_data();
-  if (!$include_hidden) {
-    foreach ($themes as $key => $theme) {
-      if (isset($theme->info['hidden'])) {
-        unset($themes[$key]);
-      }
-    }
-  }
-
-  return $themes;
+function drush_get_themes() {
+  return system_rebuild_theme_data();
 }
 
 /**
index ee0d10a..11bb58d 100644 (file)
  * @return
  *   An array containing module info for all available modules.
  */
-function drush_get_modules($include_hidden = TRUE) {
+function drush_get_modules() {
   $modules = module_rebuild_cache();
-  foreach ($modules as $key => $module) {
+  foreach ($modules as $module) {
     if (!isset($module->type)) {
       $module->type = 'module';
     }
-    if ((!$include_hidden) && $module->info['hidden']) {
-      unset($modules[$key]);
-    }
   }
 
   return $modules;
@@ -102,8 +99,6 @@ function drush_module_enable($modules) {
     _drupal_install_module($module);
   }
   module_enable($modules);
-  $modules = drush_map_assoc(array_keys(drush_get_modules(FALSE)));
-  drush_system_modules_form_submit($modules);
 }
 
 /**
@@ -114,8 +109,6 @@ function drush_module_enable($modules) {
  */
 function drush_module_disable($modules) {
   module_disable($modules, FALSE);
-  $modules = drush_map_assoc(array_keys(drush_get_modules(FALSE)));
-  drush_system_modules_form_submit($modules);
 }
 
 /**
@@ -154,13 +147,10 @@ function drush_system_modules_form_submit($active_modules) {
  */
 function drush_get_themes() {
   $themes = system_theme_data();
-  foreach ($themes as $key => $theme) {
+  foreach ($themes as $theme) {
     if (!isset($theme->type)) {
       $theme->type = 'theme';
     }
-    if ((!$include_hidden) && $theme->info['hidden']) {
-      unset($themes[$key]);
-    }
   }
 
   return $themes;
index b4a5777..4890cc3 100644 (file)
@@ -8,6 +8,7 @@ function drush_core_site_install_version($profile, array $additional_form_option
     $profile = 'standard';
   }
 
+  define('MAINTENANCE_MODE', 'install');
   require_once DRUSH_DRUPAL_CORE . '/includes/install.core.inc';
 
   $db_spec = _drush_sql_get_db_spec();
index 2b62506..a2d7406 100644 (file)
@@ -120,7 +120,8 @@ function _drush_site_install6_cookies($profile, $cookie = NULL) {
     $output .= sprintf('$_COOKIE=unserialize("%s");', str_replace('"', '\"', $cookie));
   }
   else {
-    $output .= 'function _cli_cookie_print(){print(serialize(array(session_name()=>session_id())));} register_shutdown_function("_cli_cookie_print");';
+    $output .= 'function _cli_cookie_print(){print(serialize(array(session_name()=>session_id())));}
+register_shutdown_function("_cli_cookie_print");';
   }
 
   return $output;
index ce704c8..ccbaeda 100644 (file)
@@ -129,8 +129,8 @@ function update_fix_compatibility() {
   while ($result = db_fetch_object($query)) {
     if (update_check_incompatibility($result->name, $result->type)) {
       $incompatible[] = $result->name;
-      drush_log(dt("!type !name is incompatible with this release of Drupal, and will be disabled.",
-        array("!type" => $result->type, '!name' => $result->name)), "warning");
+      drush_log(dt("%type %name is incompatible with this release of Drupal, and will be disabled.",
+        array("%type" => $result->type, '%name' => $result->name)), "warning");
     }
   }
   if (!empty($incompatible)) {
index 2bd256a..937d065 100644 (file)
@@ -108,7 +108,7 @@ function drush_update_do_one($module, $number, $dependency_map,  &$context) {
     drupal_set_installed_schema_version($module, $number);
   }
 
-  $context['message'] = 'Performed update: ' . $function;
+  $context['message'] = 'Performing ' . $function;
 }
 
 /**
index 3ceff9a..01113fd 100644 (file)
@@ -223,7 +223,7 @@ function drush_field_delete($field_name) {
       $confirm = FALSE;
     }
     else {
-      if (!drush_confirm(dt('Do you want to delete the !field_name field?', array('!field_name' => $field_name)))) {
+      if (!drush_confirm(dt('Do you want to delete the %field_name field?', array('%field_name' => $field_name)))) {
         return drush_user_abort();
       }
     }
@@ -252,7 +252,7 @@ function drush_field_delete($field_name) {
 
 function drush_field_clone($source_field_name, $target_field_name) {
    if (!$info = field_info_field($source_field_name)) {
-     return drush_set_error(dt('!source not found in field list.', array('!source' => $source_field_name)));
+     return drush_set_error(dt('%source not found in field list.', array('%source' => $source_field_name)));
    }
 
    unset($info['id']);
index fae1e31..9083882 100644 (file)
@@ -40,10 +40,6 @@ function drush_print_help($command) {
     $help = array($command['description']);
   }
 
-  if ($command['strict-option-handling']) {
-    $command['topics'][] = 'docs-strict-options';
-  }
-
   // Give commandfiles an opportunity to add examples and options to the command.
   drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_SITE);
   drush_command_invoke_all_ref('drush_help_alter', $command);
@@ -189,7 +185,7 @@ function _drush_format_help_subsection($command, $section, $subsection, $formatt
  */
 function drush_help_section_formatter_options($command, &$help_attributes) {
   if ($help_attributes['label'][0] == '-') {
-    drush_log(dt("Option '!option' of command !command should instead be declared as '!fixed'", array('!option' => $help_attributes['label'], '!command' => $command['command'], '!fixed' => preg_replace('/^--*/', '', $help_attributes['label']))), 'debug');
+    drush_log(dt("Option '%option' of command %command should instead be declared as '%fixed'", array('%option' => $help_attributes['label'], '%command' => $command['command'], '%fixed' => preg_replace('/^--*/', '', $help_attributes['label']))), 'debug');
   }
   else {
     $help_attributes['label'] = '--' . $help_attributes['label'];
@@ -197,18 +193,10 @@ function drush_help_section_formatter_options($command, &$help_attributes) {
   if (array_key_exists('required', $help_attributes)) {
     $help_attributes['description'] .= " " . dt("Required.");
   }
-
-  $prefix = '<';
-  $suffix = '>';
   if (array_key_exists('example-value', $help_attributes)) {
-    if (isset($help_attributes['value']) && $help_attributes['value'] == 'optional') {
-      $prefix = '[';
-      $suffix = ']';
-    }
-    $help_attributes['label'] .= '=' . $prefix . $help_attributes['example-value'] . $suffix;
-
+    $help_attributes['label'] .= '=' . $help_attributes['example-value'];
     if (array_key_exists('short-form', $help_attributes)) {
-      $help_attributes['short-form'] .= " $prefix" . $help_attributes['example-value'] . $suffix;
+      $help_attributes['short-form'] .= ' ' . $help_attributes['example-value'];
     }
   }
   if (array_key_exists('short-form', $help_attributes)) {
diff --git a/commands/core/queue.drush.inc b/commands/core/queue.drush.inc
deleted file mode 100644 (file)
index 6cf533d..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-<?php
-
-/**
- * Implements hook_drush_help().
- */
-function queue_drush_help($section) {
-  switch ($section) {
-    case 'drush:queue-run':
-      return dt('Run Drupal queue workers. As opposed to "drush cron" that can only be run one at a time on a single site, "drush queue-run" can be invoked as many times as the server load allows.');
-  }
-}
-
-/**
- * Implements hook_drush_command().
- */
-function queue_drush_command() {
-  $items['queue-run'] = array(
-    'description' => 'Run a specific queue by name',
-    'arguments' => array(
-      'queue_name' => 'The name of the queue to run, as defined in either hook_queue_info or hook_cron_queue_info.',
-    ),
-    'required-arguments' => TRUE,
-  );
-  $items['queue-list'] = array(
-    'description' => 'Returns a list of all defined queues',
-    'options' => array(
-      'pipe' => 'Return a comma delimited list of queues.'
-    )
-  );
-
-  return $items;
-}
-
-
-/**
- * Validation for Drupal 6 to ensure the drupal_queue module is enabled.
- *
- * @param $command
- *   The command being validated.
- */
-function drush_queue_validate($command) {
-  if (drush_drupal_major_version() == 6) {
-    if (!module_exists('drupal_queue')) {
-      $args = array('!command' => $command, '!dependencies' => 'drupal_queue');
-      return drush_set_error('DRUSH_COMMAND_DEPENDENCY_ERROR', dt('Command !command needs the following modules installed/enabled to run: !dependencies.', $args));
-    }
-    else {
-      drupal_queue_include();
-    }
-  }
-}
-
-/**
- * Validation callback for drush queue-run.
- */
-function drush_queue_run_validate($queue_name) {
-  drush_queue_validate('queue-run');
-
-  // Get all queues.
-  $queues = drush_queue_get_queues();
-  if (!isset($queues[$queue_name])) {
-    return drush_set_error('DRUSH_QUEUE_ERROR', dt('Could not find the !name queue.', array('!name' => $queue_name)));
-  }
-}
-
-/**
- * Validation callback for drush queue-list.
- */
-function drush_queue_list_validate() {
-  drush_queue_validate('queue-list');
-}
-
-/**
- * Command callback for drush queue-run.
- *
- * Queue runner that is compatible with queues declared using both
- * hook_queue_info() and hook_cron_queue_info().
- *
- * @param $queue_name
- *   Arbitrary string. The name of the queue to work with.
- */
-function drush_queue_run($queue_name) {
-  // Get all queues.
-  $queues = drush_queue_get_queues();
-
-  // Try to increase the maximum execution time if it is too low.
-  $max_execution_time = ini_get('max_execution_time');
-  if ($max_execution_time > 0 && $max_execution_time < 240 && !ini_get('safe_mode')) {
-    set_time_limit(240);
-  }
-
-  $info = $queues[$queue_name];
-  $function = $info['cron']['callback'];
-  $end = time() + (isset($info['cron']['time']) ? $info['cron']['time'] : 15);
-  $queue = DrupalQueue::get($queue_name);
-  while (time() < $end && ($item = $queue->claimItem())) {
-    $function($item->data);
-    $queue->deleteItem($item);
-  }
-}
-
-/**
- * Command callback for drush queue-list.
- */
-function drush_queue_list() {
-  $queues = drush_queue_get_queues();
-  $default_class = variable_get('queue_default_class', 'SystemQueue');
-  $rows = array(array('Queue', 'Items', 'Class'));
-  foreach (array_keys($queues) as $name) {
-    $q = DrupalQueue::get($name);
-    $class = variable_get('queue_class_' . $name, $default_class);
-    $rows[] = array($name, $q->numberOfItems(), $class);
-  }
-
-  if (drush_get_option('pipe')) {
-    $pipe = array();
-    array_shift($rows);
-    foreach ($rows as $r) {
-      $pipe[] = implode(",", $r);
-    }
-    drush_print_pipe($pipe);
-  }
-  else {
-    drush_print_table($rows, TRUE);
-  }
-
-  // Return the result for backend invoke
-  return $rows;
-}
-
-/**
- * Get queues defined with hook_cron_queue_info().
- *
- * @return
- *   Array of queues indexed by name and containing queue object and number
- *   of items.
- */
-function drush_queue_get_queues() {
-  static $queues;
-  if (!isset($queues)) {
-    // Invoke hook_queue_info(), currently only defined by the queue_ui module.
-    $queues = module_invoke_all('queue_info');
-
-    // Merge in queues from modules that implement hook_cron_queue_info().
-    $cron_queues = module_invoke_all('cron_queue_info');
-    drupal_alter('cron_queue_info', $cron_queues);
-    foreach($cron_queues as $name => $queue) {
-      $queues[$name] = array(
-        'cron' => array(
-          'callback' => $queue['worker callback'],
-          'time' => $queue['time'],
-        )
-      );
-    }
-  }
-  return $queues;
-}
index 0d5a3bc..df8fe5d 100644 (file)
  *   the scope of this one call).
  */
 function drush_core_rsync($source, $destination, $additional_options = array()) {
-  // Preflight source in case it defines aliases used by the destination
-  _drush_sitealias_preflight_path($source);
-  // After preflight, evaluate file paths.  We evaluate destination paths first, because
-  // there is a first-one-wins policy with --exclude-paths, and we want --target-command-specific
-  // to take precidence over --source-command-specific.
-  $destination_settings = drush_sitealias_evaluate_path($destination, $additional_options, FALSE, "rsync", 'target-');
-  $source_settings = drush_sitealias_evaluate_path($source, $additional_options, FALSE, "rsync", 'source-');
+  // Preflight destination in case it defines aliases used by the source
+  _drush_sitealias_preflight_path($destination);
+  // After preflight, evaluate file paths
+  $source_settings = drush_sitealias_evaluate_path($source, $additional_options, FALSE, "rsync");
+  $destination_settings = drush_sitealias_evaluate_path($destination, $additional_options, FALSE, "rsync");
   $source_path = $source_settings['evaluated-path'];
   $destination_path = $destination_settings['evaluated-path'];
 
@@ -58,18 +56,8 @@ function drush_core_rsync($source, $destination, $additional_options = array())
     // destination contains a : or a /.
     $include_settings_is_default = (strpos($source . $destination, ':') !== FALSE) || (strpos($source . $destination, '/') !== FALSE);
 
-    $options = _drush_build_rsync_options($additional_options, $include_settings_is_default);
-
-    // Get all of the args and options that appear after the command name.
-    $original_args = drush_get_original_cli_args_and_options();
-    foreach ($original_args as $original_option) {
-      if ($original_option{0} == '-') {
-        $options .= ' ' . $original_option;
-      }
-    }
-    
     // Go ahead and call rsync with the paths we determined
-    return drush_core_exec_rsync($source_path, $destination_path, $options);
+    return drush_core_call_rsync($source_path, $destination_path, $additional_options, $include_settings_is_default);
   }
 }
 
@@ -98,38 +86,13 @@ function drush_core_rsync($source, $destination, $additional_options = array())
  *   TRUE on success, FALSE on failure.
  */
 function drush_core_call_rsync($source, $destination, $additional_options = array(), $include_settings_is_default = TRUE, $live_output = TRUE) {
-  $options = _drush_build_rsync_options($additional_options, $include_settings_is_default);  
-  return drush_core_exec_rsync($source, $destination, $options, $additional_options, $live_output);
-}
-
-function drush_core_exec_rsync($source, $destination, $options, $additional_options = array(), $live_output = TRUE) {
-  $ssh_options = drush_get_option_override($additional_options, 'ssh-options', '');
-  $exec = "rsync -e 'ssh $ssh_options' $options $source $destination";
-
-  if ($live_output) {
-    $exec_result = drush_op_system($exec);
-    $result = ($exec_result == 0);
-  }
-  else {
-    $result = drush_shell_exec($exec);
-  }
-
-  if (!$result) {
-    drush_set_error('DRUSH_RSYNC_FAILED', dt("Could not rsync from !source to !dest", array('!source' => $source, '!dest' => $destination)));
-  }
-
-  return $result;
-}
-
-function _drush_build_rsync_options($additional_options, $include_settings_is_default = TRUE) {
-  $options = '';
   // Exclude vcs reserved files.
+  $options = '';
   if (!_drush_rsync_option_exists('include-vcs', $additional_options)) {
     $vcs_files = drush_version_control_reserved_files();
     foreach ($vcs_files as $file) {
       $options .= ' --exclude="'.$file.'"';
     }
-    unset($additional_options['include-vcs']);
   }
 
   $mode = '-akz';
@@ -137,15 +100,12 @@ function _drush_build_rsync_options($additional_options, $include_settings_is_de
   foreach (array('include', 'exclude') as $include_exclude) {
     // Get the option --include-path or --exclude-path and explode to an array of paths
     // that we will translate into an --include or --exclude option to pass to rsync
-    $inc_ex_path = explode(PATH_SEPARATOR, drush_get_option($include_exclude . '-paths', ''));
+    $inc_ex_path = explode(PATH_SEPARATOR, drush_get_option(array($include_exclude . '-path', $include_exclude . '-paths'), ''));
     foreach ($inc_ex_path as $one_path_to_inc_ex) {
       if (!empty($one_path_to_inc_ex)) {
         $options .= ' --' . $include_exclude . '="' . $one_path_to_inc_ex . '"';
       }
     }
-    // Remove stuff inserted by evaluate path
-    unset($additional_options[$include_exclude . '-paths']);
-    unset($additional_options[$include_exclude . '-files-processed']);
   }
   // drush_core_rsync passes in $include_settings_is_default such that
   // 'exclude-conf' is the default when syncing from one alias to
@@ -153,27 +113,117 @@ function _drush_build_rsync_options($additional_options, $include_settings_is_de
   // is included.
   if ($include_settings_is_default ? _drush_rsync_option_exists('exclude-conf', $additional_options) : !_drush_rsync_option_exists('include-conf', $additional_options)) {
     $options .= ' --exclude="settings.php"';
-    unset($additional_options['exclude-conf']);
   }
   if (_drush_rsync_option_exists('exclude-sites', $additional_options)) {
     $options .= ' --include="sites/all" --exclude="sites/*"';
-    unset($additional_options['exclude-sites']);
   }
   if (_drush_rsync_option_exists('mode', $additional_options)) {
     $mode = "-" . drush_get_option_override($additional_options, 'mode');
-    unset($additional_options['mode']);
   }
   if (drush_get_context('DRUSH_VERBOSE')) {
     // the drush_op() will be verbose about the command that gets executed.
     $mode .= 'v';
     $options .= ' --stats --progress';
   }
-
+  $rsync_available_options = array(
+    // unary options
+    'archive', // -a
+    'recursive', // -r
+    'relative', // -R
+    'backup', // -b
+    'update', // -u
+    'checksum', // -c
+    'dirs', // -d
+    'links', // -l
+    'copy-links', // -L
+    'copy-dirlinks', // -k
+    'keep-dirlinks', // -K
+    'hard-links', // -H
+    'perms', // -p
+    'executability', // -E
+    'acls', // -A
+    'xattrs', // -X
+    'owner', // -o
+    'group', // -g
+    'times', // -t
+    'omit-dir-times', // -O
+    'sparse', // -S
+    'dry-run', // -n
+    'whole-file', // -W
+    'one-file-system', // -x
+    'prune-empty-dirs', // -m
+    'ignore-times', // -I
+    'fuzzy', // -y
+    'cvs-exclude', // -C
+    'compress', // -Z
+    'protect-args', // -s
+    '8-bit-output', // -8
+    'human-readable', // -h
+    'itemize-changes', // -i
+    'copy-unsafe-links',
+    'safe-links',
+    'no-implied-dirs',
+    'inplace',
+    'append',
+    'append-verify',
+    'existing',
+    'remove-source-files',
+    'delete',
+    'delete-before',
+    'delete-during',
+    'delete-delay',
+    'delete-after',
+    'delete-excluded',
+    'ignore-errors',
+    'force',
+    'ignore-existing',
+    'partial',
+    'delay-updates',
+    'numeric-ids',
+    'size-only',
+    'blocking-io',
+    'stats',
+    'progress',
+    'list-only',
+    // options with values
+    'block-size',
+    'backup-dir',
+    'suffix',
+    'chmod',
+    'rsync-path',
+    'modify-window',
+    'compare-dest',
+    'copy-dest',
+    'link-dest',
+    'skip-compress',
+    'filter',
+    // filter out 'exclude'; consistency with --include. Use --exclude-paths.
+    'exclude-from',
+    // filter out 'include'; conflicts with global drush --include option. Use --include-paths.
+    'include-from',
+    'files-from',
+    'address',
+    'port',
+    'sockopts',
+    'out-format',
+    'bwlimit',
+    'iconv',
+    'checksum-seed',
+    'max-delete',
+    'max-size',
+    'min-size',
+    'partial-dir',
+    'timeout',
+    'temp-dir',
+    'compress-level',
+    'out-format',
+    'protocol',
+    );
   // Check if the user has set $options['rsync-version'] to enable rsync legacy version support.
   // Drush was written for rsync 2.6.9 or later, so assume that version if nothing was explicitly set.
   $rsync_version = drush_get_option(array('rsync-version','source-rsync-version','target-rsync-version'), '2.6.9');
-  $options_to_exclude = array('ssh-options');
-  foreach ($additional_options as $test_option => $value) {
+  foreach ($rsync_available_options as $test_option) {
+    $value = drush_get_option_override($additional_options, $test_option);
     // Downgrade some options for older versions of rsync
     if ($test_option == 'remove-source-files') {
       if (version_compare($rsync_version, '2.6.4', '<')) {
@@ -184,7 +234,7 @@ function _drush_build_rsync_options($additional_options, $include_settings_is_de
         $test_option = 'remove-sent-files';
       }
     }
-    if ((isset($test_option)) && !in_array($test_option, $options_to_exclude) && (isset($value)  && !is_array($value))) {
+    if ((isset($test_option)) && (isset($value)  && !is_array($value))) {
       if ($value === TRUE) {
         $options .= " --$test_option";
       }
@@ -194,7 +244,22 @@ function _drush_build_rsync_options($additional_options, $include_settings_is_de
     }
   }
 
-  return $mode . $options;
+  $ssh_options = drush_get_option_override($additional_options, 'ssh-options', '');
+  $exec = "rsync -e 'ssh $ssh_options' $mode$options $source $destination";
+
+  if ($live_output) {
+    $exec_result = drush_op_system($exec);
+    $result = ($exec_result == 0);
+  }
+  else {
+    $result = drush_shell_exec($exec);
+  }
+
+  if (!$result) {
+    drush_set_error('DRUSH_RSYNC_FAILED', dt("Could not rsync from !source to !dest", array('!source' => $source, '!dest' => $destination)));
+  }
+
+  return $result;
 }
 
 function _drush_rsync_option_exists($option, $additional_options) {
index bfd25f8..226156d 100644 (file)
@@ -4,7 +4,7 @@
  * Command validate.
  */
 function drush_core_site_install_validate() {
-  if ($sites_subdir = drush_get_option('sites-subdir')) {
+  if ($sites_subdir = drush_get_option('sites-subdir', 'default')) {
     $lower = strtolower($sites_subdir);
     if ($sites_subdir != $lower) {
       drush_log(dt('Only lowercase sites-subdir are valid. Switching to !lower.', array('!lower' => $lower)), 'warning');
@@ -23,8 +23,7 @@ function drush_core_pre_site_install($profile = NULL) {
     drush_set_error(dt('Could not determine database connection parameters. Pass --db-url option.'));
     return;
   }
-  $default_sites_subdir = drush_get_context('DRUSH_DRUPAL_SITE', 'default');
-  $sites_subdir = drush_get_option('sites-subdir', $default_sites_subdir);
+  $sites_subdir = drush_get_option('sites-subdir', 'default');
 
   $conf_path = "sites/$sites_subdir";
   $files = "$conf_path/files";
@@ -95,7 +94,6 @@ function drush_core_pre_site_install($profile = NULL) {
 
   // The Drupal 6 installer needs to bootstrap up to the specified site.
   // We need to be at least at DRUSH_BOOTSTRAP_DRUPAL_SITE to select the site uri to install to
-  define('MAINTENANCE_MODE', 'install');
   if (drush_drupal_major_version() == 6) {
     drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION);
   }
index 324f73d..6f3c6b1 100644 (file)
@@ -15,7 +15,7 @@ function ssh_drush_command() {
     ),
     'options' => drush_shell_exec_proc_build_options(),
     'handle-remote-commands' => TRUE,
-    'strict-option-handling' => TRUE,
+    'mask-local-cli-options' => TRUE,
     'examples' => array(
       'drush @mysite ssh' => 'Open an interactive shell on @mysite\'s server.',
       'drush @prod ssh \'ls /tmp\'' => 'Run "ls /tmp" on @prod site. If @prod is a site list, then ls will be executed on each site.',
@@ -31,8 +31,8 @@ function ssh_drush_command() {
  * Command callback.
  */
 function drush_ssh_site_ssh($command = NULL) {
-  // Get all of the args and options that appear after the command name.
-  $args = drush_get_original_cli_args_and_options();
+  // The DRUSH_COMMAND_ARGS contain all args and options that appear after the command name.
+  $args = drush_get_context('DRUSH_COMMAND_ARGS', array());
   // n.b. we do not escape the first (0th) arg to allow `drush ssh 'ls /path'`
   // to work in addition to the preferred form of `drush ssh ls /path`.
   // Supporting the legacy form means that we cannot give the full path to an
@@ -42,7 +42,7 @@ function drush_ssh_site_ssh($command = NULL) {
   }
   $command = implode(' ', $args);
   if (!$alias = drush_get_context('DRUSH_TARGET_SITE_ALIAS')) {
-    return drush_set_error('DRUSH_MISSING_TARGET_ALIAS', 'A remote site alias is required. The way you call ssh command has changed to `drush @alias ssh`.');
+    return drush_set_error('DRUSH_MISSING_TARGET_ALIAS', 'A remote site alias is required. The way call ssh command has changed to `drush @alias ssh`.');
   }
   $site = drush_sitealias_get_record($alias);
   // We only accept remote aliases.
index bf286df..aa40fa8 100644 (file)
@@ -53,11 +53,11 @@ function test_drush_command() {
  *  Array of test classes and groups.
  */
 function test_test_run_complete() {
-  if (drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_FULL) == DRUSH_BOOTSTRAP_DRUPAL_FULL && module_exists('simpletest')) {
+  if (drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_FULL) && module_exists('simpletest')) {
     // Retrieve all tests and groups.
     list($groups, $all_tests) = drush_test_get_all_tests();
-    return array('values' => array_keys($groups) + $all_tests);
   }
+  return array('values' => array_keys($groups) + $all_tests);
 }
 
 // Command callback
@@ -94,7 +94,7 @@ function drush_test_run($specs = NULL) {
     // Run all tests.
     foreach (array_keys($groups) as $group) {
       foreach (array_keys($groups[$group]) as $class) {
-        drush_invoke_process('@self', 'test-run', array($class));
+        drush_invoke_process('@none', 'test-run', array($class), array('--no-all'));
       }
     }
     return;
index cd863e0..b9f9679 100644 (file)
@@ -44,15 +44,12 @@ function usage_drush_command() {
  * different endpoint.
  */
 function usage_drush_exit() {
-  // Ignore statistics for simulated commands. (n.b. in simulated mode, _drush_usage_mongolab will print rather than send statistics)
-  if (!drush_get_context('DRUSH_SIMULATE')) {
-    $file = _drush_usage_get_file();
-    if (drush_get_option('drush_usage_log', FALSE)) {
-      _drush_usage_log(drush_get_command(), $file);
-    }
-    if (drush_get_option('drush_usage_send', FALSE)) {
-      _drush_usage_mongolab($file, drush_get_option('drush_usage_size', 51200));
-    }
+  $file = _drush_usage_get_file();
+  if (drush_get_option('drush_usage_log', FALSE)) {
+    _drush_usage_log(drush_get_command(), $file);
+  }
+  if (drush_get_option('drush_usage_send', FALSE)) {
+    _drush_usage_mongolab($file, drush_get_option('drush_usage_size', 51200));
   }
 }
 
index 37b1fb6..21b62ca 100644 (file)
@@ -24,10 +24,7 @@ function variable_drush_command() {
       'drush vget user' => 'List all variables beginning with the string "user".',
     ),
     'options' => array(
-      'format' => array(
-        'description' => 'Format to output the object. Use "print_r" for print_r (default), "export" for var_export, and "json" for JSON.',
-        'example-value' => 'export',
-      ),
+      'format' => 'Format to output the object. Use "print_r" for print_r (default), "export" for var_export, and "json" for JSON.',
       'pipe' => 'A synonym for --format=export. Useful for pasting into code.',
     ),
     'aliases' => array('vget'),
@@ -42,10 +39,7 @@ function variable_drush_command() {
     'options' => array(
       'yes' => 'Skip confirmation if only one variable name matches.',
       'always-set' => 'Always skip confirmation.',
-      'format' => array(
-        'description' => 'Format to parse the object. Use "auto" to detect format from value (default), "string", "integer" or "boolean" for corresponding primitive type, and "json" for JSON.',
-        'example-value' => 'boolean',
-      ),
+      'format' => 'Format to parse the object. Use "auto" to detect format from value (default), "string", "integer" or "boolean" for corresponding primitive type, and "json" for JSON.',
     ),
     'examples' => array(
       'drush vset --yes preprocess_css TRUE' => 'Set the preprocess_css variable to true. Skip confirmation if variable already exists.',
index f47da67..ea839cd 100644 (file)
@@ -52,7 +52,7 @@ function watchdog_drush_command() {
   $items['watchdog-delete'] = array(
     'description' => 'Delete watchdog messages.',
     'drupal dependencies' => array('dblog'),
-    'options' => array(
+    'arguments' => array(
       'severity' => 'Delete messages of a given severity level.',
       'type' => 'Delete messages of a given type.',
     ),
diff --git a/commands/entity/entity.drush.inc b/commands/entity/entity.drush.inc
new file mode 100644 (file)
index 0000000..0c7adda
--- /dev/null
@@ -0,0 +1,927 @@
+<?php
+
+/**
+ * @file
+ *   Drush support for entities.
+ */
+/**
+ * Define how a print out needs to be
+ */
+define('DRUSH_ENTITY_SEPARATED_SPACE', 'space-separated');
+
+/**
+ * Implementation of hook_drush_command().
+ */
+function entity_drush_command() {
+  // Some standard settings
+  $output_json = "Process the entity as JSON";
+  $output_format = "Define output format. Known formats are: json, print_r, properties, export";
+  $type = "Entity type";
+  $nids = "A list of space-separated entity IDs to print to stdout.";
+
+  $items = array();
+
+  /*
+   * Entity type commands
+   */
+  $items['entity-type-read'] = array(
+    'description' => "List details of entity types",
+    'arguments' => array(
+      'type' => "Entity type to list. If omitted all types are listed.",
+    ),
+    'options' => array(
+      'format' => $output_format,
+      'fields' => 'fields of type to list specific info.',
+      'exclude-fields' => 'exclude fields from the list',
+      'include-fieldapi' => 'include the bundle specific field definitions',
+    ),
+    'examples' => array(
+      'entity-type-read' => 'List available entity types',
+      'entity-type-read node' => 'List node type information',
+      'entity-type-read node --fields=bundles' => 'List bundles for node type',
+      "etr `drush etr` --fields='entity keys',fieldable" => 'List all entity keys and fieldable.',
+      'entity-type-read node --fields=bundles/*/admin/path' => 'List admin path for all bundles',
+    ),
+    'aliases' => array('etr'),
+  );
+
+  /*
+   * Entity commands
+   */
+  $items['entity-create'] = array(
+    'description' => "Create an entity from a json object",
+    'arguments' => array(
+      'type' => "Entity type",
+    ),
+    'options' => array(
+      'json' => $output_json,
+    ),
+    'examples' => array(
+      'entity-read node 4 --json | drush entity-create node' => 'Copy node/4 to a new entity.',
+    ),
+    'aliases' => array('ec'),
+  );
+
+  $items['entity-read'] = array(
+    'description' => "Print entity contents",
+    'arguments' => array(
+      'type' => $type,
+      'nids' => $nids,
+    ),
+    'options' => array(
+      'format' => $output_format,
+      'fields' => 'fields of type to list specific info',
+    ),
+    'examples' => array(
+      'entity-read user 4' => 'Print the user object 4',
+      'entity-read taxonomy_vocabulary 1' => 'Print the taxonomy_vocabulary object 1',
+    ),
+    'aliases' => array('er'),
+  );
+
+  $items['entity-update'] = array(
+    'description' => "Update an entity as json in the default editor",
+    'arguments' => array(
+      'type' => $type,
+      'nids' => $nids,
+    ),
+    'options' => array(
+      'json' => $output_json,
+      'fields' => 'fields of type to list specific info',
+      'json-input' => 'Filename with json content, or - for STDIN'
+    ),
+    'examples' => array(
+      'entity-update node 4' => 'Update node 4 as json in the default editor',
+    ),
+    'aliases' => array('eu'),
+  );
+
+  $items['entity-delete'] = array(
+    'description' => 'Delete entities.',
+    'arguments' => array(
+      'type' => $type,
+      'nids' => $nids,
+    ),
+    'options' => array(
+      'json' => $output_json,
+    ),
+    'examples' => array(
+      'entity-delete' => '.',
+      'entity-delete node 64' => '.',
+      'entity-delete node --type=story' => '.',
+    ),
+    'aliases' => array('ed'),
+  );
+
+  return $items;
+}
+
+/**
+ * Implementation of hook_drush_help().
+ *
+ * @param
+ *   A string with the help section (prepend with 'drush:')
+ *
+ * @return
+ *   A string with the help text for your command.
+ */
+function entity_drush_help($section) {
+  // TODO: is this function still needed?
+  switch ($section) {
+    case 'drush:entity-show':
+      return dt("Print entity objects to stdout");
+    case 'drush:entity-create':
+      return dt("Create an entity from a json object");
+    case 'drush:entity-edit':
+      return dt("Edit an entity as json in the default editor");
+    case 'drush:entity-delete':
+      return dt('Delete entities.');
+  }
+}
+
+/**
+ * List details of entity types
+ *
+ * @param type $type
+ * @return type
+ */
+function drush_entity_type_read($entity_type = NULL) {
+  $types = func_get_args();
+  $entities_info = _drush_entity_get_info();
+
+  // List the available types
+  if (empty($entity_type)) {
+    $result = array_keys($entities_info);
+    drush_print_pipe(drush_format($result));
+    drush_print(drush_format($result));
+    return;
+  }
+
+  $fields = _drush_entity_get_fields();
+  $result = array();
+  if (count($types)) {
+    foreach ($types as $type) {
+      $info = $entities_info[$type];
+      $result_type = _drush_entity_filter_fields($info, $fields, _drush_entity_get_exclude_fields());
+      if (!empty($result_type)) {
+        $result[$type] = $result_type;
+      }
+    }
+  }
+
+  drush_print_pipe(drush_format($result));
+  drush_print(drush_format($result));
+  return;
+}
+
+/**
+ * Filter on the given paths
+ *
+ * Each path may contain forward slashes / to filter subtrees
+ *
+ * A path may contain a star * or ** to wildcard a path
+ *
+ * Example paths (ignore space after *)
+ * - schema_fields_sql
+ * - bundles/* /label
+ * - ** /display
+ *
+ * @param array $value
+ * @param array $paths
+ *   List of paths to filter about.
+ * @return array
+ *   The matching path
+ */
+function _drush_entity_filter_fields(array $value, array $paths, array $delete_path = array()) {
+  // Store object hashs to prevent object ref recursion
+  $object_hash_list = array();
+  $return = array();
+  if (empty($paths)) {
+    $return = $value;
+  }
+  // List of path already done
+  $visited = array();
+  while (count($paths) > 0) {
+    $path = array_shift($paths);
+    if (isset($visited[$path])) {
+      continue;
+    }
+    $visited[$path] = $path;
+    // Contains the resulting path
+    $head = array();
+    // The result is a linked list done with an key => array construct
+    $tail = &$head;  // By ref
+    $sub_tree = split('/', $path);
+    $current_path = '';
+    $work_value = $value;
+    $failed_path = FALSE;
+    while (count($sub_tree) > 0) {
+      $p = array_shift($sub_tree);
+      if ($p == '*' || $p == '**') {
+        // Wildcards only usefull on arrays
+        if (is_array($work_value) || _drush_entity_visit_object(&$object_hash_list, $work_value)) {
+          // Say a/**/b was requested
+          // We need to search for a/b and a/$option/**/b
+          $new_tree = $sub_tree;
+          $new_path = $current_path . join('/', $new_tree);
+          if (!isset($visited[$new_path])) {
+            array_push($paths, $new_path);
+          }
+          // We cast to array only to generate paths
+          $p_options = array_keys((array)$work_value);
+          //$p = array_shift($p_options);
+          foreach ($p_options as $p_option) {
+            // Create
+            $new_tree = $sub_tree;
+            array_unshift($new_tree, $p_option);
+            $new_path = $current_path . join('/', $new_tree);
+            if (!isset($visited[$new_path])) {
+              array_push($paths, $new_path);
+              if ($p == '**') {
+                $new_tree = $sub_tree;
+                array_unshift($new_tree, '**');
+                array_unshift($new_tree, $p_option);
+                $new_path = $current_path . join('/', $new_tree);
+                if (!isset($visited[$new_path])) {
+                  array_push($paths, $new_path);
+                }
+              }
+            }
+          }
+        }
+        else {
+          $failed_path = TRUE;
+        }
+        // We processed the wildcard paths so stop processing
+        break; // sub tree processing
+      }
+      // Remember current path
+      $current_path .= $p . '/';
+      // Consume key
+      $temp = (array) $work_value;
+      if (is_array($temp) && isset($temp[$p])) {
+        $work_value = $temp[$p];
+        if (is_object($work_value) || is_array($work_value)) {
+          if (count($sub_tree)) {
+            // We are not done yet
+            $tail[$p] = array();
+          }
+          else {
+            // Stuff value: object or array
+            $tail[$p] = $work_value;
+          }
+        }
+        else {
+          $tail[$p] = $work_value;
+        }
+        // Follow the build tree
+        $tail = &$tail[$p]; // By ref
+      }
+      else {
+        $failed_path = TRUE;
+        break; // while
+      }
+    }
+    if (!$failed_path && count($sub_tree) == 0) {
+      $return = array_merge_recursive($return, $head);
+    }
+  }
+  if ($delete_path) {
+    $delete = _drush_entity_filter_fields($return, $delete_path);
+    // TODO substract $delete from $return .. next command deletes more
+    // try etr --include-fieldapi ... the are deleted too
+    // $return = array_diff($return, $delete);
+  }
+  return $return;
+}
+
+/**
+ * List the entity IDs of the given type
+ *
+ * @param type $entity_type
+ */
+function _drush_entity_read_list($entity_type) {
+  $entity_info = _drush_entity_get_info($entity_type);
+
+  if (drush_drupal_major_version() < 7) {
+    if (isset($entity_info['load list sql'])) {
+      $sql = $entity_info['load list sql'];
+      $entities = array();
+      $result = db_query($sql);
+      while ($row = db_fetch_array($result)) {
+        $id = $row['id'];
+        $entities[$entity_type][$id] = $id;
+      }
+    }
+  }
+  else {
+    $query = new EntityFieldQuery();
+    $entities = $query->entityCondition('entity_type', $entity_type)->execute();
+  }
+
+  if (count($entities)) {
+    $result = array_keys($entities[$entity_type]);
+    $format = DRUSH_ENTITY_SEPARATED_SPACE;
+    $header = "Available ids for $entity_type";
+  }
+  else {
+    $format = DRUSH_ENTITY_SEPARATED_SPACE;
+    $header = "No ids for $entity_type";
+    $result = array();
+  }
+  _drush_entity_print($result, $format, $header);
+}
+
+/**
+ * Show entities by given type and id
+ *
+ * @param string $type
+ *   Given entity type
+ */
+function drush_entity_read($entity_type = NULL) {
+  if (!isset($entity_type)) {
+    drush_die("You must specify an entity_type");
+  }
+  $ids = func_get_args();
+  $entity_type = array_shift($ids);
+  _drush_entity_check_type($entity_type);
+
+  // Do listing
+  if (count($ids) == 0) {
+    _drush_entity_read_list($entity_type);
+    return;
+  }
+
+  // Do content
+  $entities = _drush_entity_load($entity_type, $ids);
+
+  $fields = _drush_entity_get_fields();
+  if ($fields) {
+    $entities = _drush_entity_select_fields($entities, $fields, TRUE);
+  }
+
+  if (drush_get_option('json', FALSE) && count($entities) == 1) {
+    // We only shift when requesting json and just one entity
+    $entities = array_shift($entities);
+  }
+
+  drush_print_pipe(drush_format($entities));
+  drush_print(drush_format($entities));
+}
+
+/**
+ * Extract the selected fields from a number of entities.
+ *
+ * @param type $entities Array of entities
+ * @param type $fields Array of fileds to be returned for each entity
+ * @return type
+ */
+function _drush_entity_select_fields($entities, $fields, $allow_path = FALSE) {
+  if (!$allow_path) {
+    foreach ($fields as $path) {
+      if (strpos($path, '/') !== FALSE) {
+        drush_die("No path supported yet : $path");
+      }
+      if (strpos($path, '*') !== FALSE) {
+        drush_die("No wildcard supported yet : $path");
+      }
+    }
+  }
+
+  $result = array();
+  foreach ($entities as $eid => $entity) {
+    $result[$eid] = _drush_entity_filter_fields((array) $entity, $fields);
+  }
+  return $result;
+}
+
+/**
+ * Create an entity by the given type.
+ *
+ * @param $entity_type
+ * @param $arg
+ */
+function drush_entity_create($entity_type, $arg = NULL) {
+  if (!isset($entity_type)) {
+    drush_die("You must specify a entity_type");
+  }
+
+//  if (!drush_get_option('json')) {
+//    drush_die("You must specify --json");
+//  }
+
+  _drush_entity_check_type($entity_type);
+
+  $entity_info = _drush_entity_get_info($entity_type);
+
+  if (empty($arg)) {
+    $entity = new stdClass();
+    if (isset($entity_info['drush']['defaults'])) {
+      foreach ($entity_info['drush']['defaults'] as $key => $value) {
+        $entity->$key = $value;
+      }
+    }
+    $entity_json = _drush_entity_edit_string(drush_json_encode($entity));
+  }
+  else if ($arg === '-' || file_exists($arg)) {
+    if ($arg === '-') {
+      $entity_json = stream_get_contents(STDIN);
+    }
+    else {
+      drush_log("Reading file $arg");
+      $entity_json = file_get_contents($arg);
+    }
+  }
+  else {
+    drush_die("Improper input/args");
+  }
+
+  if (!empty($entity_json)) {
+    $entity = (object) drush_json_decode($entity_json);
+
+    $eid = $entity_info['drush']['new'][0];
+    if (_drush_entity_save($entity_type, $entity, TRUE)) {
+      _drush_entity_print('', DRUSH_ENTITY_SEPARATED_SPACE, 'Entity created.');
+    }
+    else if (isset($entity->$eid)) {
+      _drush_entity_print(array($entity->$eid), DRUSH_ENTITY_SEPARATED_SPACE, 'Entity created.');
+    }
+    else {
+      drush_set_error('DRUSH_ERROR', dt("Failed to create entity!"));
+    }
+  }
+  else if ($arg === '-') {
+    drush_set_error('DRUSH_NO_STDIN', dt("stdin empty!"));
+  }
+  else {
+    drush_set_error('DRUSH_EMPTY_FILE', dt("Empty file!"));
+  }
+}
+
+/**
+ * Edit a string with the default shell editor.
+ *
+ * @param type $string
+ * @return type
+ */
+function _drush_entity_edit_string($string) {
+  $editor = getenv('EDITOR');
+  if (empty($editor)) {
+    drush_set_error('DRUSH_NO_EDITOR', dt('The environment variable EDITOR is not set'));
+    return $string;
+  }
+  else {
+    $file = drush_save_data_to_temp_file($string);
+    drush_shell_exec_interactive('$EDITOR ' . $file);
+    return file_get_contents($file, "r");
+  }
+}
+
+/**
+ * Convert the given fields into an array
+ *
+ * @return Array
+ */
+function _drush_entity_get_fields() {
+  $fields = drush_get_option('fields', '');
+  if ($fields) {
+    return split(',', $fields);
+  }
+  return array();
+}
+
+function _drush_entity_get_exclude_fields() {
+  $fields = drush_get_option('exclude-fields', '');
+  // We always include 'drush' to exclude fields
+  if (!$fields) {
+    $fields = 'drush';
+  }
+  else {
+    $fields .= ',drush';
+  }
+  $exclude_fields = split(',', $fields);
+  return array_diff($exclude_fields, _drush_entity_get_fields());
+}
+
+/**
+ * Update given entity.
+ */
+function drush_entity_update($entity_type, $id) {
+  $fields = _drush_entity_get_fields();
+  $entities = _drush_entity_load($entity_type, array($id));
+
+  if (isset($entities[$id])) {
+    $entity = $entities[$id];
+    $input = drush_get_option('json-input', NULL);
+    if ($input === '-') {
+      $edited_obj_json = stream_get_contents(STDIN);
+    }
+    elseif (file_exists($input)) {
+      drush_log("Reading file $input");
+      $edited_obj_json = file_get_contents($input);
+    }
+    else {
+      // Prepare to EDIT a json string
+      if ($fields) {
+        $entities_fields = _drush_entity_select_fields($entities, $fields, FALSE);
+      }
+
+      if ($fields) {
+        $edit_obj = $entities_fields[$id];
+      }
+      else {
+        $edit_obj = $entity;
+      }
+      $edited_obj_json = _drush_entity_edit_string(drush_json_encode($edit_obj));
+    }
+
+    // Prepare the changed entity to be saved
+    if ($fields) {
+      // Remap fields onto entity
+      $edited_entity = $entity;
+      $edited_fields = (object) drush_json_decode($edited_obj_json);
+      foreach ($fields as $field) {
+        $edited_entity->$field = $edited_fields->$field;
+      }
+    }
+    else {
+      $edited_entity = (object) drush_json_decode($edited_obj_json);
+    }
+
+    // Save
+    _drush_entity_save($entity_type, $edited_entity);
+  }
+  else {
+    drush_set_error('DRUSH_ERROR', dt("Entity to update not found!"));
+  }
+}
+
+/**
+ * Load entities with the given type and IDs
+ *
+ * @param type $entity_type
+ * @param type $ids
+ * @return array with entity objects
+ */
+function _drush_entity_load($entity_type, $ids) {
+  $entities = array();
+  switch (drush_drupal_major_version()) {
+    case 5:
+    case 6:
+      foreach ($ids as $id) {
+        $entity = _drush_entity_op('load', $entity_type, $id);
+        $entities[$id] = (object) $entity;
+      }
+      break;
+    case 7:
+      $entities = entity_load($entity_type, $ids);
+      break;
+  }
+  return $entities;
+}
+
+/**
+ * Command callback.
+ */
+function drush_entity_delete($entity_type, $ids = NULL) {
+  if (!isset($entity_type)) {
+    drush_die("You must specify an entity type");
+  }
+  $info = _drush_entity_get_info($entity_type);
+  if (!isset($info)) {
+    drush_die("Type '$entity_type' does not exist.");
+  }
+  $ids = func_get_args();
+  $entity_type = array_shift($ids);
+
+  if (!is_null($ids)) {
+    $result = _drush_entity_delete($entity_type, $ids);
+    if (count($result) > 0) {
+      _drush_entity_print($result);
+    }
+  }
+  else {
+    drush_log("NOT YET IMPLEMENTED ... Delete ALL entities of type $entity_type", "ERROR");
+    //$nids = drush_get_nodes_by_type($entity_type);
+    //_drush_entity_delete($entity_type, $nids);
+  }
+}
+
+/**
+ * Save the given entity as a given type.
+ *
+ *
+ * @param $entity_type
+ *   The type of the entity.
+ * @param $entity
+ *   The entity to save.
+ * @return
+ *   Depending on implementation and drupal version
+ *
+ * @see entity_type_supports()
+ */
+function _drush_entity_save($entity_type, &$entity, $new = FALSE) {
+  if ($new) {
+    $entity_info = _drush_entity_get_info($entity_type);
+    if (isset($entity_info['drush']['new'])) {
+      foreach ($entity_info['drush']['new'] as $field) {
+        unset($entity->$field);
+      }
+    }
+  }
+
+  return _drush_entity_op('save', $entity_type, $entity);
+}
+
+/**
+ * Try running CRUD op on the given id or entity.
+ *
+ * This mimics from D7 Entity API entity.module but for all CRUD ops
+ *
+ * @param type $op
+ *   Operation to perform. Supported ops are: save, delete, load
+ * @param type $entity_type
+ * @param type $entity_or_id
+ *   TODO: why by ref?
+ * @return type
+ */
+function _drush_entity_op($op, $entity_type, &$entity_or_id) {
+  // We need to fix for D7 when $op == delete
+  $op_alias = $op;
+  if ($op == 'delete') {
+    // D7 operator rename ..
+    // TODO: is this changed ?
+    $op_alias = 'deletion';
+    // D6 user_delete requires two arguments
+    if (drush_drupal_major_version() < 7 && $entity_type == 'user') {
+      user_delete(array(), $entity_or_id);
+      return;
+    }
+  }
+  $info = _drush_entity_get_info($entity_type);
+
+  // TODO: add comment
+  if (isset($info['drush'][$op . ' needs'])) {
+    switch ($info['drush'][$op . ' needs']) {
+      case 'array':
+        $entity_or_id = (array) $entity_or_id;
+        break;
+      case 'entity':
+        $old = $entity_or_id;
+        $entity_or_id = reset(_drush_entity_load($entity_type, array($entity_or_id)));
+        if (empty($entity_or_id)) {
+          drush_log("Unable to run $op on $entity_type : $old", 'warning');
+          return;
+        }
+        break;
+      default:
+        break;
+    }
+  }
+  if (method_exists($entity_or_id, $op)) {
+    return $entity_or_id->$op();
+  }
+  elseif (isset($info[$op_alias . ' callback'])) {
+    return $info[$op . ' callback']($entity_or_id);
+  }
+  elseif (isset($info['controller class']) && in_array('EntityAPIControllerInterface', class_implements($info['controller class']))) {
+    return entity_get_controller($entity_type)->$op($entity_or_id);
+  }
+  elseif (function_exists($entity_type . '_' . $op)) {
+    $op_function = $entity_type . '_' . $op;
+    // Fix user_save
+    if ($op == 'save' && isset($info['drush']['save needs keys'])) {
+      $keys = (array) $entity_or_id;
+      return $op_function($entity_or_id, $keys);
+    }
+    else {
+      return $op_function($entity_or_id);
+    }
+  }
+  else {
+    drush_log("Unable to $op the entity $entity_or_id. Maybe you could try to install and enable the entity.module");
+    return FALSE;
+  }
+}
+
+/**
+ * Try to run _delete_multiple or otherwise iterate.
+ *
+ * @param type $entity_type
+ * @param type $ids
+ * @return type the delete results per type implementation
+ */
+function _drush_entity_delete($entity_type, $ids = array()) {
+  if ($entity_type == 'node') {
+    // We want to use _delete_multiple
+    $result = _drush_entity_delete_node($ids);
+  }
+  else {
+    $result = array();
+    foreach ($ids as $id) {
+      $result["$id"] = _drush_entity_op('delete', $entity_type, $id);
+    }
+  }
+  return $result;
+}
+
+/**
+ * This is special as we have node_delete_multiple
+ *
+ * The code for D6 is also special as cache_clear_all is only invoked in the end
+ *
+ * @param array $nids
+ * //TODO : better doc please
+ * @return the drupal behaviour for the delete function used.
+ */
+function _drush_entity_delete_node(array $nids) {
+  if (drush_drupal_major_version() >= 7) {
+    return node_delete_multiple($nids);
+  }
+  else {
+    // Drupal 5/6
+    // node-delete is implemented this way to prevent calling cache_clear_all() for each node.
+    foreach ($nids as $nid) {
+      $node = node_load($nid, NULL, TRUE);
+      db_query('DELETE FROM {node} WHERE nid = %d', $nid);
+      db_query('DELETE FROM {node_revisions} WHERE nid = %d', $nid);
+      node_invoke($node, 'delete');
+      node_invoke_nodeapi($node, 'delete');
+      if (function_exists('search_wipe')) {
+        search_wipe($node->nid, 'node');
+      }
+    }
+    cache_clear_all();
+  }
+}
+
+/**
+ * Wrapper for entity_info()
+ *
+ * We wrap entity_info to make D5/6 compatible
+ */
+function _drush_entity_get_info($entity_type = NULL) {
+  static $entities_info;
+
+  if (!isset($entities_info)) {
+    if (drush_drupal_major_version() < 7) {
+      /*
+       * Mapping for D6/5 which mimic D7 info structure
+       */
+      $entities_info = array(
+        'node' => array(
+          'entity keys' => array(
+            'id' => 'nid',
+            'revision' => 'vid',
+            'label' => 'title',
+          ),
+          'drush' => array(
+            'defaults' => array(
+              'type' => '',
+              'title' => '',
+            ),
+            // What fields to zap for a new entity
+            'new' => array('nid', 'vid'),
+          ),
+          'load list sql' => 'select nid id from {node}',
+        ),
+        'user' => array(
+          'entity keys' => array(
+            'id' => 'nid',
+            'label' => 'name',
+          ),
+          'drush' => array(
+            'defaults' => array(
+              'name' => '',
+            ),
+            // What fields to zap for a new entity
+            'new' => array('uid'),
+            // user_save needs list of keys
+            'save needs keys' => TRUE,
+          ),
+          'load list sql' => 'select uid id from {users}',
+        ),
+        'taxonomy_vocabulary' => array(
+          'entity keys' => array(
+            'id' => 'nid',
+            'label' => 'name',
+          ),
+          'drush' => array(
+            'defaults' => array(
+              'name' => '',
+            ),
+            // What fields to zap for a new entity
+            'new' => array('vid'),
+          ),
+          'load list sql' => 'select vid id from {vocabulary}',
+        ),
+        'taxonomy_term' => array(
+          'entity keys' => array(
+            'id' => 'nid',
+            'label' => 'name',
+          ),
+          'drush' => array(
+            'defaults' => array(
+              'name' => '',
+            ),
+            // What fields to zap for a new entity
+            'new' => array('tid'),
+            'save needs' => 'array',
+          ),
+          'load list sql' => 'select tid id from {term_data}',
+          'load callback' => 'taxonomy_get_term',
+          'save callback' => 'taxonomy_save_term',
+        ),
+      );
+    }
+    else {
+      $entities_info = entity_get_info();
+      foreach ($entities_info as $key => $info) {
+        $entities_info[$key]['drush'] = array(
+          'defaults' => array(
+          ),
+        );
+        if (drush_get_option('include-fieldapi', FALSE)) {
+          // Add field related values
+          foreach ($entities_info[$key]['bundles'] as $bundle => $dummy) {
+            // Merge in the fields
+            $field_info_instances = field_info_instances($key, $bundle);
+            if ($field_info_instances) {
+              $entities_info[$key]['field_info_instances'][$bundle] = $field_info_instances;
+            }
+            // Merge in the pseudo fields
+            foreach (array('form', 'display') as $context) {
+              $field_info_extra_fields = field_info_extra_fields($entity_type, $bundle, $context);
+              if ($field_info_extra_fields) {
+                $entities_info[$key]['field_info_extra_fields'][$bundle][$context] = $field_info_extra_fields;
+              }
+            }
+          }
+        }
+      }
+      // What keys to delete on create
+      $entities_info['node']['drush']['new'] = array('nid', 'vid');
+      $entities_info['user']['drush']['new'] = array('uid');
+      $entities_info['file']['drush']['new'] = array('fid');
+
+      // Defaults
+      $entities_info['node']['drush']['defaults']['type'] = '';
+      $entities_info['node']['drush']['defaults']['title'] = '';
+      $entities_info['node']['drush']['defaults']['language'] = 'und';
+      $entities_info['node']['drush']['defaults']['body'] = array(
+        LANGUAGE_NONE => array(
+          0 => array(
+            'value' => "",
+            'format' => filter_default_format(),
+          ),
+        ),
+      );
+      // To delete a file we need the file object
+      $entities_info['file']['drush']['delete needs'] = 'entity';
+    }
+  }
+  if (isset($entity_type)) {
+    return $entities_info[$entity_type];
+  }
+  return $entities_info;
+}
+
+/**
+ * Print the given object depending on json switch
+ *
+ * @param $object
+ */
+function _drush_entity_print($object, $format = NULL, $header = '') {
+  if (drush_get_option('json')) {
+    // php-5.4.0 : JSON_PRETTY_PRINT
+    drush_print(drush_json_encode($object));
+  }
+  else {
+    if ($header) {
+      drush_print($header);
+    }
+    if ($format == DRUSH_ENTITY_SEPARATED_SPACE) {
+      drush_print(join(" ", (array) $object));
+    }
+    else {
+      //$object = drush_json_decode(drush_json_encode($object));
+      drush_print_r($object);
+    }
+  }
+}
+
+/**
+ * Prevent D5/6 from invoking wrong types
+ *
+ * @param type $type
+ */
+function _drush_entity_check_type($type) {
+  switch (drush_drupal_major_version()) {
+    case 5:
+    case 6:
+      $entity_info = _drush_entity_get_info();
+      if (!isset($entity_info[$type])) {
+        drush_die("No support for $type for drupal core < 7.x");
+      }
+  }
+}
similarity index 98%
rename from examples/example.make
rename to commands/make/EXAMPLE.make
index ae0cb06..2376597 100644 (file)
@@ -48,7 +48,7 @@ projects[] = drupal
 
 ; To include the most recent views module:
 
-projects[] = views
+projects[] = views     
 
 ; This will, by default, retrieve the latest recommended version of the project
 ; using its update XML feed on Drupal.org. If any of those defaults are not
@@ -57,7 +57,7 @@ projects[] = views
 
 ; If you want to retrieve a specific version of a project:
 
-; projects[views] = 2.16
+projects[views] = 2.16
 
 ; Or an alternative, extended syntax:
 
similarity index 90%
rename from docs/make.txt
rename to commands/make/README.txt
index 157ef6f..e693fd9 100644 (file)
@@ -61,7 +61,7 @@ specified as the key.
 
 **Project using options (see below):**
 
-    projects[drupal][version] = 7.12
+    projects[drupal][version] = 6.15
 
 Do not use both types of declarations for a single project in your makefile.
 
@@ -155,11 +155,6 @@ Do not use both types of declarations for a single project in your makefile.
 ### Project download options
 
   Use an alternative download method instead of retrieval through update XML.
-
-  If no download type is specified, make defaults the type to
-  `git`. Additionally, if no url is specified, make defaults to use
-  Drupal.org.
-
   The following methods are available:
 
 - `download[type] = file`
@@ -170,17 +165,13 @@ Do not use both types of declarations for a single project in your makefile.
 
   `md5`, `sha1`, `sha256`, or `sha512` - one or more checksums for the file. Optional.
 
-  `request_type` - the request type - get or post. post depends on
-  http://drupal.org/project/make_post. Optional.
+  `request_type` - the request type - get or post. Defaults to get. Optional.
 
   `data` - The post data to be submitted with the request. Should be a
-  valid URL query string. Requires http://drupal.org/project/make_post. Optional.
+  valid URL query string. Required.
 
   `filename` - What to name the file, if it's not an archive. Optional.
 
-  `subtree`  - if the download is an archive, only this subtree within the
-  archive will be copied to the target destination. Optional.
-
 - `download[type] = bzr`
 
   Use a bazaar repository as the source for this project. Options:
@@ -202,19 +193,6 @@ Do not use both types of declarations for a single project in your makefile.
      projects[mytheme][download][type] = "git"
      projects[mytheme][download][url] = "git://github.com/jane_doe/mytheme.git"
 
-  Shorthand is available to pull a specific revision from a git
-  repository:
-
-  projects[context_admin][revision] = "eb9f05e"
-
-  is the same as:
-
-  projects[context_admin][download][revision] = "eb9f05e"
-
-  `refspec` - the git reference to fetch and checkout. Optional.
-
-     If this is set, it will have priority over tag, revision and branch options.
-
 - `download[type] = svn`
 
   Use an SVN repository as the source for this project. Options:
@@ -348,15 +326,14 @@ Testing
 Drush make also comes with testing capabilities, designed to test drush make
 itself. Writing a new test is extremely simple. The process is as follows:
 
-1. Figure out what you want to test. Write a makefile that will test
-   this out.  You can refer to existing test makefiles for
-   examples. These are located in `DRUSH/tests/makefiles`.
+1. Figure out what you want to test. Write a makefile that will test this out.
+   You can refer to existing test makefiles for examples.
 2. Drush make your makefile, and use the --md5 option. You may also use other
    options, but be sure to take note of which ones for step 4.
 3. Verify that the result you got was in fact what you expected. If so,
    continue. If not, tweak it and re-run step 2 until it's what you expected.
 4. Using the md5 hash that was spit out from step 2, make a new entry in the
-   tests clase (DRUSH/tests/makeTest.php), following the example below.
+   tests array in drush_make.test.inc, following the example below.
     'machine-readable-name' => array(
       'name'     => 'Human readable name',
       'makefile' => 'tests/yourtest.make',
@@ -365,11 +342,7 @@ itself. Writing a new test is extremely simple. The process is as follows:
       ),
       'options'  => array('any' => TRUE, 'other' => TRUE, 'options' => TRUE),
     ),
-5. Test! Run drush test suite (see DRUSH/tests/README.txt). To just
-   run the make tests:
-
-     `phpunit --filter=makeMake .`
-
+5. Test! Run drush make-test machine-readable-name to see if the test passes.
 
 You can check for any messages you want in the message array, but the most
 basic tests would just check the build hash.
@@ -388,10 +361,10 @@ the makefile will not complete - you'll have to fill in some information before
 it is fully functional.
 
 Maintainers
------------
-- Jonathan Hedstrom (jhedstrom)
+----------
+- Jonathan Hedstrom
 - The rest of the Drush maintainers
 
 Original Author
----------------
+----------
 Dmitri Gaskin (dmitrig01)
index e88d3dc..97037e1 100644 (file)
@@ -1,8 +1,4 @@
 <?php
-/**
- * @file
- * Functions for the generate makefile command.
- */
 
 include_once 'includes/install.inc';
 include_once drupal_get_path('module', 'system') . '/system.install';
@@ -15,7 +11,7 @@ function drush_make_generate($file = NULL) {
   $all_extensions = drush_get_extensions();
   $projects = _drush_make_generate_projects($all_extensions, $version_options);
   $contents = _drush_make_generate_makefile_contents($projects);
-
+  
   if (!$file) {
     drush_print($contents);
   }
@@ -23,18 +19,18 @@ function drush_make_generate($file = NULL) {
     drush_log(dt("Wrote .make file @file", array('@file' => $file)), 'ok');
   }
   else {
-    drush_make_error('FILE_ERROR', dt("Unable to write .make file !file", array('!file' => $file)));
+    drush_make_error('FILE_ERROR', dt("Unable to write .make file %file", array('%file' => $file)));
   }
 }
 
 /**
- * Create the $version_options array from the --include-versions and
- * --exclude-versions command line options.
+ * Create the $version_options array from the --include-versions and --exclude-versions
+ * command line options.
  */
 function _drush_make_generate_get_version_options() {
   // What projects should we pin the versions for?
-  // Check the command-line options for details.
-  foreach (array("include", "exclude") as $option) {
+  // Check the command-line options for details
+  foreach(array("include", "exclude") as $option) {
     $version_options[$option] = drush_get_option("$option-versions");
     if ($version_options[$option] !== TRUE) {
       $version_options[$option] = array_filter(explode(",", $version_options[$option]));
@@ -50,18 +46,18 @@ function _drush_make_generate_projects($all_extensions, $version_options) {
   $projects = array();
 
   $system_requirements = system_requirements('runtime');
-  // Update xml expects the drupal version to be expressed as "7.x" or "8.x"
+  // update xml expects the drupal version to be expressed as "7.x" or "8.x"
   // We used to check $system_requirements['drupal']['value'], but this now
   // contains values such as "7.10-dev".
   $drupal_major_version = drush_drupal_major_version() . '.x';
-  $core_project = strtolower($system_requirements['drupal']['title']);
+  $core_project = strtolower($system_requirements['drupal']['title']); 
   $projects[$core_project] = array('_type' => 'core');
   if ($core_project != 'drupal') {
     $projects[$core_project]['custom_download'] = TRUE;
     $projects[$core_project]['type'] = 'core';
   }
   else {
-    // Drupal core - we can determine the version if required.
+    // Drupal core - we can determine the version if required
     if (_drush_generate_track_version("drupal", $version_options)) {
       $projects[$core_project]["version"] = $drupal_major_version;
     }
@@ -70,8 +66,8 @@ function _drush_make_generate_projects($all_extensions, $version_options) {
   // Non-default profiles section.
   $install_profile = variable_get('install_profile', '');
   if (!in_array($install_profile, array('default', 'standard', 'minimal')) && $install_profile != '') {
-    $projects[$install_profile]['type']
-      = $projects[$install_profile]['_type'] = 'profile';
+    $projects[$install_profile]['type'] =
+    $projects[$install_profile]['_type'] = 'profile';
     $request = array(
       'name' => $install_profile,
       'drupal_version' => $drupal_major_version,
@@ -115,12 +111,10 @@ function _drush_make_generate_projects($all_extensions, $version_options) {
       $projects[$name]['custom_download'] = TRUE;
     }
     // Add 'subdir' if the project is installed in a non-default location.
-    if (isset($project['path'])) {
-      $projects[$name] += _drush_generate_makefile_check_path($project);
-    }
+    $projects[$name] += _drush_generate_makefile_check_path($project);
     // Add version number if this project's version is to be tracked.
     if (_drush_generate_track_version($name, $version_options) && $project["version"]) {
-      $projects[$name]['version'] = preg_replace("/^" . DRUPAL_CORE_COMPATIBILITY . "-/", "", $project["version"]);
+      $projects[$name]['version'] = preg_replace("/^". DRUPAL_CORE_COMPATIBILITY . "-/", "", $project["version"]);
     }
     foreach ($project['extensions'] as $extension_name) {
       _drush_make_generate_add_patch_files($projects[$name], dirname($all_extensions[$extension_name]->filename));
@@ -157,8 +151,8 @@ function _drush_make_generate_add_patch_files(&$project, $location) {
   $patchfile = DRUPAL_ROOT . '/' . $location . '/PATCHES.txt';
   if (is_file($patchfile)) {
     foreach (file($patchfile) as $line) {
-      if (substr($line, 0, 2) == '- ') {
-        $project['patches'][] = trim(substr($line, 2));
+      if (substr($line,0,2) == '- ') {
+        $project['patches'][] = trim(substr($line,2));
       }
     }
   }
@@ -180,7 +174,7 @@ function _drush_generate_custom_project($name, $extension, $version_options) {
   $output = drush_shell_exec_output();
   if (!empty($output)) {
     $git_dir = $output[0];
-    // Find the actual base of the git repository.
+    // Find the actual base of the git repository
     $repo_root = $git_dir == ".git" ? $location : dirname($git_dir);
     // If the repository root is at the drupal root or some parent
     // of the drupal root, or some other location that could not
@@ -202,7 +196,7 @@ function _drush_generate_custom_project($name, $extension, $version_options) {
             $project['download][type'] = 'git';
             $project['download][url'] = $url;
 
-            // Fill in the branch as well.
+            // Fill in the branch as well
             drush_shell_cd_and_exec($repo_root, 'git branch');
             $output = drush_shell_exec_output();
             foreach ($output as $line) {
@@ -214,7 +208,7 @@ function _drush_generate_custom_project($name, $extension, $version_options) {
               }
             }
 
-            // Put in the commit hash.
+            // Put in the commit hash
             drush_shell_cd_and_exec($repo_root, 'git log');
             $output = drush_shell_exec_output();
             if (substr($output[0], 0, 7) == "commit ") {
@@ -224,15 +218,15 @@ function _drush_generate_custom_project($name, $extension, $version_options) {
               }
             }
 
-            // Add patch files, if any.
+            // Add patch files, if any
             _drush_make_generate_add_patch_files($project, $repo_root);
           }
         }
       }
     }
   }
-  // If we could not figure out where the extension came from, then give up and
-  // flag it as a "custom" download.
+  // If we could not figure out where the extension came from,
+  // then give up and flag it as a "custom" download
   if (!isset($project['download][type'])) {
     $project['custom_download'] = TRUE;
   }
@@ -251,40 +245,39 @@ function _drush_generate_validate_repo_location($repo_root) {
   if ((strlen(DRUPAL_ROOT) >= strlen($repo_root)) || (dirname($repo_root) == DRUPAL_ROOT)) {
     return NULL;
   }
-  // Also exclude sites/* and sites/*/{modules,themes} and profile/* and
-  // profile/*/{modules,themes}.
-  return $project_name;
+  // also exclude sites/* and sites/*/{modules,themes} and
+  // profile/* and profile/*/{modules,themes}
+  return $project_name;  
 }
 
 /**
- * Helper function to determine if a given project is to have its version
- * tracked.
+ * Helper function to determine if a given project is to have its version tracked
  */
 function _drush_generate_track_version($project, $version_options) {
   // A. If --exclude-versions has been specified:
-  // A.a. if it's a boolean, check the --include-versions option.
+  //  A.a. if it's a boolean, check the --include-versions option
   if ($version_options["exclude"] === TRUE) {
-    // A.a.1 if --include-versions has been specified, ensure it's an array.
+    // A.a.1 if --include-versions has been specified, ensure it's an array
+    // 
     if (is_array($version_options["include"])) {
       return in_array($project, $version_options["include"]);
     }
-    // A.a.2 If no include array, then we're excluding versions for ALL
-    // projects.
+    // A.a.2 If no include array, then we're excluding versions for ALL projects
     return FALSE;
   }
-  // A.b. if --exclude-versions is an array with items, check this project is in
-  // it: if so, then return FALSE.
+  //  A.b. if --exclude-versions is an array with items, check this project is in it: if so,
+  //    then return FALSE.
   elseif (is_array($version_options["exclude"]) && count($version_options["exclude"])) {
     return !in_array($project, $version_options["exclude"]);
   }
 
   // B. If by now no --exclude-versions, but --include-versions is an array,
-  // examine it for this project.
+  // examine it for this project
   if (is_array($version_options["include"]) && count($version_options["include"])) {
     return in_array($project, $version_options["include"]);
   }
 
-  // If none of the above conditions match, include version number by default.
+  // If none of the above conditions match, include version number by default
   return TRUE;
 }
 
@@ -294,9 +287,8 @@ function _drush_generate_track_version($project, $version_options) {
 function _drush_generate_makefile_check_path($project) {
   $info = array();
   $type = $project['type'];
-  $path = dirname($project['path']);
-  // Check to see if the path is in a subdir sites/all/modules or
-  // profiles/profilename/modules
+  $path = dirname(_pm_find_common_path($type, $project['extensions']));
+  // Check to see if the path is in a subdir sites/all/modules or profiles/profilename/modules
   if (preg_match('@^sites/[a-zA-Z0-9_]*/' . $type . 's/..*@', $path) || preg_match('@^sites/[a-zA-Z0-9_]*/' . $type . 's/..*@', $path)) {
     $subdir = preg_replace(array('@^[a-zA-Z0-9_]*/[a-zA-Z0-9_]*/' . $type . 's/*@', "@/$name" . '$@'), '', $path);
     if (!empty($subdir)) {
@@ -306,9 +298,6 @@ function _drush_generate_makefile_check_path($project) {
   return $info;
 }
 
-/**
- * Generate the actual contents of the .make file.
- */
 function _drush_make_generate_makefile_contents($projects) {
   $output = array();
   $custom = FALSE;
index 44323cd..1024cb1 100644 (file)
@@ -1,13 +1,9 @@
 <?php
-/**
- * @file
- * Download-specific functions for Drush Make.
- */
 
 /**
  * Downloads the given package to the destination directory.
  *
- * @return mixed
+ * @return
  *   The destination path on success, FALSE on failure.
  */
 function make_download_factory($name, $download, $download_location) {
@@ -24,16 +20,12 @@ function make_download_factory($name, $download, $download_location) {
  * Download project using drush's pm-download command.
  */
 function make_download_pm($name, $download, $download_location) {
-  $full_project_version = $name . '-' . $download['full_version'];
+  $version = $name . '-' . $download['version'];
 
   $options = array(
     'destination' => dirname($download_location),
     'yes' => TRUE,
     'package-handler' => 'wget',
-    'source' => $download['status url'],
-    // This is only relevant for profiles, but we always want the variant to
-    // be 'profile-only' so we don't end up with extra copies of core.
-    'variant' => 'profile-only',
     'cache' => TRUE,
   );
   if ($name == 'drupal') {
@@ -45,165 +37,77 @@ function make_download_pm($name, $download, $download_location) {
 
   $backend_options = array();
   if (!drush_get_option(array('verbose', 'debug'), FALSE)) {
-    $backend_options['integrate'] = TRUE;
+    $backend_options['integrate'] = FALSE;
     $backend_options['log'] = FALSE;
   }
 
   // Perform actual download with `drush pm-download`.
-  $return = drush_invoke_process('@none', 'pm-download', array($full_project_version), $options, $backend_options);
+  $return = drush_invoke_process('@none', 'pm-download', array($version), $options, $backend_options);
   if (empty($return['error_log'])) {
-    // @todo Report the URL we used for download. See
-    // http://drupal.org/node/1452672.
-    drush_log(dt('@project downloaded.', array('@project' => $full_project_version)), 'ok');
+    drush_log(dt('@project downloaded from @url', array('@project' => $version, '@url' => $download['download_link'])), 'ok');
   }
 }
 
 /**
  * Downloads a file to the specified location.
  *
- * @return mixed
+ * @return
  *   The destination directory on success, FALSE on failure.
  */
-function make_download_file($name, $download, $download_location, $cache_duration = DRUSH_CACHE_LIFETIME_DEFAULT) {
-  if ($filename = _make_download_file($download['url'], $cache_duration)) {
+function make_download_file($name, $download, $download_location) {
+  if ($filename = _make_download_file($download)) {
     if (!drush_get_option('ignore-checksums') && !_make_verify_checksums($download, $filename)) {
       return FALSE;
     }
     drush_log(dt('@project downloaded from @url.', array('@project' => $name, '@url' => $download['url'])), 'ok');
-    $download_filename = isset($download['filename']) ? $download['filename'] : '';
-    $subtree = isset($download['subtree']) ? $download['subtree'] : NULL;
-    return make_download_file_unpack($filename, $download_location, $download_filename, $subtree);
+    return make_download_file_unpack($filename, $download_location, (isset($download['filename']) ? $download['filename'] : ''));
   }
   make_error('DOWNLOAD_ERROR', dt('Unable to download @project from @url.', array('@project' => $name, '@url' => $download['url'])));
   return FALSE;
 }
 
-/**
- * Wrapper to drush_download_file().
- *
- * @param string $download
- *   The url of the file to download.
- * @param int $cache_duration
- *   The time in seconds to cache the resultant download.
- *
- * @return string
- *   The location of the downloaded file, or FALSE on failure.
- */
-function _make_download_file($download, $cache_duration = DRUSH_CACHE_LIFETIME_DEFAULT) {
-  if (drush_get_option('no-cache', FALSE)) {
-    $cache_duration = 0;
+function _make_download_file($download) {
+  // TODO cleanup so $download is always a string, since with
+  // drush_download_file(), that's all we pass in.
+  if (is_string($download)) {
+    $download = array('url' => $download);
   }
 
-  $tmp_path = make_tmp();
-  // Ensure that we aren't including the querystring when generating a filename
-  // to save our download to.
-  $file = basename(reset(explode('?', $download, 2)));
-  return drush_download_file($download, $tmp_path . '/' . $file, $cache_duration);
-}
-
-/**
- * Determines the MIME content type of the specified file. The power of this
- * function depends on whether the PHP installation has either
- * mime_content_type() or finfo installed -- if not, only tar, gz, and zip types
- * can be detected, and other file types will return 'unknown'.
- *
- * @return mixed
- *   The MIME content type of the file, 'unknown', or FALSE on error.
- */
-function drush_mime_content_type($filename) {
-  // Tar files have no magic value, so we can detect them only by filename.
-  if (strrpos($filename, '.tar') === (strlen($filename) - 4)) {
-    return 'application/x-tar';
+  // Default to use cache.
+  $cache = drush_get_option('cache');
+  if (!drush_get_option('no-cache', FALSE)) {
+    drush_set_option('cache', TRUE);
   }
+  drush_set_option('cache', $cache);
 
-  $content_type = '';
-
-  // mime_content_type() and the finfo class are not available everywhere.
-  if (function_exists('mime_content_type')) {
-    $content_type = mime_content_type($filename);
-  }
-  elseif (class_exists('finfo')) {
-    $finfo = new finfo(FILEINFO_MIME);
-    // We only want the first part, before the ;
-    $type = explode(';', $finfo->file($filename));
-    $content_type = $type[0];
-  }
-
-  // If we found what we're looking for, we're done.
-  if (!empty($content_type) && ($content_type == 'application/x-gzip' || $content_type == 'application/x-zip')) {
-    return $content_type;
-  }
-
-  // If PHP's built-ins aren't present or PHP is configured in such a way that
-  // all these files are considered octet-stream (e.g with mod_mime_magic and
-  // an http conf that's serving all archives as octet-stream for other
-  // reasons) we'll detect (a few select) mime types on our own by examing the
-  // file's magic header bytes.
-  if ($file = fopen($filename, 'rb')) {
-    $first = fread($file, 2);
-    fclose($file);
-
-    if ($first === FALSE) {
-      return FALSE;
-    }
-
-    // Interpret the two bytes as a little endian 16-bit unsigned int.
-    $data = unpack('v', $first);
-    switch ($data[1]) {
-      case 0x8b1f:
-        // First two bytes of gzip files are 0x1f, 0x8b.
-        // See http://www.gzip.org/zlib/rfc-gzip.html#header-trailer.
-        return 'application/x-gzip';
-
-      case 0x4b50:
-        // First two bytes of zip files are 0x50, 0x4b ('PK').
-        // See http://en.wikipedia.org/wiki/Zip_(file_format)#File_headers.
-        return 'application/zip';
-
-      default:
-        // Not a file type we can detect. If PHP's built-ins gave us
-        // something, we can at least return that.
-        return !empty($content_type) ? $content_type : 'unknown';
-    }
-  }
-  return FALSE;
+  $tmp_path = make_tmp();
+  $file = basename($download['url']);
+  return drush_download_file($download['url'], $tmp_path . '/' . $file);
 }
 
 /**
  * Unpacks a file to the specified download location.
  *
- * @return mixed
+ * @return
  *   The download location on success, FALSE on failure.
  */
-function make_download_file_unpack($filename, $download_location, $name, $subtree = NULL) {
+function make_download_file_unpack($filename, $download_location, $name) {
+  $extension = array_pop(explode('.', $filename));
   $success = FALSE;
-
-  switch (drush_mime_content_type($filename)) {
-    case 'application/x-gzip':
-      // I'd like to just use tar -z, but apparently it breaks on windoze. Why
-      // do they always have to ruin everything?
-      $success = make_download_file_unpack_gzip($filename, $download_location, $subtree);
+  switch ($extension) {
+    case 'gz':
+    case 'tgz':
+      // I'd like to just use tar -z, but apparently it breaks on windoze. Why do they always have to ruin everything?
+      $success = make_download_file_unpack_gzip($filename, $download_location);
       break;
-
-    case 'application/x-tar':
-      $success = make_download_file_unpack_tar($filename, $download_location, $subtree);
+    case 'tar':
+      $success = make_download_file_unpack_tar($filename, $download_location);
       break;
-
-    case 'application/zip':
-      $success = make_download_file_unpack_zip($filename, $download_location, $subtree);
+    case 'zip':
+      $success = make_download_file_unpack_zip($filename, $download_location);
       break;
-
     default:
-      // If this is an individual file, and no filename has been specified,
-      // assume the original name.
-      if (is_file($filename) && !$name) {
-        $name = basename($filename);
-      }
-
-      // The destination directory has already been created by
-      // findDownloadLocation().
-      $destination = $download_location . ($name ? '/' . $name : '');
-      $success = drush_move_dir($filename, $destination, TRUE);
+      $success = drush_shell_exec('mv %s %s', $filename, $download_location . ($name ? '/' . $name : ''));
   }
   return $success ? $download_location : FALSE;
 }
@@ -211,28 +115,28 @@ function make_download_file_unpack($filename, $download_location, $name, $subtre
 /**
  * Unpacks a tar file to the specified location.
  *
- * @return boolean
+ * @return
  *   TRUE or FALSE depending on whether the operation was successful.
  */
-function make_download_file_unpack_tar($filename, $download_location, $subtree = NULL) {
-  $tmp_path = drush_tempdir();
+function make_download_file_unpack_tar($filename, $download_location) {
+  $tmp_path = make_tmp();
 
   list($main_directory) = array_reverse(explode('/', $download_location));
 
-  drush_shell_exec('%s -x -C %s -f %s', drush_get_tar_executable(), $tmp_path, $filename);
+  drush_mkdir($tmp_path . '/__unzip__');
+  drush_shell_exec('tar -x -C %s -f %s', $tmp_path . '/__unzip__', $filename);
 
-  return _make_download_file_move($tmp_path, $filename, $download_location, $subtree);
+  return _make_download_file_move($tmp_path, $filename, $download_location);
 }
 
 /**
  * Unpacks a gzip file to the specified location.
  *
- * @return boolean
+ * @return
  *   TRUE or FALSE depending on whether the operation was successful.
  */
-function make_download_file_unpack_gzip($filename, $download_location, $subtree = NULL) {
-  // Find out where contents will end up. Retrieve last column of output using
-  // awk.
+function make_download_file_unpack_gzip($filename, $download_location) {
+  // Find out where contents will end up. Retrieve last column of output using awk.
   drush_shell_exec("gzip --list %s", $filename);
   $info = drush_shell_exec_output();
   if ($info) {
@@ -247,65 +151,52 @@ function make_download_file_unpack_gzip($filename, $download_location, $subtree
     if (isset($file)) {
       // Unzip it and then delete the tar file.
       drush_shell_exec('gzip -d %s', $filename);
-      return make_download_file_unpack_tar($file, $download_location, $subtree);
+      return make_download_file_unpack_tar($file, $download_location);
     }
   }
 }
 
-/**
- * Unpack a zip file.
- */
-function make_download_file_unpack_zip($filename, $download_location, $subtree = NULL) {
-  $tmp_path = drush_tempdir();
+function make_download_file_unpack_zip($filename, $download_location) {
+  $tmp_path = make_tmp();
 
   list($main_directory) = array_reverse(explode('/', $download_location));
 
-  $result = drush_shell_exec("unzip %s -d %s", $filename, $tmp_path);
-
-  if (!$result) {
-    make_error('UNZIP_ERROR', dt('Unable to unzip @filename.', array('@filename' => $filename)));
-    return FALSE;
-  }
+  drush_mkdir($tmp_path . '/__unzip__');
+  drush_shell_exec("unzip %s -d %s", $filename, $tmp_path . '/__unzip__');
 
-  return _make_download_file_move($tmp_path, $filename, $download_location, $subtree);
+  return _make_download_file_move($tmp_path, $filename, $download_location);
 }
 
-/**
- * Move a downloaded and unpacked file or directory into place.
- *
- * TODO merge with core drush methods.
- */
-function _make_download_file_move($tmp_path, $filename, $download_location, $subtree = NULL) {
-  $lines = drush_scan_directory($tmp_path, '/./', array('.', '..'), 0, FALSE, 'filename', 0, TRUE);
+function _make_download_file_move($tmp_path, $filename, $download_location) {
+  drush_shell_exec('ls %s', $tmp_path . '/__unzip__');
+  $lines = drush_shell_exec_output();
   $main_directory = basename($download_location);
   if (count($lines) == 1) {
     $directory = array_shift($lines);
-    if ($directory->basename != $main_directory) {
-      drush_move_dir($directory->filename, $tmp_path . DIRECTORY_SEPARATOR . $main_directory, TRUE);
+    if ($directory != $main_directory) {
+      drush_shell_exec('mv %s %s', $tmp_path . '/__unzip__/' . $directory, $tmp_path . '/__unzip__/' . $main_directory);
     }
-    drush_copy_dir($tmp_path . DIRECTORY_SEPARATOR . $main_directory . DIRECTORY_SEPARATOR . $subtree, $download_location, TRUE);
-    drush_delete_dir($tmp_path, TRUE);
+    drush_shell_exec('cp -Rf %s %s', $tmp_path . '/__unzip__/' . $main_directory, dirname($download_location));
+    drush_shell_exec('rm -rf %s', $tmp_path . '/__unzip__');
   }
   elseif (count($lines) > 1) {
-    drush_delete_dir($download_location, TRUE);
-    drush_move_dir($tmp_path . DIRECTORY_SEPARATOR . $subtree, $download_location, TRUE);
+    drush_shell_exec('rm -rf %s', $download_location);
+    drush_shell_exec('mv %s %s', $tmp_path . '/__unzip__', $download_location);
   }
 
   // Remove the tarball.
   if (file_exists($filename)) {
-    drush_delete_dir($filename, TRUE);
+    drush_shell_exec('rm %s', $filename);
   }
 
-  if (file_exists($tmp_path)) {
-    drush_delete_dir($tmp_path, TRUE);
+  if (file_exists($tmp_path . '/__unzip__')) {
+    drush_shell_exec('rm -rf %s', $tmp_path . '/__unzip__');
   }
   return TRUE;
 }
 
 
-/**
- * For backwards compatibility.
- */
+// Backwards compatibility.
 function make_download_get($name, $download, $download_location) {
   return make_download_file($name, $download, $download_location);
 }
@@ -313,193 +204,113 @@ function make_download_get($name, $download, $download_location) {
 /**
  * Checks out a git repository to the specified download location.
  *
- * Allowed parameters in $download, in order of precedence:
- *   - 'tag'
- *   - 'revision'
- *   - 'branch'
- *
- * This will also attempt to write out release information to the
- * .info file if the 'no-gitinfofile' option is FALSE. If
- * $download['full_version'] is present, this will be used, otherwise,
- * version will be set in this order of precedence:
- *   - 'tag'
- *   - 'branch'
- *   - 'revision'
- *
- * @return mixed
+ * @return
  *   The download location on success, FALSE otherwise.
  */
 function make_download_git($name, $download, $download_location) {
   $tmp_path = make_tmp();
   $wc = drush_get_option('working-copy');
+
+  // check if branch option is set in info file, otherwise set default to master branch
+  $download['branch'] = isset($download['branch']) ? $download['branch'] : 'master';
+  // check if tag option is set in info file, otherwise we set it to false
+  $download['tag'] = isset($download['tag']) ? $download['tag'] : FALSE;
+  // check if specific revision is set in info file
+  $download['revision'] = isset($download['revision']) ? $download['revision'] : FALSE;
+
   // If no download URL specified, assume anonymous clone from git.drupal.org.
-  $download['url'] = isset($download['url']) ? $download['url'] : "http://git.drupal.org/project/$name.git";
+  $download['url'] = isset($download['url']) ? $download['url'] : "git://git.drupal.org/project/$name.git";
   // If no working-copy download URL specified, assume it is the same.
   $download['wc_url'] = isset($download['wc_url']) ? $download['wc_url'] : $download['url'];
 
-  // If not a working copy, and if --no-cache has not been explicitly
-  // declared, create a new git reference cache of the remote repository,
-  // or update the existing cache to fetch recent changes.
-  // @see package_handler_download_project()
-  $cache = !$wc && !drush_get_option('no-cache', FALSE);
-  if ($cache && ($git_cache = drush_directory_cache('git'))) {
-    $project_cache = $git_cache . '/' . $name . '-' . md5($download['url']);
-    // Set up a new cache, if it doesn't exist.
-    if (!file_exists($project_cache)) {
-      $command = 'git clone --mirror';
-      if (drush_get_context('DRUSH_VERBOSE')) {
-        $command .= ' --verbose --progress';
-      }
-      $command .= ' %s %s';
-      drush_shell_cd_and_exec($git_cache, $command, $download['url'], $project_cache);
-    }
-    else {
-      // Update the --mirror clone.
-      drush_shell_cd_and_exec($project_cache, 'git remote update');
-    }
-    $git_cache = $project_cache;
-  }
-
   // Use working-copy download URL if --working-copy specified.
   $url = $wc ? $download['wc_url'] : $download['url'];
 
   $tmp_location = drush_tempdir() . '/' . basename($download_location);
 
-  $command = 'git clone %s %s';
-  if (drush_get_context('DRUSH_VERBOSE')) {
-    $command .= ' --verbose --progress';
-  }
-  if ($cache) {
-    $command .= ' --reference ' . drush_escapeshellarg($git_cache);
-  }
-
-  // Before we can checkout anything, we need to clone the repository.
-  if (!drush_shell_exec($command, $url, $tmp_location)) {
-    make_error('DOWNLOAD_ERROR', dt('Unable to clone @project from @url.', array('@project' => $name, '@url' => $url)));
-    return FALSE;
-  }
-
-  drush_log(dt('@project cloned from @url.', array('@project' => $name, '@url' => $url)), 'ok');
+  // clone the given repository
+  if (drush_shell_exec("git clone %s %s", $url, $tmp_location)) {
+    drush_log(dt('@project cloned from @url.', array('@project' => $name, '@url' => $url)), 'ok');
 
-  // Get the current directory (so we can move back later).
-  $cwd = getcwd();
-  // Change into the working copy of the cloned repo.
-  chdir($tmp_location);
+    // GIT Checkout only work on a ready cloned repo. So we switch to branch or to tag (only if we have no branch) after cloneing.
+    if ($download['branch'] !== 'master' || $download['tag'] || $download['revision'] || !empty($download['submodule'])) {
 
-  // We want to use the most specific target possible, so first try a refspec.
-  if (!empty($download['refspec'])) {
-    if (drush_shell_exec("git fetch %s %s", $url, $download['refspec'])) {
-      drush_log(dt("Fetched refspec !refspec.", array('!refspec' => $download['refspec'])), 'ok');
+      // get current directory (for move back later)
+      $cwd = getcwd();
+      // change path to working copy of cloned repo
+      chdir($tmp_location);
 
-      if (drush_shell_exec("git checkout FETCH_HEAD")) {
-        drush_log(dt("Checked out FETCH_HEAD."), 'info');
+      // Progress branch / tag / revision download. Ensure that only one option ist set (branch OR tag OR revision)
+      // check if branch a other than master
+      if ($download['branch'] !== 'master' && !$download['tag'] && !$download['revision']) {
+        if (drush_shell_exec("git checkout %s", $download['branch'])) {
+          drush_log(dt("Checked out branch @branch.", array('@branch' => $download['branch'])), 'ok');
+        }
+        elseif (drush_shell_exec("git checkout -b %s %s", $download['branch'], 'origin/' . $download['branch'])) {
+          drush_log(dt("Checked out branch @branch.", array('@branch' => $download['branch'])), 'ok');
+        }
+        else {
+          make_error('DOWNLOAD_ERROR', dt("Unable to check out branch @branch.", array('%branch' => $download['branch'])));
+        }
       }
-    }
-    else {
-      make_error('DOWNLOAD_ERROR', dt("Unable to fetch the refspec @refspec from @project.", array('@refspec' => $download['refspec'], '@project' => $name)));
-    }
-  }
-
-  // If there wasn't a refspec, try a tag.
-  elseif (!empty($download['tag'])) {
-    // @TODO: change checkout to refs path.
-    if (drush_shell_exec("git checkout %s", 'refs/tags/' . $download['tag'])) {
-      drush_log(dt("Checked out tag @tag.", array('@tag' => $download['tag'])), 'ok');
-    }
-    else {
-      make_error('DOWNLOAD_ERROR', dt("Unable to check out tag @tag.", array('@tag' => $download['tag'])));
-    }
-  }
-
-  // If there wasn't a tag, try a specific revision hash.
-  elseif (!empty($download['revision'])) {
-    if (drush_shell_exec("git checkout %s", $download['revision'])) {
-      drush_log(dt("Checked out revision @revision.", array('@revision' => $download['revision'])), 'ok');
-    }
-    else {
-      make_error('DOWNLOAD_ERROR', dt("Unable to checkout revision @revision", array('@revision' => $download['revision'])));
-    }
-  }
-
-  // If not, see if we at least have a branch.
-  elseif (!empty($download['branch'])) {
-    if (drush_shell_exec("git checkout %s", $download['branch'])) {
-      drush_log(dt("Checked out branch @branch.", array('@branch' => $download['branch'])), 'ok');
-    }
-    elseif (drush_shell_exec("git checkout -b %s %s", $download['branch'], 'origin/' . $download['branch'])) {
-      drush_log(dt('Checked out branch origin/@branch.', array('@branch' => $download['branch'])), 'ok');
-    }
-    else {
-      make_error('DOWNLOAD_ERROR', dt('Unable to check out branch @branch.', array('@branch' => $download['branch'])));
-    }
-  }
-
-  if (!empty($download['submodule'])) {
-    $command = 'git submodule update';
-    foreach ($download['submodule'] as $option) {
-      $command .= ' --%s';
-    }
-    if (call_user_func_array('drush_shell_exec', array_merge(array($command), $download['submodule']))) {
-      drush_log(dt('Initialized registered submodules.'), 'ok');
-    }
-    else {
-      make_error('DOWNLOAD_ERROR', dt('Unable to initialize submodules.'));
-    }
-  }
-
-  // Move back to last current directory (first line).
-  chdir($cwd);
-
-  // Handle .info file re-writing (if so desired).
-  if (!drush_get_option('no-gitinfofile', FALSE)) {
-    // Figure out the proper version string to use based on the .make file.
-    // Best case is the .make file author told us directly.
-    if (!empty($download['full_version'])) {
-      $full_version = $download['full_version'];
-    }
-    // Next best is if we have a tag, since those are identical to versions.
-    elseif (!empty($download['tag'])) {
-      $full_version = $download['tag'];
-    }
-    // If we have a branch, append '-dev'.
-    elseif (!empty($download['branch'])) {
-      $full_version = $download['branch'] . '-dev';
-    }
-    // Ugh. Not sure what else we can do in this case.
-    elseif (!empty($download['revision'])) {
-      $full_version = $download['revision'];
-    }
-    // Probably can never reach this case.
-    else {
-      $full_version = 'unknown';
+      // progress if: tag is set but not the others
+      elseif ($download['branch'] == 'master' && $download['tag'] && !$download['revision']) {
+        // @TODO: change checkout to refs path
+        if (drush_shell_exec("git checkout %s", 'refs/tags/' . $download['tag'])) {
+          drush_log(dt("Checked out tag @tag.", array('@tag' => $download['tag'])), 'ok');
+        }
+        else {
+          make_error('DOWNLOAD_ERROR', dt("Unable to check out tag @tag.", array('@tag' => $download['tag'])));
+        }
+      }
+      // progress if: revision is set but not the others
+      elseif ($download['branch'] == 'master' && !$download['tag'] && $download['revision']) {
+        if (drush_shell_exec("git checkout %s", $download['revision'])) {
+          drush_log(dt("Checked out revision @revision.", array('@revision' => $download['revision'])), 'ok');
+        }
+        else {
+          make_error('DOWNLOAD_ERROR', dt("Unable to checkout revision @revision", array('@revision' => $download['revision'])));
+        }
+      }
+      // more than one option is set so we throw a error message
+      elseif ($download['branch'] !== 'master' || $download['tag'] || $download['revision']) {
+        make_error('DOWNLOAD_ERROR', dt("You can only specific branch or tag or revision but not combined in make file."));
+        return false;
+      }
+      if (!empty($download['submodule'])) {
+        $command = 'git submodule update';
+        foreach ($download['submodule'] as $option) {
+          $command .= ' --%s';
+        }
+        if (call_user_func_array('drush_shell_exec', array_merge(array($command), $download['submodule']))) {
+          drush_log(dt('Initialized registered submodules.'), 'ok');
+        }
+        else {
+          make_error('DOWNLOAD_ERROR', dt('Unable to initialize submodules.'));
+        }
+      }
+      // move back to last current directory (first line)
+      chdir($cwd);
     }
 
-    // If the version string ends in '.x-dev' do the Git magic to figure out
-    // the appropriate 'rebuild version' string, e.g. '7.x-1.2+7-dev'.
-    $matches = array();
-    if (preg_match('/^(.+).x-dev$/', $full_version, $matches)) {
-      require_once dirname(__FILE__) . '/../pm/package_handler/git_drupalorg.inc';
-      $full_version = drush_pm_git_drupalorg_compute_rebuild_version($tmp_location, $matches[1]);
+    // Remove .git/ directory if working-copy flag was not specified.
+    if (!$wc && file_exists($tmp_location . '/.git')) {
+      drush_shell_exec("rm -rf %s", $tmp_location . '/.git');
     }
-    require_once dirname(__FILE__) . '/../pm/pm.drush.inc';
-    drush_pm_inject_info_file_metadata($tmp_location, $name, $full_version);
+    drush_shell_exec('cp -Rf %s %s', $tmp_location, dirname($download_location));
+    return dirname($tmp_location);
   }
-
-  // Remove .git/ directory if working-copy flag was not specified.
-  if (!$wc && file_exists($tmp_location . '/.git')) {
-    drush_delete_dir($tmp_location . '/.git', TRUE);
+  else {
+    make_error('DOWNLOAD_ERROR', dt('Unable to clone @project from @url.', array('@project' => $name, '@url' => $url)));
   }
 
-  // Move the directory into the final resting location.
-  drush_copy_dir($tmp_location, $download_location, TRUE);
-
-  return dirname($tmp_location);
+  return FALSE;
 }
 
 /**
  * Checks out a Bazaar repository to the specified download location.
  *
- * @return mixed
+ * @return
  *   The download location on success, FALSE otherwise.
  */
 function make_download_bzr($name, $download, $download_location) {
@@ -530,7 +341,7 @@ function make_download_bzr($name, $download, $download_location) {
     array_unshift($args, $command);
     if (call_user_func_array('drush_shell_exec', $args)) {
       drush_log(dt('@project downloaded from @url.', array('@project' => $name, '@url' => $download['url'])), 'ok');
-      drush_copy_dir($tmp_location, $download_location, TRUE);
+      drush_shell_exec('cp -Rf %s %s', $tmp_location, dirname($download_location));
       return dirname($download_location);
     }
   }
@@ -538,14 +349,14 @@ function make_download_bzr($name, $download, $download_location) {
     $download['url'] = dt("unspecified location");
   }
   make_error('DOWNLOAD_ERROR', dt('Unable to download @project from @url.', array('@project' => $name, '@url' => $download['url'])));
-  drush_delete_dir(dirname($tmp_location), TRUE);
+  drush_shell_exec('rm -rf %s', dirname($tmp_location));
   return FALSE;
 }
 
 /**
  * Checks out an SVN repository to the specified download location.
  *
- * @return mixed
+ * @return
  *   The download location on success, FALSE otherwise.
  */
 function make_download_svn($name, $download, $download_location) {
@@ -589,12 +400,7 @@ function make_download_svn($name, $download, $download_location) {
     array_unshift($args, $command);
     $result = call_user_func_array($function, $args);
     if ($result) {
-      $args = array(
-        '@project' => $name,
-        '@command' => $command,
-        '@url' => $download['url'],
-      );
-      drush_log(dt('@project @command from @url.', $args), 'ok');
+      drush_log(dt('@project @command from @url.', array('@project' => $name, '@command' => $command, '@url' => $download['url'])), 'ok');
       return $download_location;
     }
     else {
@@ -622,13 +428,7 @@ function _make_verify_checksums($info, $filename) {
       if (!empty($info[$algo])) {
         $hash = _make_hash($algo, $content);
         if ($hash !== $info[$algo]) {
-          $args = array(
-            '@algo' => $algo,
-            '@file' => basename($filename),
-            '@expected' => $info[$algo],
-            '@hash' => $hash,
-          );
-          make_error('DOWNLOAD_ERROR', dt('Checksum @algo verification failed for @file. Expected @expected, received @hash.', $args));
+           make_error('DOWNLOAD_ERROR', dt('Checksum @algo verification failed for @file. Expected @expected, received @hash.', array('@algo' => $algo, '@file' => basename($filename), '@expected' => $info[$algo], '@hash' => $hash)));
           return FALSE;
         }
       }
index b72f4e3..771266a 100644 (file)
@@ -1,12 +1,5 @@
 <?php
-/**
- * @file
- * Drush Make commands.
- */
 
-/**
- * Default localization server for downloading translations.
- */
 define('MAKE_DEFAULT_L10N_SERVER', 'http://localize.drupal.org/l10n_server.xml');
 
 /**
@@ -20,7 +13,7 @@ include_once 'make.download.inc';
 include_once 'make.project.inc';
 
 /**
- * Implements hook_drush_command().
+ * Implementation of hook_drush_command().
  */
 function make_drush_command() {
   $items['make'] = array(
@@ -33,42 +26,25 @@ function make_drush_command() {
     'examples' => array(
       'drush make example.make example' => 'Build the example.make makefile in the example directory.',
       'drush make --no-core --contrib-destination=. installprofile.make' => 'Build an installation profile within an existing Drupal site',
-      'drush make http://example.com/example.make example' => 'Build the remote example.make makefile in the example directory.',
     ),
     'options' => array(
       'version' => 'Print the make API version and exit.',
-      'concurrency' => array(
-        'description' => 'Limits the number of concurrent projects that will be processed at the same time. The default is 4.',
-        'example-value' => '1',
-      ),
+      'concurrency' => 'Limits the number of concurrent projects that will be processed at the same time. The default is 4.',
       'contrib-destination' => 'Specify a path under which modules and themes should be placed. Defaults to sites/all.',
       'force-complete' => 'Force a complete build even if errors occur.',
       'ignore-checksums' => 'Ignore md5 checksums for downloads.',
-      'md5' => array(
-        'description' => 'Output an md5 hash of the current build after completion. Use --md5=print to print to stdout.',
-        'example-value' => 'print',
-        'value' => 'optional',
-      ),
+      'md5' => 'Output an md5 hash of the current build after completion. Use --md5=print to print to stdout.',
       'make-update-default-url' => 'The default location to load the XML update information from.',
       'no-cache' => 'Do not use the pm-download caching (defaults to using this).',
       'no-clean' => 'Leave temporary build directories in place instead of cleaning up after completion.',
       'no-core' => 'Do not require a Drupal core project to be specified.',
       'no-patch-txt' => 'Do not write a PATCHES.txt file in the directory of each patched project.',
-      'no-gitinfofile' => 'Do not modify .info files when cloning from Git.',
       'prepare-install' => 'Prepare the built site for installation. Generate a properly permissioned settings.php and files directory.',
       'tar' => 'Generate a tar archive of the build. The output filename will be [build path].tar.gz.',
       'test' => 'Run a temporary test build and clean up.',
       'translations' => 'Retrieve translations for the specified comma-separated list of language(s) if available for all projects.',
       'working-copy' => 'Preserves VCS directories, like .git, for projects downloaded using such methods.',
       'download-mechanism' => 'How to download files. Should be autodetected, but this is an override if it doesn\'t work. Options are "curl" and "make" (a native download method).',
-      'projects' => array(
-        'description' => 'Restrict the make to this comma-separated list of projects. To specify all projects, pass *.',
-        'example' => 'views,ctools',
-      ),
-      'libraries' => array(
-        'description' => 'Restrict the make to this comma-separated list of libraries. To specify all libraries, pass *.',
-        'example' => 'tinymce',
-      ),
     ),
     'engines' => array('release_info'),
     'topics' => array('docs-make', 'docs-make-example'),
@@ -105,14 +81,14 @@ function make_drush_command() {
   );
 
   // Add docs topic.
-  $docs_dir = drush_get_context('DOC_PREFIX', DRUSH_BASE_PATH);
+  $make_dir = dirname(__FILE__);
   $items['docs-make'] = array(
     'description' => 'Drush Make overview with examples',
     'hidden' => TRUE,
     'topic' => TRUE,
     'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
     'callback' => 'drush_print_file',
-    'callback arguments' => array($docs_dir . '/docs/make.txt'),
+    'callback arguments' => array($make_dir . '/README.txt'),
   );
   $items['docs-make-example'] = array(
     'description' => 'Drush Make example makefile',
@@ -120,18 +96,18 @@ function make_drush_command() {
     'topic' => TRUE,
     'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
     'callback' => 'drush_print_file',
-    'callback arguments' => array($docs_dir . '/examples/example.make'),
+    'callback arguments' => array($make_dir . '/EXAMPLE.make'),
   );
   return $items;
 }
 
 /**
- * Implements hook_drush_help().
+ * Implementation of hook_drush_help().
  */
 function make_drush_help($section) {
   switch ($section) {
     case 'drush:make':
-      return 'Turns a makefile into a Drupal codebase. For a full description of options and makefile syntax, see docs/make.txt and examples/example.make.';
+      return 'Turns a makefile into a Drupal codebase. For a full description of options and makefile syntax, see the README.txt included with drush make.';
     case 'drush:make-generate':
       return 'Generate a makefile from the current Drupal site, specifying project version numbers unless not known or otherwise specified. Unversioned projects will be interpreted later by drush make as "most recent stable release"';
   }
@@ -140,16 +116,12 @@ function make_drush_help($section) {
 /**
  * Command argument complete callback.
  *
- * @return array
+ * @return
  *   Strong glob of files to complete on.
  */
 function make_make_complete() {
   return array(
     'files' => array(
-      'directories' => array(
-        'pattern' => '*',
-        'flags' => GLOB_ONLYDIR,
-      ),
       'make' => array(
         'pattern' => '*.make',
       ),
@@ -173,14 +145,6 @@ function drush_make($makefile = NULL, $build_path = NULL) {
   }
 
   $info = make_parse_info_file($makefile);
-
-  // Support making just a portion of a make file.
-  $include_only = array(
-    'projects' => array_filter(drush_get_option_list('projects')),
-    'libraries' => array_filter(drush_get_option_list('libraries')),
-  );
-  $info = make_prune_info_file($info, $include_only);
-
   if ($info === FALSE || ($info = make_validate_info_file($info)) === FALSE) {
     return FALSE;
   }
@@ -211,7 +175,7 @@ function drush_make_process($directory) {
 }
 
 /**
- * Implements drush_hook_post_COMMAND() for the make command.
+ * Implementation of drush_hook_post_COMMAND() for the make command.
  */
 function drush_make_post_make($makefile = NULL, $build_path = NULL) {
   if (drush_get_option('version')) {
@@ -244,9 +208,6 @@ function drush_make_post_make($makefile = NULL, $build_path = NULL) {
   make_clean_tmp();
 }
 
-/**
- * Process all projects specified in the make file.
- */
 function make_projects($recursion, $contrib_destination, $info, $build_path) {
   $projects = array();
   if (empty($info['projects'])) {
@@ -296,10 +257,8 @@ function make_projects($recursion, $contrib_destination, $info, $build_path) {
       $project['l10n_url'] = MAKE_DEFAULT_L10N_SERVER;
     }
 
-    // For convenience: define $request to be compatible with release_info
-    // engine.
-    // TODO: refactor to enforce 'make' to internally work with release_info
-    // keys.
+    // For convenience: define $request to be compatible with release_info engine.
+    // TODO: refactor to enforce 'make' to internally work with release_info keys.
     $request = make_prepare_request($project);
 
     if ($project['location'] != RELEASE_INFO_DEFAULT_URL && !isset($project['type'])) {
@@ -341,11 +300,9 @@ function make_projects($recursion, $contrib_destination, $info, $build_path) {
 
   foreach ($projects as $type => $type_projects) {
     foreach ($type_projects as $project) {
-      if (make_project_needs_release_info($project)) {
-        // For convenience: define $request to be compatible with release_info
-        // engine.
-        // TODO: refactor to enforce 'make' to internally work with release_info
-        // keys.
+      if ($project['location'] == RELEASE_INFO_DEFAULT_URL && (!isset($project['type']) || !isset($project['download']))) {
+        // For convenience: define $request to be compatible with release_info engine.
+        // TODO: refactor to enforce 'make' to internally work with release_info keys.
         $request = make_prepare_request($project, $type);
         // Set the cache option based on our '--no-cache' option.
         $cache_before = drush_get_option('cache');
@@ -360,9 +317,8 @@ function make_projects($recursion, $contrib_destination, $info, $build_path) {
         if (!isset($project['download'])) {
           $project['download'] = array(
             'type' => 'pm',
-            'full_version' => $release['version'],
+            'version' => $release['version'],
             'download_link' => $release['download_link'],
-            'status url' => $request['status url'],
           );
         }
       }
@@ -411,8 +367,6 @@ function make_projects($recursion, $contrib_destination, $info, $build_path) {
       );
 
       $common_options = drush_redispatch_get_options();
-      // Merge in stdin options since we process makefiles recursively. See http://drupal.org/node/1510180.
-      $common_options = array_merge($common_options, drush_get_context('stdin'));
       // Package handler should use 'wget'.
       $common_options['package-handler'] = 'wget';
 
@@ -426,7 +380,7 @@ function make_projects($recursion, $contrib_destination, $info, $build_path) {
       // Unless --verbose or --debug are passed, quiter backend output.
       if (empty($common_options['verbose']) && empty($common_options['debug'])) {
         $backend_options['#output-label'] = FALSE;
-        $backend_options['integrate'] = TRUE;
+        $backend_options['integrate'] = FALSE;
       }
       drush_backend_invoke_concurrent($invocations, $common_options, $backend_options, 'make-process', '@none');
     }
@@ -434,9 +388,6 @@ function make_projects($recursion, $contrib_destination, $info, $build_path) {
   return TRUE;
 }
 
-/**
- * Process all libraries specified in the make file.
- */
 function make_libraries($contrib_destination, $info, $build_path) {
   if (empty($info['libraries'])) {
     return;
@@ -444,7 +395,7 @@ function make_libraries($contrib_destination, $info, $build_path) {
   $ignore_checksums = drush_get_option('ignore-checksums');
   foreach ($info['libraries'] as $key => $library) {
     if (!is_string($key) || !is_array($library)) {
-      // TODO Print a prettier message.
+      // TODO Print a prettier message
       continue;
     }
     // Merge the known data onto the library info.
@@ -464,9 +415,6 @@ function make_libraries($contrib_destination, $info, $build_path) {
   }
 }
 
-/**
- * The path where the final build will be placed.
- */
 function make_build_path($build_path) {
   static $saved_path;
   if (isset($saved_path)) {
@@ -494,34 +442,20 @@ function make_build_path($build_path) {
   return $build_path;
 }
 
-/**
- * Move the completed build into place.
- */
 function make_move_build($build_path) {
   $tmp_path = make_tmp();
   $ret = TRUE;
   if ($build_path == '.') {
-    $info = drush_scan_directory($tmp_path . DIRECTORY_SEPARATOR . '__build__', '/./', array('.', '..'), 0, FALSE, 'filename', 0, TRUE);
+    drush_shell_exec('ls -A %s', $tmp_path . '/__build__');
+    $info = drush_shell_exec_output();
     foreach ($info as $file) {
-      $destination = $build_path . DIRECTORY_SEPARATOR . $file->basename;
-      if (file_exists($destination)) {
-        // To prevent the removal of top-level directories such as 'modules' or
-        // 'themes', descend in a level if the file exists.
-        // TODO: This only protects one level of directories from being removed.
-        $files = drush_scan_directory($file->filename, '/./', array('.', '..'), 0, FALSE);
-        foreach ($files as $file) {
-          $ret = $ret && drush_copy_dir($file->filename, $destination . DIRECTORY_SEPARATOR . $file->basename, FILE_EXISTS_MERGE);
-        }
-      }
-      else {
-        $ret = $ret && drush_copy_dir($file->filename, $destination);
-      }
+      $ret = $ret && drush_shell_exec("cp -Rf %s %s", $tmp_path . '/__build__/' . $file, $build_path);
     }
   }
   else {
     drush_mkdir(dirname($build_path));
-    $ret = drush_move_dir($tmp_path . DIRECTORY_SEPARATOR . '__build__', $tmp_path . DIRECTORY_SEPARATOR . basename($build_path), TRUE);
-    $ret = $ret && drush_copy_dir($tmp_path . DIRECTORY_SEPARATOR . basename($build_path), $build_path);
+    drush_shell_exec("mv %s %s", $tmp_path . '/__build__', $tmp_path . '/' . basename($build_path));
+    drush_shell_exec("cp -Rf %s %s", $tmp_path . '/' . basename($build_path), dirname($build_path));
   }
   if (!$ret) {
     drush_set_error(dt("Cannot move build into place"));
@@ -532,9 +466,9 @@ function make_move_build($build_path) {
 /**
  * Create a request array for use with release_info_fetch().
  *
- * @param array $project
+ * @param $project
  *   Project array.
- * @param string $type
+ * @param $type
  *   'contrib' or 'core'.
  */
 function make_prepare_request($project, $type = 'contrib') {
@@ -549,23 +483,3 @@ function make_prepare_request($project, $type = 'contrib') {
   }
   return $request;
 }
-
-/**
- * Determine if the release information is required for this
- * project. When it is determined that it is, this potentially results
- * in the use of pm-download to process the project.
- *
- * If the location of the project is not customized (uses d.o), and
- * one of the following is true, then release information is required:
- *
- * - $project['type'] has not been specified
- * - $project['download'] has not been specified
- *
- * @see make_projects()
- */
-function make_project_needs_release_info($project) {
-  return isset($project['location'])
-    // Only fetch release info if the project type is unknown OR if
-    // download attributes are unspecified.
-    && (!isset($project['type']) || !isset($project['download']));
-}
index b7beeda..04d5338 100644 (file)
@@ -1,8 +1,4 @@
 <?php
-/**
- * @file
- * Drush Make processing classes.
- */
 
 /**
  * The base project class.
@@ -32,14 +28,6 @@ class DrushMakeProject {
   protected static $self = array();
 
   /**
-   * Default to overwrite to allow recursive builds to process properly.
-   *
-   * TODO refactor this to be more selective. Ideally a merge would take place
-   * instead of an overwrite.
-   */
-  protected $overwrite = TRUE;
-
-  /**
    * Set attributes and retrieve project information.
    */
   protected function __construct($project) {
@@ -52,12 +40,11 @@ class DrushMakeProject {
   /**
    * Get an instance for the type and project.
    *
-   * @param string $type
+   * @param $type
    *   Type of project: core, library, module, profile, or translation.
-   * @param array $project
+   * @param $project
    *   Project information.
-   *
-   * @return mixed
+   * @return
    *   An instance for the project or FALSE if invalid type.
    */
   public static function getInstance($type, $project) {
@@ -73,55 +60,6 @@ class DrushMakeProject {
    */
   function download() {
     $this->downloaded = TRUE;
-
-    // In some cases, make_download_factory() is going to need to know the
-    // full version string of the project we're trying to download. However,
-    // the version is a project-level attribute, not a download-level
-    // attribute. So, if we don't already have a full version string in the
-    // download array (e.g. if it was initialized via the release history XML
-    // for the PM case), we take the version info from the project-level
-    // attribute, convert it into a full version string, and stuff it into
-    // $this->download so that the download backend has access to it, too.
-    if (!empty($this->version) && empty($this->download['full_version'])) {
-      $full_version = '';
-      $matches = array();
-      // Core needs different conversion rules than contrib.
-      if (!empty($this->type) && $this->type == 'core') {
-        // Generally, the version for core is already set properly.
-        $full_version = $this->version;
-        // However, it might just be something like '7' or '7.x', in which
-        // case we need to turn that into '7.x-dev';
-        if (preg_match('/^\d+(\.x)?$/', $this->version, $matches)) {
-          // If there's no '.x' already, append it.
-          if (empty($matches[1])) {
-            $full_version .= '.x';
-          }
-          $full_version .= '-dev';
-        }
-      }
-      // Contrib.
-      else {
-        // If the version doesn't already define a core version, prepend it.
-        if (!preg_match('/^\d+\.x-\d+.*$/', $this->version)) {
-          // Just find the major version from $this->core so we don't end up
-          // with version strings like '7.12-2.0'.
-          $core_parts = explode('.', $this->core);
-          $full_version = $core_parts[0] . '.x-';
-        }
-        $full_version .= $this->version;
-        // If the project-level version attribute is just a number it's a major
-        // version.
-        if (preg_match('/^\d+(\.x)?$/', $this->version, $matches)) {
-          // If there's no '.x' already, append it.
-          if (empty($matches[1])) {
-            $full_version .= '.x';
-          }
-          $full_version .= '-dev';
-        }
-      }
-      $this->download['full_version'] = $full_version;
-    }
-
     if (make_download_factory($this->name, $this->download, $this->download_location) === FALSE) {
       $this->downloaded = FALSE;
     }
@@ -159,9 +97,6 @@ class DrushMakeProject {
     return TRUE;
   }
 
-  /**
-   * Determine the location to download project to.
-   */
   function findDownloadLocation() {
     $this->path = $this->generatePath();
     $this->project_directory = !empty($this->directory_name) ? $this->directory_name : $this->name;
@@ -169,7 +104,7 @@ class DrushMakeProject {
     // This directory shouldn't exist yet -- if it does, stop,
     // unless overwrite has been set to TRUE.
     if (is_dir($this->download_location) && !$this->overwrite) {
-      drush_set_error(dt('Directory not empty: !directory', array('!directory' => $this->download_location)));
+      drush_set_error(dt('Directory not empty: %directory', array('%directory' => $this->download_location)));
       return FALSE;
     }
     elseif ($this->download['type'] === 'pm') {
@@ -197,7 +132,7 @@ class DrushMakeProject {
         $info = array('url' => $info);
       }
       // Download the patch.
-      if ($filename = _make_download_file($info['url'])) {
+      if ($filename = _make_download_file($info)) {
         $patched = FALSE;
         $output = '';
         // Test each patch style; -p1 is the default with git. See
@@ -213,15 +148,9 @@ class DrushMakeProject {
         }
 
         // In some rare cases, git will fail to apply a patch, fallback to using
-        // the 'patch' command.
+        // the 'patch -p0' command.
         if (!$patched) {
-          foreach ($patch_levels as $patch_level) {
-            // --no-backup-if-mismatch here is a hack that fixes some
-            // differences between how patch works on windows and unix.
-            if ($patched = drush_shell_exec("patch %s --no-backup-if-mismatch -d %s < %s", $patch_level, $project_directory, $filename)) {
-              break;
-            }
-          }
+          $patched = drush_shell_exec("patch -p0 -d %s < %s", $project_directory, $filename);
         }
 
         if ($output = drush_shell_exec_output()) {
@@ -278,7 +207,7 @@ class DrushMakeProject {
   function getTranslations($project_directory) {
     static $cache = array();
     $langcodes = $this->translations;
-    if ($langcodes && in_array($this->type, array('core', 'module', 'profile', 'theme'), TRUE)) {
+    if ($langcodes && $this->version !== drush_get_option('drush-make-version-best') && in_array($this->type, array('core', 'module', 'profile', 'theme'), TRUE)) {
       // Support the l10n_path, l10n_url keys from l10n_update. Note that the
       // l10n_server key is not supported.
       if (isset($this->l10n_path)) {
@@ -296,13 +225,14 @@ class DrushMakeProject {
             if ($filename = _make_download_file($l10n_server)) {
               $server_info = simplexml_load_string(file_get_contents($filename));
               $cache[$l10n_server] = !empty($server_info->update_url) ? $server_info->update_url : FALSE;
+              drush_op('unlink', $filename);
             }
           }
           if ($cache[$l10n_server]) {
             $update_url = $cache[$l10n_server];
           }
           else {
-            make_error('XML_ERROR', dt("Could not retrieve l10n update url for !project.", array('!project' => $project['name'])));
+            make_error('XML_ERROR', dt("Could not retrieve l10n update url for %project.", array('%project' => $project['name'])));
             return FALSE;
           }
         }
@@ -312,34 +242,30 @@ class DrushMakeProject {
         foreach ($langcodes as $langcode) {
           $variables = array(
             '%project' => $this->name,
-            '%release' => $this->download['full_version'],
+            '%release' => $this->download['version'],
             '%core' => $this->core,
             '%language' => $langcode,
             '%filename' => '%filename',
           );
           $url = strtr($update_url, $variables);
 
-          // Download the translation file.  Since its contents are volatile,
-          // cache for only 4 hours.
-          if ($filename = _make_download_file($url, 3600 * 4)) {
+          // Download the translation file.
+          if ($filename = _make_download_file($url)) {
             // If this is the core project type, download the translation file
-            // and place it in every profile and an additional copy in
-            // modules/system/translations where it can be detected for import
-            // by other non-default install profiles.
+            // and create two copies:
+            // 1. To profiles/default/translations. It must be named
+            //    langcode.po to be used properly by the installer.
+            // 2. To modules/system/translations where it can be detected for
+            //    import by other non-default install profiles.
             if ($this->type === 'core') {
-              $profiles = drush_scan_directory($project_directory . '/profiles', '/.*/', array(), 0, FALSE, 'filename', 0, TRUE);
-              foreach ($profiles as $profile) {
-                if (is_dir($project_directory . '/profiles/' . $profile->basename)) {
-                  drush_mkdir($project_directory . '/profiles/' . $profile->basename . '/translations');
-                  drush_copy_dir($filename, $project_directory . '/profiles/' . $profile->basename . '/translations/' . $langcode . '.po');
-                }
-              }
+              drush_mkdir($project_directory . '/profiles/default/translations');
               drush_mkdir($project_directory . '/modules/system/translations');
-              drush_copy_dir($filename, $project_directory . '/modules/system/translations/' . $langcode . '.po');
+              drush_shell_exec("cp %s %s", $filename, $project_directory . '/profiles/default/translations/' . $langcode . '.po');
+              drush_shell_exec("mv %s %s", $filename, $project_directory . '/modules/system/translations/' . $langcode . '.po');
             }
             else {
               drush_mkdir($project_directory . '/translations');
-              drush_copy_dir($filename, $project_directory . '/translations/' . $langcode . '.po', TRUE);
+              drush_shell_exec("mv %s %s", $filename, $project_directory . '/translations/' . $langcode . '.po');
             }
           }
           else {
@@ -350,7 +276,7 @@ class DrushMakeProject {
           drush_log('All translations downloaded for ' . $this->name, 'ok');
         }
         else {
-          drush_log('Unable to download translations for ' . $this->name . ': ' . implode(', ', $failed), 'warning');
+          drush_log('Unable to download translations for '. $this->name .': ' . implode(', ', $failed), 'warning');
         }
       }
     }
@@ -360,7 +286,7 @@ class DrushMakeProject {
   /**
    * Generate the proper path for this project type.
    *
-   * @param boolean $base
+   * @param $base
    *   Whether include the base part (tmp dir). Defaults to TRUE.
    */
   protected function generatePath($base = TRUE) {
@@ -381,17 +307,13 @@ class DrushMakeProject {
   /**
    * Return the proper path for dependencies to be placed in.
    *
-   * @return string
+   * @return
    *   The path that dependencies will be placed in.
    */
   protected function buildPath($directory) {
     return $this->base_contrib_destination;
   }
 
-  /**
-   * Recurse to process additional makefiles that may be found during
-   * processing.
-   */
   function recurse($path) {
     $makefile = $this->download_location . '/' . $this->name . '.make';
     if (!file_exists($makefile)) {
@@ -400,7 +322,7 @@ class DrushMakeProject {
         return TRUE;
       }
     }
-    drush_log(dt("Found makefile: !makefile", array("!makefile" => basename($makefile))), 'ok');
+    drush_log(dt("Found makefile: %makefile", array("%makefile" => basename($makefile))), 'ok');
 
     $info = make_parse_info_file($makefile);
     if (!($info = make_validate_info_file($info))) {
@@ -414,31 +336,21 @@ class DrushMakeProject {
   }
 }
 
-/**
- * For processing Drupal core projects.
- */
 class DrushMakeProject_Core extends DrushMakeProject {
-  /**
-   * Override constructor for core to properly set contrib destination.
-   */
   protected function __construct(&$project) {
     parent::__construct($project);
     $this->contrib_destination = '';
   }
 
-  /**
-   * Determine the location to download project to.
-   */
   function findDownloadLocation() {
     $this->path = $this->download_location = $this->generatePath();
     $this->project_directory = '';
     if (is_dir($this->download_location)) {
-      drush_set_error(dt('Directory not empty: !directory', array('!directory' => $this->download_location)));
+      drush_set_error(dt('Directory not empty: %directory', array('%directory' => $this->download_location)));
       return FALSE;
     }
     elseif ($this->download['type'] === 'pm') {
-      // pm-download will create the final __build__ directory, so nothing to do
-      // here.
+      // pm-download will create the final __build__ directory, so nothing to do here.
     }
     else {
       drush_mkdir($this->download_location);
@@ -447,13 +359,7 @@ class DrushMakeProject_Core extends DrushMakeProject {
   }
 }
 
-/**
- * For processing libraries.
- */
 class DrushMakeProject_Library extends DrushMakeProject {
-  /**
-   * Override constructor for libraries to properly set contrib destination.
-   */
   protected function __construct(&$project) {
     parent::__construct($project);
     // Allow libraries to specify where they should live in the build path.
@@ -466,81 +372,46 @@ class DrushMakeProject_Library extends DrushMakeProject {
 
     $this->contrib_destination = ($this->base_contrib_destination != '.' ? $this->base_contrib_destination . '/' : '') . $project_path;
   }
-
-  /**
-   * No recursion for libraries, sorry :-(
-   */
+  // No recursion for libraries, sorry :-(
   function recurse($path) {}
-
-  /**
-   * No translations for libraries.
-   */
+  // Neither translations.
   function getTranslations($download_location) {}
 }
 
-/**
- * For processing modules.
- */
 class DrushMakeProject_Module extends DrushMakeProject {
-  /**
-   * Override constructor for modules to properly set contrib destination.
-   */
   protected function __construct(&$project) {
     parent::__construct($project);
     $this->contrib_destination = ($this->base_contrib_destination != '.' ? $this->base_contrib_destination . '/' : '') . 'modules';
   }
 }
 
-/**
- * For processing installation profiles.
- */
 class DrushMakeProject_Profile extends DrushMakeProject {
-  /**
-   * Override contructor for installation profiles to properly set contrib
-   * destination.
-   */
   protected function __construct(&$project) {
     parent::__construct($project);
     $this->contrib_destination = (!empty($this->destination) ? $this->destination : 'profiles');
   }
 
-  /**
-   * Find the build path.
-   */
   protected function buildPath($directory) {
     return $this->generatePath(FALSE) . '/' . $directory;
   }
 }
 
-/**
- * For processing themes.
- */
 class DrushMakeProject_Theme extends DrushMakeProject {
-  /**
-   * Override contructor for themes to properly set contrib destination.
-   */
   protected function __construct(&$project) {
     parent::__construct($project);
     $this->contrib_destination = ($this->base_contrib_destination != '.' ? $this->base_contrib_destination . '/' : '') . 'themes';
   }
 }
 
-/**
- * For processing translations.
- */
 class DrushMakeProject_Translation extends DrushMakeProject {
-  /**
-   * Override constructor for translations to properly set contrib destination.
-   */
   protected function __construct(&$project) {
     parent::__construct($project);
-    switch ($project['core']) {
+    switch($project['core']) {
       case '5.x':
         // Don't think there's an automatic place we can put 5.x translations,
         // so we'll toss them in a translations directory in the Drupal root.
         $this->contrib_destination = ($this->base_contrib_destination != '.' ? $this->base_contrib_destination . '/' : '') . 'translations';
         break;
-
       default:
         $this->contrib_destination = '';
         break;
index e66515e..0d7da57 100644 (file)
@@ -1,8 +1,4 @@
 <?php
-/**
- * @file
- * General utility functions for Drush Make.
- */
 
 /**
  * Parse Drupal info file format.
@@ -13,9 +9,9 @@
  */
 function make_parse_info_file($makefile, $parsed = TRUE) {
   if (!($data = make_get_data($makefile))) {
-    return drush_set_error(dt('Invalid or empty make file: !makefile', array('!makefile' => $makefile)));
+    return drush_set_error(dt('Invalid or empty make file: %makefile', array('%makefile' => $makefile)));
   }
-  if (!($info = _drush_drupal_parse_info_file($data))) {
+  if (!($info = _make_parse_info_file($data))) {
     return FALSE;
   }
   if (!empty($info['includes'])) {
@@ -27,21 +23,21 @@ function make_parse_info_file($makefile, $parsed = TRUE) {
           if (make_valid_url($include, TRUE) && ($file = make_parse_info_file($include, FALSE))) {
             $includes[] = $file;
           }
-          elseif (file_exists($include_path . '/' . $include) && ($file = make_parse_info_file($include_path . '/' . $include, FALSE))) {
+          else if (file_exists($include_path .'/'. $include) && ($file = make_parse_info_file($include_path .'/'. $include, FALSE))) {
             $includes[] = $file;
           }
-          elseif (file_exists($include) && ($file = make_parse_info_file($include, FALSE))) {
+          else if (file_exists($include) && ($file = make_parse_info_file($include, FALSE))) {
             $includes[] = $file;
           }
           else {
-            make_error('BUILD_ERROR', dt("Include file missing: !include", array('!include' => $include)));
+            make_error('BUILD_ERROR', dt("Include file missing: %include", array('%include' => $include)));
           }
         }
       }
     }
     $includes[] = $data;
     $data = implode("\n", $includes);
-    $info = _drush_drupal_parse_info_file($data);
+    $info = _make_parse_info_file($data);
   }
   if ($parsed) {
     return $info;
@@ -51,79 +47,65 @@ function make_parse_info_file($makefile, $parsed = TRUE) {
   }
 }
 
-/**
- * Remove entries in the info file in accordance with the options passed in.
- * Entries are either explicitly 'allowed' (with the $include_only parameter) in
- * which case all *other* entries will be excluded.
- *
- * @param array $info
- *   A parsed info file.
- *
- * @param array $include_only
- *   (Optional) Array keyed by entry type (e.g. 'libraries') against an array of
- *   allowed keys for that type. The special value '*' means 'all entries of
- *   this type'. If this parameter is omitted, no entries will be excluded.
- *
- * @return array
- *   The $info array, pruned if necessary.
- */
-function make_prune_info_file($info, $include_only = array()) {
-
-  // We may get passed FALSE in some cases.
-  // Also we cannot prune an empty array, so no point in this code running!
-  if (empty($info)) {
-    return $info;
+function _make_parse_info_file($data) {
+  if (!$data) {
+    return FALSE;
   }
 
-  // We will accrue an explanation of our activities here.
-  $msg = array();
-  $msg['scope'] = dt("Drush make restricted to the following entries:");
-
-  $pruned = FALSE;
-
-  if (count(array_filter($include_only))) {
-    $pruned = TRUE;
-    foreach ($include_only as $type => $keys) {
-
-      // For translating
-      // dt("Projects");
-      // dt("Libraries");
-      $type_title = dt(ucfirst($type));
-
-      // Handle the special '*' value.
-      if (in_array('*', $keys)) {
-        $msg[$type] = dt("!entry_type: <All>", array('!entry_type' => $type_title));
+  if (preg_match_all('
+    @^\s*                           # Start at the beginning of a line, ignoring leading whitespace
+    ((?:
+      [^=;\[\]]|                    # Key names cannot contain equal signs, semi-colons or square brackets,
+      \[[^\[\]]*\]                  # unless they are balanced and not nested
+    )+?)
+    \s*=\s*                         # Key/value pairs are separated by equal signs (ignoring white-space)
+    (?:
+      ("(?:[^"]|(?<=\\\\)")*")|     # Double-quoted string, which may contain slash-escaped quotes/slashes
+      (\'(?:[^\']|(?<=\\\\)\')*\')| # Single-quoted string, which may contain slash-escaped quotes/slashes
+      ([^\r\n]*?)                   # Non-quoted string
+    )\s*$                           # Stop at the next end of a line, ignoring trailing whitespace
+    @msx', $data, $matches, PREG_SET_ORDER)) {
+    $info = array();
+    foreach ($matches as $match) {
+      // Fetch the key and value string
+      $i = 0;
+      foreach (array('key', 'value1', 'value2', 'value3') as $var) {
+        $$var = isset($match[++$i]) ? $match[$i] : '';
       }
+      $value = stripslashes(substr($value1, 1, -1)) . stripslashes(substr($value2, 1, -1)) . $value3;
 
-      // Handle a (possibly empty) array of keys to include/exclude.
-      else {
-        $info[$type] = array_intersect_key($info[$type], array_fill_keys($keys, 1));
-        unset($msg[$type]);
-        if (!empty($info[$type])) {
-          $msg[$type] = dt("!entry_type: !make_entries", array('!entry_type' => $type_title, '!make_entries' => implode(', ', array_keys($info[$type]))));
+      // Parse array syntax
+      $keys = preg_split('/\]?\[/', rtrim($key, ']'));
+      $last = array_pop($keys);
+      $parent = &$info;
+
+      // Create nested arrays
+      foreach ($keys as $key) {
+        if ($key == '') {
+          $key = count($parent);
         }
+        if (!isset($parent[$key]) || !is_array($parent[$key])) {
+          $parent[$key] = array();
+        }
+        $parent = &$parent[$key];
       }
-    }
-  }
 
-  if ($pruned) {
-    // Make it clear to the user what's going on.
-    drush_log(implode("\n", $msg), 'ok');
+      // Handle PHP constants
+      if (defined($value)) {
+        $value = constant($value);
+      }
 
-    // Throw an error if these restrictions reduced the make to nothing.
-    if (empty($info['projects']) && empty($info['libraries'])) {
-      // This error mentions the options explicitly to make it as clear as
-      // possible to the user why this error has occurred.
-      make_error('BUILD_ERROR', dt("All projects and libraries have been excluded. Review the 'projects' and 'libraries' options."));
+      // Insert actual value
+      if ($last == '') {
+        $last = count($parent);
+      }
+      $parent[$last] = $value;
     }
+    return $info;
   }
-
-  return $info;
+  return FALSE;
 }
 
-/**
- * Validate the make file.
- */
 function make_validate_info_file($info) {
   // Assume no errors to start.
   $errors = FALSE;
@@ -143,7 +125,7 @@ function make_validate_info_file($info) {
     $info['core'] = $matches[1] . '.x';
   }
   else {
-    make_error('BUILD_ERROR', dt("The 'core' attribute !core has an incorrect format.", array('!core' => $info['core'])));
+    make_error('BUILD_ERROR', dt("The 'core' attribute %core has an incorrect format.", array('%core' => $info['core'])));
     $errors = TRUE;
   }
 
@@ -165,14 +147,14 @@ function make_validate_info_file($info) {
       $errors = TRUE;
     }
     else {
-      // Filter out entries that have been forcibly removed via [foo] = FALSE.
+      // Filter out entries that have been forcibly removed via [foo] = FALSE
       $info['projects'] = array_filter($info['projects']);
 
       foreach ($info['projects'] as $project => $project_data) {
         // Project has an attributes array.
         if (is_string($project) && is_array($project_data)) {
           if (in_array($project, $names)) {
-            make_error('BUILD_ERROR', dt("Project !project defined twice (remove the first projects[] = !project).", array('!project' => $project)));
+            make_error('BUILD_ERROR', dt("Project %project defined twice (remove the first projects[] = %project).", array('%project' => $project)));
             $errors = TRUE;
           }
           $names[] = $project;
@@ -181,15 +163,9 @@ function make_validate_info_file($info) {
             if (in_array($attribute, array('contrib_destination'))) {
               unset($info['projects'][$project][$attribute]);
             }
-            // Prevent malicious attempts to access other areas of the
-            // filesystem.
+            // Prevent malicious attempts to access other areas of the filesystem.
             elseif (in_array($attribute, array('subdir', 'directory_name')) && !make_safe_path($value)) {
-              $args = array(
-                '!path' => $value,
-                '!attribute' => $attribute,
-                '!project' => $project,
-              );
-              make_error('BUILD_ERROR', dt("Illegal path !path for '!attribute' attribute in project !project.", $args));
+              make_error('BUILD_ERROR', dt("Illegal path %path for '%attribute' attribute in project %project.", array('%path' => $value, '%attribute' => $attribute, '%project' => $project)));
               $errors = TRUE;
             }
           }
@@ -197,7 +173,7 @@ function make_validate_info_file($info) {
         // Cover if there is no project info, it's just a project name.
         elseif (is_numeric($project) && is_string($project_data)) {
           if (in_array($project_data, $names)) {
-            make_error('BUILD_ERROR', dt("Project !project defined twice (remove the first projects[] = !project).", array('!project' => $project_data)));
+            make_error('BUILD_ERROR', dt("Project %project defined twice (remove the first projects[] = %project).", array('%project' => $project_data)));
             $errors = TRUE;
           }
           $names[] = $project_data;
@@ -207,14 +183,14 @@ function make_validate_info_file($info) {
         // Convert shorthand project version style to array format.
         elseif (is_string($project_data)) {
           if (in_array($project, $names)) {
-            make_error('BUILD_ERROR', dt("Project !project defined twice (remove the first projects[] = !project).", array('!project' => $project)));
+            make_error('BUILD_ERROR', dt("Project %project defined twice (remove the first projects[] = %project).", array('%project' => $project)));
             $errors = TRUE;
           }
           $names[] = $project;
           $info['projects'][$project] = array('version' => $project_data);
         }
         else {
-          make_error('BUILD_ERROR', dt('Project !project incorrectly specified.', array('!project' => $project)));
+          make_error('BUILD_ERROR', dt('Project %project incorrectly specified.', array('%project' => $project)));
           $errors = TRUE;
         }
       }
@@ -226,25 +202,19 @@ function make_validate_info_file($info) {
       $errors = TRUE;
     }
     else {
-      // Filter out entries that have been forcibly removed via [foo] = FALSE.
+      // Filter out entries that have been forcibly removed via [foo] = FALSE
       $info['libraries'] = array_filter($info['libraries']);
 
-      foreach ($info['libraries'] as $library => $library_data) {
+      foreach($info['libraries'] as $library => $library_data) {
         if (is_array($library_data)) {
-          foreach ($library_data as $attribute => $value) {
+          foreach($library_data as $attribute => $value) {
             // Unset disallowed attributes.
             if (in_array($attribute, array('contrib_destination'))) {
               unset($info['libraries'][$library][$attribute]);
             }
-            // Prevent malicious attempts to access other areas of the
-            // filesystem.
+            // Prevent malicious attempts to access other areas of the filesystem.
             elseif (in_array($attribute, array('contrib-destination', 'directory_name')) && !make_safe_path($value)) {
-              $args = array(
-                '!path' => $value,
-                '!attribute' => $attribute,
-                '!library' => $library,
-              );
-              make_error('BUILD_ERROR', dt("Illegal path !path for '!attribute' attribute in library !library.", $args));
+              make_error('BUILD_ERROR', dt("Illegal path %path for '%attribute' attribute in library %library.", array('%path' => $value, '%attribute' => $attribute, '%library' => $library)));
               $errors = TRUE;
             }
           }
@@ -254,7 +224,7 @@ function make_validate_info_file($info) {
   }
 
   foreach (drush_command_implements('make_validate_info') as $module) {
-    $function = $module . '_make_validate_info';
+    $function = $module .'_make_validate_info';
     $return = $function($info);
     if ($return) {
       $info = $return;
@@ -279,7 +249,7 @@ function make_validate_info_file($info) {
  */
 function make_valid_url($url, $absolute = FALSE) {
   if ($absolute) {
-    return (bool) preg_match("
+    return (bool)preg_match("
       /^                                                      # Start at the beginning of the text
       (?:ftp|https?):\/\/                                     # Look for ftp, http, or https schemes
       (?:                                                     # Userinfo (optional) which is typically
@@ -297,19 +267,18 @@ function make_valid_url($url, $absolute = FALSE) {
     $/xi", $url);
   }
   else {
-    return (bool) preg_match("/^(?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})+$/i", $url);
+    return (bool)preg_match("/^(?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})+$/i", $url);
   }
 }
 
 /**
  * Find, and possibly create, a temporary directory.
  *
- * @param boolean $set
+ * @param $set
  *   Must be TRUE to create a directory.
- * @param string $directory
+ * @param $directory
  *   Pass in a directory to use. This is required if using any
  *   concurrent operations.
- *
  * @todo Merge with drush_tempdir().
  */
 function make_tmp($set = TRUE, $directory = NULL) {
@@ -327,53 +296,51 @@ function make_tmp($set = TRUE, $directory = NULL) {
     else {
       $tmp_dir .= '/make_tmp_' . time() . '_' . uniqid();
     }
-    if (!drush_get_option('no-clean', FALSE)) {
-      drush_register_file_for_deletion($tmp_dir);
-    }
     if (file_exists($tmp_dir)) {
       return make_tmp(TRUE);
     }
-    // Create the directory.
+    // else create it
     drush_mkdir($tmp_dir);
   }
   return $tmp_dir;
 }
 
-/**
- * Removes the temporary build directory. On failed builds, this is handled by
- * drush_register_file_for_deletion().
- */
 function make_clean_tmp() {
   if (!($tmp_dir = make_tmp(FALSE))) {
     return;
   }
   if (!drush_get_option('no-clean', FALSE)) {
-    drush_delete_dir($tmp_dir);
+    drush_shell_exec('rm -rf %s', $tmp_dir);
   }
   else {
-    drush_log(dt('Temporary directory: !dir', array('!dir' => $tmp_dir)), 'ok');
+    drush_log(dt('Temporary directory: %dir', array('%dir' => $tmp_dir)), 'ok');
   }
 }
 
-/**
- * Prepare a Drupal installation, copying default.settings.php to settings.php.
- */
 function make_prepare_install($build_path) {
   $default = make_tmp() . '/__build__/sites/default';
-  drush_copy_dir($default . DIRECTORY_SEPARATOR . 'default.settings.php', $default . DIRECTORY_SEPARATOR . 'settings.php', TRUE);
+  drush_shell_exec("cp %s %s", $default . '/default.settings.php', $default . '/settings.php');
   drush_mkdir($default . '/files');
-  chmod($default . DIRECTORY_SEPARATOR . 'settings.php', 0666);
-  chmod($default . DIRECTORY_SEPARATOR . 'files', 0777);
+  drush_shell_exec("chmod a+w %s %s", $default . '/settings.php', $default . '/files');
 }
 
-/**
- * Calculate a cksum on each file in the build, and md5 the resulting hashes.
- */
 function make_md5() {
-  return drush_dir_md5(make_tmp());
+  if (drush_shell_exec("( find %s -type f -exec cksum {} \; )", make_tmp())) {
+    $hashes = array();
+    foreach (drush_shell_exec_output() as $line) {
+      // Remove the temporary build path which includes a (relatively)
+      // unique timestamp.
+      $line = str_replace(make_tmp(), '', $line);
+      // Trim to sanitize.
+      $line = trim($line);
+      $hashes[] = $line;
+    }
+    sort($hashes);
+    return md5(implode("\n", $hashes));
+  }
 }
 
-/**
+/*
  * @todo drush_archive_dump() also makes a tar. Consolidate?
  */
 function make_tar($build_path) {
@@ -384,14 +351,14 @@ function make_tar($build_path) {
   $dirname = basename($build_path, '.tar.gz');
   // Move the build directory to a more human-friendly name, so that tar will
   // use it instead.
-  drush_move_dir($tmp_path . DIRECTORY_SEPARATOR . '__build__', $tmp_path . DIRECTORY_SEPARATOR . $dirname, TRUE);
+  drush_shell_exec("mv %s %s", $tmp_path . '/__build__', $tmp_path . '/' . $dirname);
   // Only move the tar file to it's final location if it's been built
   // successfully.
-  if (drush_shell_exec("%s -C %s -Pczf %s %s", drush_get_tar_executable(), $tmp_path, $tmp_path . '/' . $filename, $dirname)) {
-    drush_move_dir($tmp_path . DIRECTORY_SEPARATOR . $filename, $build_path, TRUE);
+  if (drush_shell_exec("tar -C %s -Pczf %s %s", $tmp_path, $tmp_path . '/' . $filename, $dirname)) {
+    drush_shell_exec("mv %s %s", $tmp_path . '/' . $filename, $build_path);
   };
   // Move the build directory back to it's original location for consistency.
-  drush_move_dir($tmp_path . DIRECTORY_SEPARATOR . $dirname, $tmp_path . DIRECTORY_SEPARATOR . '__build__');
+  drush_shell_exec("mv %s %s", $tmp_path . '/' . $dirname, $tmp_path . '/__build__');
 }
 
 /**
@@ -409,7 +376,7 @@ function make_error($error_code, $message) {
 /**
  * Checks an attribute's path to ensure it's not maliciously crafted.
  *
- * @param string $path
+ * @param $path
  *   The path to check.
  */
 function make_safe_path($path) {
@@ -421,10 +388,9 @@ function make_safe_path($path) {
  * This is a helper function to abstract the retrieval of data, so that it can
  * come from files, STDIN, etc.  Currently supports filepath and STDIN.
  *
- * @param string $data_source
+ * @param $data_source
  *   The path to a file, or '-' for STDIN.
- *
- * @return string
+ * @return
  *   The raw data as a string.
  */
 function make_get_data($data_source) {
@@ -450,7 +416,7 @@ function make_get_data($data_source) {
   }
   // Remote file.
   else {
-    $file = _make_download_file($data_source);
+    $file = _make_download_file(array('url' => $data_source));
     $data = file_get_contents($file);
     drush_op('unlink', $file);
   }
@@ -458,28 +424,26 @@ function make_get_data($data_source) {
 }
 
 /**
- * Helper to provide sys_get_temp_dir if on php < 5.2.1.
+ * Helper to provide sys_get_temp_dir if on php < 5.2.1
  */
 if (!function_exists('sys_get_temp_dir')) {
-  /**
-   * Based on
-   * http://www.phpit.net/article/creating-zip-tar-archives-dynamically-php/2/
-   */
+  // Based on http://www.phpit.net/
+  // article/creating-zip-tar-archives-dynamically-php/2/
   function sys_get_temp_dir() {
-    // Try to get from environment variable.
-    if (!empty($_ENV['TMP'])) {
+    // Try to get from environment variable
+    if (!empty($_ENV['TMP'])){
       return realpath($_ENV['TMP']);
     }
-    elseif (!empty($_ENV['TMPDIR'])) {
+    else if (!empty($_ENV['TMPDIR'])){
       return realpath($_ENV['TMPDIR']);
     }
-    elseif (!empty($_ENV['TEMP'])) {
+    else if (!empty($_ENV['TEMP'])){
       return realpath($_ENV['TEMP']);
     }
     else {
-      // Detect by creating a temporary file.
-      // Try to use system's temporary directory as random name
-      // shouldn't exist.
+      // Detect by creating a temporary file
+      // Try to use system's temporary directory
+      // as random name shouldn't exist
       $temp_file = tempnam(md5(uniqid(rand(), TRUE)), '');
       if ($temp_file) {
         $temp_dir = realpath(dirname($temp_file));
@@ -492,3 +456,169 @@ if (!function_exists('sys_get_temp_dir')) {
     }
   }
 }
+
+/**
+ * @todo Consolidate with drush_download_file if possible.
+ * Drush make parallel to drupal_http_request, but writes responses to a file.
+ */
+function make_http_request($url, $destination, $headers = array(), $method = 'GET', $data = NULL, $retry = 5) {
+  global $db_prefix;
+
+  $result = new stdClass();
+
+  // Parse the URL and make sure we can handle the schema.
+  $uri = parse_url($url);
+
+  if ($uri == FALSE) {
+    $result->error = 'unable to parse URL';
+    $result->code = -1001;
+    return $result;
+  }
+
+  if (!isset($uri['scheme'])) {
+    $result->error = 'missing schema';
+    $result->code = -1002;
+    return $result;
+  }
+
+  switch ($uri['scheme']) {
+    case 'http':
+    case 'feed':
+      $port = isset($uri['port']) ? $uri['port'] : 80;
+      $host = $uri['host'] . ($port != 80 ? ':'. $port : '');
+      $fp = @fsockopen($uri['host'], $port, $errno, $errstr, 15);
+      break;
+    case 'https':
+      // Note: Only works for PHP 4.3 compiled with OpenSSL.
+      $port = isset($uri['port']) ? $uri['port'] : 443;
+      $host = $uri['host'] . ($port != 443 ? ':'. $port : '');
+      $fp = @fsockopen('ssl://'. $uri['host'], $port, $errno, $errstr, 20);
+      break;
+    default:
+      $result->error = 'invalid schema '. $uri['scheme'];
+      $result->code = -1003;
+      return $result;
+  }
+
+  // Make sure the socket opened properly.
+  if (!$fp) {
+    // When a network error occurs, we use a negative number so it does not
+    // clash with the HTTP status codes.
+    $result->code = -$errno;
+    $result->error = trim($errstr);
+
+    return $result;
+  }
+
+  // Construct the path to act on.
+  $path = isset($uri['path']) ? $uri['path'] : '/';
+  if (isset($uri['query'])) {
+    $path .= '?'. $uri['query'];
+  }
+
+  // Create HTTP request.
+  $defaults = array(
+    // RFC 2616: "non-standard ports MUST, default ports MAY be included".
+    // We don't add the port to prevent from breaking rewrite rules checking the
+    // host that do not take into account the port number.
+    'Host' => "Host: $host",
+    'User-Agent' => 'User-Agent: Drupal (+http://drupal.org/)',
+  );
+
+  // Only add Content-Length if we actually have any content or if it is a POST
+  // or PUT request. Some non-standard servers get confused by Content-Length in
+  // at least HEAD/GET requests, and Squid always requires Content-Length in
+  // POST/PUT requests.
+  $content_length = strlen($data);
+  if ($content_length > 0 || $method == 'POST' || $method == 'PUT') {
+    $defaults['Content-Length'] = 'Content-Length: '. $content_length;
+  }
+
+  // If the server url has a user then attempt to use basic authentication
+  if (isset($uri['user'])) {
+    $defaults['Authorization'] = 'Authorization: Basic '. base64_encode($uri['user'] . (!empty($uri['pass']) ? ":". $uri['pass'] : ''));
+  }
+
+  // If the database prefix is being used by SimpleTest to run the tests in a copied
+  // database then set the user-agent header to the database prefix so that any
+  // calls to other Drupal pages will run the SimpleTest prefixed database. The
+  // user-agent is used to ensure that multiple testing sessions running at the
+  // same time won't interfere with each other as they would if the database
+  // prefix were stored statically in a file or database variable.
+  if (is_string($db_prefix) && preg_match("/^simpletest\d+$/", $db_prefix, $matches)) {
+    $defaults['User-Agent'] = 'User-Agent: ' . $matches[0];
+  }
+
+  foreach ($headers as $header => $value) {
+    $defaults[$header] = $header .': '. $value;
+  }
+
+  $request = $method .' '. $path ." HTTP/1.0\r\n";
+  $request .= implode("\r\n", $defaults);
+  $request .= "\r\n\r\n";
+  $request .= $data;
+
+  $result->request = $request;
+
+  fwrite($fp, $request);
+
+  $fp_dest = fopen($destination, 'w');
+  $all_headers = FALSE;
+
+  // Fetch response.
+  $split = '';
+  while (!feof($fp) && $chunk = fread($fp, 1024)) {
+    if (strpos($chunk, "\r\n\r\n") !== FALSE && !$all_headers) {
+      $all_headers = TRUE;
+      list($header, $body) = explode("\r\n\r\n", $chunk, 2);
+      $split .= $header;
+      fwrite($fp_dest, $body);
+    }
+    elseif (!$all_headers) {
+      $split .= $chunk;
+    }
+    else {
+      fwrite($fp_dest, $chunk);
+    }
+  }
+  fclose($fp_dest);
+  fclose($fp);
+
+  // Parse response.
+  $split = preg_split("/\r\n|\n|\r/", $split);
+
+  list($protocol, $code, $status_message) = explode(' ', trim(array_shift($split)), 3);
+  $result->protocol = $protocol;
+  $result->status_message = $status_message;
+
+  $result->headers = array();
+
+  // Parse headers.
+  while ($line = trim(array_shift($split))) {
+    list($header, $value) = explode(':', $line, 2);
+    if (isset($result->headers[$header]) && $header == 'Set-Cookie') {
+      // RFC 2109: the Set-Cookie response header comprises the token Set-
+      // Cookie:, followed by a comma-separated list of one or more cookies.
+      $result->headers[$header] .= ','. trim($value);
+    }
+    else {
+      $result->headers[$header] = trim($value);
+    }
+  }
+
+  $responses = array(
+    100 => 'Continue', 101 => 'Switching Protocols',
+    200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content',
+    300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect',
+    400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed',
+    500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported'
+  );
+  // RFC 2616 states that all unknown HTTP codes must be treated the same as the
+  // base code in their class.
+  if (!isset($responses[$code])) {
+    $code = floor($code / 100) * 100;
+  }
+
+  $result->code = $code;
+  return $result;
+}
index e6599f8..6a8f369 100644 (file)
@@ -74,7 +74,7 @@ function drush_pm_download_validate() {
  * Command callback. Download Drupal core or any project.
  */
 function drush_pm_download() {
-  if (!$requests = pm_parse_arguments(func_get_args(), FALSE)) {
+  if (!$requests = pm_parse_arguments(func_get_args(), FALSE, FALSE)) {
     $requests = array('drupal');
   }
 
@@ -83,12 +83,12 @@ function drush_pm_download() {
 
   // Get release history for each request and download the project.
   $source = drush_get_option('source', RELEASE_INFO_DEFAULT_URL);
-  $restrict_to = drush_get_option('dev', FALSE) ? 'dev' : '';
+  $dev = drush_get_option('dev', FALSE);
   $select = drush_get_option('select', 'auto');
   $all = drush_get_option('all', FALSE);
   foreach ($requests as $name => $request) {
     $request['status url'] = $source;
-    $release = release_info_fetch($request, $restrict_to, $select, $all);
+    $release = release_info_fetch($request, $dev, $select, $all);
     if ($release == FALSE) {
       continue;
     }
@@ -96,8 +96,7 @@ function drush_pm_download() {
     // Determine the name of the directory that will contain the project.
     // We face here all the asymetries to make it smooth for package handlers.
     // For Drupal core: --drupal-project-rename or drupal-x.y
-    if (($request['project_type'] == 'core') ||
-        (($request['project_type'] == 'profile') && (drush_get_option('variant', 'full') == 'full'))) {
+    if ($request['project_type'] == 'core') {
       // Avoid downloading core into existing core.
       if (drush_get_context('DRUSH_BOOTSTRAP_PHASE') >= DRUSH_BOOTSTRAP_DRUPAL_ROOT) {
         if (strpos(realpath(drush_get_option('destination')), DRUPAL_ROOT) !== FALSE) {
@@ -107,7 +106,7 @@ function drush_pm_download() {
 
       if ($rename = drush_get_option('drupal-project-rename', FALSE)) {
         if ($rename === TRUE) {
-          $request['project_dir'] = $request['name'];
+          $request['project_dir'] = 'drupal';
         }
         else {
           $request['project_dir'] = $rename;
@@ -198,24 +197,24 @@ function drush_pm_download() {
           drush_delete_dir($empty_dir, TRUE);
         }
       }
-
-      // Post download actions.
-      package_handler_post_download($request, $release);
-      drush_command_invoke_all('drush_pm_post_download', $request, $release);
-      $version_control->post_download($request);
-
-      // Print release notes if --notes option is set.
-      if (drush_get_option('notes') && !drush_get_context('DRUSH_PIPE')) {
-        release_info_print_releasenotes(array($name . '-' . $release['version']), FALSE);
-      }
-
-      // Inform the user about available modules a/o themes in the downloaded project.
-      drush_pm_extensions_in_project($request);
     }
     else {
-      // We don't `return` here in order to proceed with downloading additional projects.
-      drush_set_error('DRUSH_PM_DOWNLOAD_FAILED', dt("Project !project (!version) could not be downloaded to !dest.", array('!project' => $request['name'], '!version' => $release['version'], '!dest' => $request['project_install_location'])));
+      drush_log(dt("Project !project (!version) could not be downloaded to !dest.", array('!project' => $request['name'], '!version' => $release['version'], '!dest' => $request['project_install_location'])), 'error');
+      continue;
     }
+
+    // Post download actions.
+    package_handler_post_download($request, $release);
+    drush_command_invoke_all('drush_pm_post_download', $request, $release);
+    $version_control->post_download($request);
+
+    // Print release notes if --notes option is set.
+    if (drush_get_option('notes') && !drush_get_context('DRUSH_PIPE')) {
+      release_info_print_releasenotes(array($name . '-' . $release['version']), FALSE);
+    }
+
+    // Inform the user about available modules a/o themes in the downloaded project.
+    drush_pm_extensions_in_project($request);
   }
 }
 
@@ -229,9 +228,8 @@ function drush_pm_download() {
  */
 function pm_drush_pm_download_destination_alter(&$project, $release) {
   // A module is a pure drush command if it has no .module and contain
-  // .drush.inc files.  Skip this test for drush itself, though; we do
-  // not want to download drush to the ~/.drush folder.
-  if (($project['project_type'] == 'module') && ($project['name'] != 'drush')) {
+  // .drush.inc files.
+  if ($project['project_type'] == 'module') {
     $drush_command_files = drush_scan_directory($project['full_project_path'], '/.*\.drush.inc/');
     if (!empty($drush_command_files)) {
       $module_files = drush_scan_directory($project['full_project_path'], '/.*\.module/');
index 5efbd71..3930289 100644 (file)
@@ -6,10 +6,15 @@
 function drush_pm_info() {
   $args = pm_parse_arguments(func_get_args());
 
-  $extension_info = drush_get_extensions(FALSE);
+  $extension_info = drush_get_extensions();
   _drush_pm_expand_extensions($args, $extension_info);
-  // If no extensions are provided, show all.
+  // If no extensions are provided, select all but the hidden ones.
   if (count($args) == 0) {
+    foreach ($extension_info as $key => $extension) {
+      if (isset($extension->info['hidden'])) {
+        unset($extension_info[$key]);
+      }
+    }
     $args = array_keys($extension_info);
   }
 
index eef7a72..acb4b68 100644 (file)
@@ -26,7 +26,7 @@ function package_handler_validate() {
   }
   // Check git_deploy is enabled. Only for bootstrapped sites.
   if (drush_get_context('DRUSH_BOOTSTRAP_PHASE') >= DRUSH_BOOTSTRAP_DRUPAL_FULL) {
-    if (!drush_get_option('gitinfofile') && !module_exists('git_deploy')) {
+    if (!module_exists('git_deploy')) {
       drush_log(dt('git package handler needs git_deploy module enabled to work properly.'), 'warning');
     }
   }
@@ -199,64 +199,48 @@ function package_handler_post_download($project, $release) {
   }
 
   if (drush_get_option('gitinfofile', FALSE)) {
-    $matches = array();
-    if (preg_match('/^(.+).x-dev$/', $release['version'], $matches)) {
-      $full_version = drush_pm_git_drupalorg_compute_rebuild_version($project['project_install_location'], $matches[1]);
-    }
-    else {
-      $full_version = $release['version'];
-    }
-    drush_pm_inject_info_file_metadata($project['project_install_location'], $project['name'], $full_version);
+    package_handler_inject_info($project, $release);
   }
 
 }
 
+function package_handler_inject_info($project, $release) {
+  // Handler for the directory and open directory and walk through the filenames
+  $info_files = drush_scan_directory($project['project_install_location'], "/.*\.info$/");
+  $return = TRUE;
+  // Consider that the d.o. package manager is aware of submodules in parent modules
+  foreach($info_files as $info_file) {
+    $return = $return && package_handler_fix_info_file_version($info_file->filename, $project['name'], $release['version']);
+  }
+  return $return;
+}
+
 /**
- * Helper function to compute the rebulid version string for a project.
- *
- * This does some magic in Git to find the latest release tag along
- * the branch we're packaging from, count the number of commits since
- * then, and use that to construct this fancy alternate version string
- * which is useful for the version-specific dependency support in Drupal
- * 7 and higher.
- *
- * NOTE: A similar function lives in git_deploy and in the drupal.org
- * packaging script (see DrupalorgProjectPackageRelease.class.php inside
- * drupalorg/drupalorg_project/plugins/release_packager). Any changes to the
- * actual logic in here should probably be reflected in the other places.
- *
- * @param string $project_dir
- *   The full path to the root directory of the project to operate on.
- * @param string $branch
- *   The branch that we're using for -dev. This should only include the
- *   core version, the dash, and the branch's major version (eg. '7.x-2').
- *
- * @return string
- *   The full 'rebuild version string' in the given Git checkout.
+ * Fix the given .info file with the specified version string
+ * Taken straight from: http://drupalcode.org/project/drupalorg.git/blob/refs/heads/6.x-3.x:/drupalorg_project/plugins/release_packager/DrupalorgProjectPackageRelease.class.php#l192
  */
-function drush_pm_git_drupalorg_compute_rebuild_version($project_dir, $branch) {
-  $rebuild_version = '';
-  $branch_preg = preg_quote($branch);
-
-  if (drush_shell_cd_and_exec($project_dir, 'git describe --tags')) {
-    $shell_output = drush_shell_exec_output();
-    $last_tag = $shell_output[0];
-    // Make sure the tag starts as Drupal formatted (for eg.
-    // 7.x-1.0-alpha1) and if we are on a proper branch (ie. not master)
-    // then it's on that branch.
-    if (preg_match('/^(?<drupalversion>' . $branch_preg . '\.\d+(?:-[^-]+)?)(?<gitextra>-(?<numberofcommits>\d+-)g[0-9a-f]{7})?$/', $last_tag, $matches)) {
-      // If we found additional git metadata (in particular, number of commits)
-      // then use that info to build the version string.
-      if (isset($matches['gitextra'])) {
-        $rebuild_version = $matches['drupalversion'] . '+' . $matches['numberofcommits'] . 'dev';
-      }
-      // Otherwise, the branch tip is pointing to the same commit as the
-      // last tag on the branch, in which case we use the prior tag and
-      // add '+0-dev' to indicate we're still on a -dev branch.
-      else {
-        $rebuild_version = $last_tag . '+0-dev';
-      }
-    }
-  }
-  return $rebuild_version;
+function package_handler_fix_info_file_version($file, $project, $version) {
+  $info = "\n\n; Information added by the drush package handler on " . date('Y-m-d') . "\n";
+  $info .= "version = \"$version\"\n";
+  // .info files started with 5.x, so we don't have to worry about version
+  // strings like "4.7.x-1.0" in this regular expression. If we can't parse
+  // the version (also from an old "HEAD" release), or the version isn't at
+  // least 6.x, don't add any "core" attribute at all.
+  $matches = array();
+  if (preg_match('/^((\d+)\.x)-.*/', $version, $matches) && $matches[2] >= 6) {
+    $info .= "core = \"$matches[1]\"\n";
+  }
+  $info .= "project = \"$project\"\n";
+  $info .= 'datestamp = "'. time() ."\"\n";
+  $info .= "\n";
+
+  if (!$info_fd = fopen($file, 'a+')) {
+    drush_set_error(dt("ERROR: fopen(@file, 'ab') failed", array('@file' => $file)));
+    return false;
+  }
+  if (!fwrite($info_fd, $info)) {
+    drush_set_error(dt("ERROR: fwrite(@file) failed", array('@file' => $file)) . '<pre>' . $info);
+    return false;
+  }
+  return true;
 }
index 7d13d4e..0b612c2 100644 (file)
@@ -51,7 +51,7 @@ function package_handler_download_project(&$request, $release) {
   // Dev snapshots can be cached for 4 hours and releases for 1 year.
   $max_age = strpos($release['download_link'], '-dev') !== FALSE ? 3600*4 : 86400*365;
   $filename = basename($release['download_link']);
-  $path = drush_download_file($release['download_link'], $request['base_project_path'] . DIRECTORY_SEPARATOR . $filename, $max_age);
+  $path = drush_download_file($release['download_link'], $request['base_project_path'] . $filename, $max_age);
   if ($path || drush_get_context('DRUSH_SIMULATE')) {
     drush_log("Downloading " . $filename . " was successful.");
   }
@@ -72,7 +72,7 @@ function package_handler_download_project(&$request, $release) {
   $file_list = drush_tarball_extract($path, $request['base_project_path'], TRUE);
 
   // Move untarred directory to project_dir, if distinct.
-  if (($request['project_type'] == 'core') || (($request['project_type'] == 'profile') && (drush_get_option('variant', 'full') == 'full'))) {
+  if (($request['project_type'] == 'core') || (($request['project_type'] == 'profile') && (drush_get_option('variant', 'core') == 'core'))) {
     // Obtain the dodgy project_dir for drupal core.
     $project_dir = drush_trim_path($file_list[0]);
     if ($request['project_dir'] != $project_dir) {
index 5401032..15019a6 100644 (file)
@@ -90,10 +90,7 @@ function pm_drush_command() {
   );
   $update_suboptions = array(
     'lock' => array(
-      'lock-message' => array(
-        'description' => 'A brief message explaining why a project is being locked; displayed during pm-updatecode.  Optional.',
-        'example-value' => 'message',
-      ),
+      'lock-message' => 'A brief message explaining why a project is being locked; displayed during pm-updatecode.  Optional.',
       'unlock' => 'Remove the persistent lock from the specified projects so that they may be updated again.',
     ),
   );
@@ -138,14 +135,8 @@ function pm_drush_command() {
     'description' => 'Show a list of available extensions (modules and themes).',
     'callback arguments' => array(array(), FALSE),
     'options' => array(
-      'type' => array(
-        'description' => 'Filter by extension type. Choices: module, theme.',
-        'example-value' => 'module',
-      ),
-      'status' => array(
-        'description' => 'Filter by extension status. Choices: enabled, disabled and/or \'not installed\'. You can use multiple comma separated values. (i.e. --status="disabled,not installed").',
-        'example-value' => 'disabled',
-      ),
+      'type' => 'Filter by extension type. Choices: module, theme.',
+      'status' => 'Filter by extension status. Choices: enabled, disable and/or \'not installed\'. You can use multiple comma separated values. (i.e. --status="disabled,not installed").',
       'package' => 'Filter by project packages. You can use multiple comma separated values. (i.e. --package="Core - required,Other").',
       'core' => 'Filter out extensions that are not in drupal core.',
       'no-core' => 'Filter out extensions that are provided by drupal core.',
@@ -242,22 +233,13 @@ function pm_drush_command() {
       'projects' => 'A comma delimited list of drupal.org project names, with optional version. Defaults to \'drupal\'',
     ),
     'options' => array(
-      'destination' => array(
-        'description' => 'Path to which the project will be copied. If you\'re providing a relative path, note it is relative to the drupal root (if bootstrapped).',
-        'example-value' => 'path',
-      ),
+      'destination' => 'Path to which the project will be copied. If you\'re providing a relative path, note it is relative to the drupal root (if bootstrapped).',
       'use-site-dir' => 'Force to use the site specific directory. It will create the directory if it doesn\'t exist. If --destination is also present this option will be ignored.',
       'notes' => 'Show release notes after each project is downloaded.',
-      'variant' => array(
-        'description' => "Only useful for install profiles. Possible values: 'full', 'projects', 'profile-only'.",
-        'example-value' => 'full',
-      ),
+      'variant' => "Only useful for install profiles. Possible values: 'full', 'projects', 'profile-only'.",
       'select' => "Select the version to download interactively from a list of available releases.",
       'drupal-project-rename' => 'Alternate name for "drupal-x.y" directory when downloading Drupal project. Defaults to "drupal".',
-      'default-major' => array(
-        'description' => 'Specify the default major version of modules to download when there is no bootstrapped Drupal site.  Defaults to "7".',
-        'example-value' => '6',
-      ),
+      'default-major' => 'Specify the default major version of modules to download when there is no bootstrapped Drupal site.  Defaults to "7".',
       'skip' => 'Skip automatic downloading of libraries (c.f. devel).',
       'pipe' => 'Returns a list of the names of the extensions (modules and themes) contained in the downloaded projects.',
     ),
@@ -342,8 +324,13 @@ function pm_pm_update_complete() {
  */
 function pm_complete_extensions() {
   if (drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_FULL)) {
-    $extension_info = drush_get_extensions(FALSE);
-    return array('values' => array_keys($extension_info));
+    $extension_info = drush_get_extensions();
+    foreach ($extension_info as $key => $extension) {
+      if (!isset($extension->info['hidden'])) {
+        $extensions[] = $key;
+      }
+    }
+    return array('values' => $extensions);
   }
 }
 
@@ -496,85 +483,10 @@ function drush_get_projects(&$extensions = NULL) {
     $projects[$project]['extensions'][] = $extension->name;
   }
 
-  // Once we have all the extensions for each project, obtain projects' path.
-  $reserved = array('modules', 'sites', 'themes');
-  foreach ($projects as $name => $project) {
-    if ($name == 'drupal') {
-      continue;
-    }
-    drush_log(dt('Obtaining !project project path.', array('!project' => $name)), 'debug');
-    $path = _drush_pm_find_common_path($project['type'], $project['extensions']);
-    // Prevent from setting a reserved path. For example it may happen in a case
-    // where a module and a theme are declared as part of a same project.
-    // There's a special case, a project called "sites", this is the reason for
-    // the second condition here.
-    if ((in_array(basename($path), $reserved)) && (!in_array($name, $reserved))) {
-      drush_log(dt('Error while trying to find the common path for enabled extensions of project !project. Extensions are: !extensions.', array('!project' => $name, '!extensions' => implode(', ', $project['extensions']))), 'error');
-    }
-    else {
-      $projects[$name]['path'] = $path;
-    }
-  }
-
   return $projects;
 }
 
 /**
- * Helper function to find the common path for a list of extensions in the aim to obtain the project name.
- *
- * @param $project_type
- *  Type of project we're trying to find. Valid values: module, theme.
- * @param $extensions
- *  Array of extension names.
- */
-function _drush_pm_find_common_path($project_type, $extensions) {
-  // Select the first path as the candidate to be the common prefix.
-  $extension = array_pop($extensions);
-  while (!($path = drupal_get_path($project_type, $extension))) {
-    drush_log(dt('Unknown path for !extension !type.', array('!extension' => $extension, '!type' => $project_type)), 'warning');
-    $extension = array_pop($extensions);
-  }
-
-  // If there's only one extension we are done. Otherwise, we need to find
-  // the common prefix for all of them.
-  if (count($extensions) > 0) {
-    // Iterate over the other projects.
-    while($extension = array_pop($extensions)) {
-      $path2 = drupal_get_path($project_type, $extension);
-      if (!$path2) {
-        drush_log(dt('Unknown path for !extension !type.', array('!extension' => $extension, '!type' => $project_type)), 'debug');
-        continue;
-      }
-      // Option 1: same path.
-      if ($path == $path2) {
-        continue;
-      }
-      // Option 2: $path is a prefix of $path2.
-      if (strpos($path2, $path) === 0) {
-        continue;
-      }
-      // Option 3: $path2 is a prefix of $path.
-      if (strpos($path, $path2) === 0) {
-        $path = $path2;
-        continue;
-      }
-      // Option 4: no one is a prefix of the other. Find the common
-      // prefix by iteratively strip the rigthtmost piece of $path.
-      // We will iterate until a prefix is found or path = '.', that on the
-      // other hand is a condition theorically impossible to reach.
-      do {
-        $path = dirname($path);
-        if (strpos($path2, $path) === 0) {
-          break;
-        }
-      } while ($path != '.');
-    }
-  }
-
-  return $path;
-}
-
-/**
  * Returns a list of enabled modules.
  *
  * This is a simplified version of module_list().
@@ -650,7 +562,7 @@ function drush_pm_list() {
   $header[] = dt('Version');
   $rows[] = $header;
 
-  $extension_info = drush_get_extensions(FALSE);
+  $extension_info = drush_get_extensions();
   uasort($extension_info, '_drush_pm_sort_extensions');
 
   $major_version = drush_drupal_major_version();
@@ -664,6 +576,10 @@ function drush_pm_list() {
       unset($extension_info[$key]);
       continue;
     }
+    if (($major_version >= 6) and (isset($extension->info['hidden']))) {
+      unset($extension_info[$key]);
+      continue;
+    }
 
     // filter out core if --no-core specified
     if (drush_get_option('no-core', FALSE)) {
@@ -740,9 +656,9 @@ function drush_pm_find_project_from_extension($extension) {
 }
 
 /**
- * Validate callback. Determine the modules and themes that the user would like enabled.
+ * Command callback. Enable one or more extensions from downloaded projects.
  */
-function drush_pm_enable_validate() {
+function drush_pm_enable() {
   $args = pm_parse_arguments(func_get_args());
 
   $extension_info = drush_get_extensions();
@@ -852,36 +768,6 @@ function drush_pm_enable_validate() {
       }
     }
   }
-  
-  $searchpath = array();
-  foreach (array_merge($modules, $themes) as $name) {
-    $searchpath[] = dirname($extension_info[$name]->filename);
-  }
-  // Add all modules that passed validation to the drush
-  // list of commandfiles (if they have any).  This
-  // will allow these newly-enabled modules to participate
-  // in the pre-pm_enable and post-pm_enable hooks.
-  if (!empty($searchpath)) {
-    _drush_add_commandfiles($searchpath);
-  }
-  
-  drush_set_context('PM_ENABLE_EXTENSION_INFO', $extension_info);
-  drush_set_context('PM_ENABLE_MODULES', $modules);
-  drush_set_context('PM_ENABLE_THEMES', $themes);
-  
-  return TRUE;
-}
-
-/**
- * Command callback. Enable one or more extensions from downloaded projects.
- * Note that the modules and themes to be enabled were evaluated during the
- * pm-enable validate hook, above.
- */
-function drush_pm_enable() {
-  // Get the data built during the validate phase
-  $extension_info = drush_get_context('PM_ENABLE_EXTENSION_INFO');
-  $modules = drush_get_context('PM_ENABLE_MODULES');
-  $themes = drush_get_context('PM_ENABLE_THEMES');
 
   // Inform the user which extensions will finally be enabled.
   $extensions = array_merge($modules, $themes);
@@ -903,24 +789,32 @@ function drush_pm_enable() {
   // Enable modules and pass dependency validation in form submit.
   if (!empty($modules)) {
     drush_module_enable($modules);
+    drush_system_modules_form_submit(pm_module_list());
   }
 
   // Inform the user of final status.
   $rsc = drush_db_select('system', array('name', 'status'), 'name IN (:extensions)', array(':extensions' => $extensions));
   $problem_extensions = array();
+  $searchpath = array();
   while ($extension = drush_db_fetch_object($rsc)) {
     if ($extension->status) {
       drush_log(dt('!extension was enabled successfully.', array('!extension' => $extension->name)), 'ok');
+      $searchpath[] = dirname($extension_info[$extension->name]->filename);
     }
     else {
       $problem_extensions[] = $extension->name;
     }
   }
+  // Add all modules that were enabled to the drush
+  // list of commandfiles (if they have any).  This
+  // will allow these newly-enabled modules to participate
+  // in the post_pm_enable hook.
+  if (!empty($searchpath)) {
+    _drush_add_commandfiles($searchpath);
+  }
   if (!empty($problem_extensions)) {
     return drush_set_error('DRUSH_PM_ENABLE_EXTENSION_ISSUE', dt('There was a problem enabling !extension.', array('!extension' => implode(',', $problem_extensions))));
   }
-  // Return the list of extensions enabled
-  return $extensions;
 }
 
 /**
@@ -976,11 +870,9 @@ function drush_pm_disable() {
     foreach ($required as $module) {
       if (isset($modules[$module])) {
         unset($modules[$module]);
-        $info = $extension_info[$module]->info;
         // No message for hidden modules.
-        if (!isset($info['hidden'])) {
-          $explanation = !empty($info['explanation']) ? ' ' . dt('Reason: !explanation', array('!explanation' => strip_tags($info['explanation'])))  : '';
-          drush_log(dt('!module is a required module and can\'t be disabled.', array('!module' => $module)) . $explanation, 'ok');
+        if (!isset($extension_info[$module]->info['hidden'])) {
+          drush_log(dt('!module is a required module and can\'t be disabled.', array('!module' => $module)), 'ok');
         }
       }
     }
@@ -1006,6 +898,7 @@ function drush_pm_disable() {
   // Disable modules and pass dependency validation in form submit.
   if (!empty($modules)) {
     drush_module_disable($modules);
+    drush_system_modules_form_submit(pm_module_list());
   }
 
   // Inform the user of final status.
@@ -1116,6 +1009,78 @@ function drush_pm_uninstall() {
 }
 
 /**
+ * Completes projects' update data with the path to install location on disk.
+ *
+ * Given an array of release info for available projects, find the path to the install location.
+ */
+function _pm_get_project_path($data, $lookup) {
+  foreach ($data as $name => $release) {
+    if ($name == 'drupal') {
+      continue;
+    }
+    // Array of extensions (modules/themes) within the project.
+    $extensions = array_keys($release[$lookup]);
+    $path = _pm_find_common_path($release['project_type'], $extensions);
+    $reserved = array('modules', 'sites', 'themes');
+    if ((in_array(basename($path), $reserved)) && (!in_array($name, $reserved))) {
+      drush_log(dt('Error while trying to find the common path for enabled extensions of project !project. Extensions are: !extensions.', array('!project' => $name, '!extensions' => implode(', ', $extensions))), 'error');
+      unset($data[$name]);
+    }
+    else {
+      $data[$name]['path'] = $path;
+    }
+  }
+
+  return $data;
+}
+
+/**
+ * Helper function to find the common path for a list of extensions in the aim to obtain the project name.
+ *
+ * @param $project_type
+ *  Type of project we're trying to find. Valid values: module, theme.
+ * @param $extensions
+ *  Array of extension names.
+ */
+function _pm_find_common_path($project_type, $extensions) {
+  // Select the first path as the candidate to be the common prefix.
+  $path = drupal_get_path($project_type, array_pop($extensions));
+  // If there's only one extension we are done. Otherwise, we need to find
+  // the common prefix for all of them.
+  if (count($extensions) > 0) {
+    // Iterate over the other projects.
+    while($extension = array_pop($extensions)) {
+      $path2 = drupal_get_path($project_type, $extension);
+      // Option 1: same path.
+      if ($path == $path2) {
+        continue;
+      }
+      // Option 2: $path is a prefix of $path2.
+      if (strpos($path2, $path) === 0) {
+        continue;
+      }
+      // Option 3: $path2 is a prefix of $path.
+      if (strpos($path, $path2) === 0) {
+        $path = $path2;
+        continue;
+      }
+      // Option 4: no one is a prefix of the other. Find the common
+      // prefix by iteratively strip the rigthtmost piece of $path.
+      // We will iterate until a prefix is found or path = '.', that on the
+      // other hand is a condition theorically impossible to reach.
+      do {
+        $path = dirname($path);
+        if (strpos($path2, $path) === 0) {
+          break;
+        }
+      } while ($path != '.');
+    }
+  }
+
+  return $path;
+}
+
+/**
  * Command callback. Show available releases for given project(s).
  */
 function drush_pm_releases() {
@@ -1251,12 +1216,16 @@ function drush_pm_updatecode_postupdate() {
 /**
  * Sanitize user provided arguments to several pm commands.
  *
- * Return an array of arguments off a space and/or comma separated values.
+ * Return an array of arguments off a space and/or comma separated values. It also
+ * lowercase arguments and optionally convert dashes to underscores.
  */
-function pm_parse_arguments($args, $dashes_to_underscores = TRUE) {
+function pm_parse_arguments($args, $dashes_to_underscores = TRUE, $lowercase = TRUE) {
   $arguments = _convert_csv_to_array($args);
   foreach ($arguments as $key => $argument) {
     $argument = ($dashes_to_underscores) ? strtr($argument, '-', '_') : $argument;
+    if ($lowercase) {
+      $arguments[$key] = strtolower($argument);
+    }
   }
   return $arguments;
 }
@@ -1280,7 +1249,7 @@ function pm_parse_project_version($requests) {
     preg_match('/-+(HEAD|(?:(\d+\.x)-+)?(\d+\.[\dx]+.*))$/', $request, $matches);
     if (isset($matches[1])) {
       // The project is whatever we have prior to the version part of the request.
-      $project = trim(substr($request, 0, strlen($request) - strlen($matches[0])), ' -');
+      $project = strtolower(trim(substr($request, 0, strlen($request) - strlen($matches[0])), ' -'));
 
       if ($matches[1] == 'HEAD' || $matches[2] == 'HEAD') {
         drush_log('DRUSH_PM_HEAD', 'Can\'t download HEAD releases because Drupal.org project information only provides for numbered release nodes.', 'warning');
@@ -1772,43 +1741,3 @@ function drush_find_empty_directories($dir, $exclude = array()) {
   }
   return $empty_dirs;
 }
-
-/**
- * Inject metadata into all .info files for a given project.
- *
- * @param string $project_dir
- *   The full path to the root directory of the project to operate on.
- * @param string $project_name
- *   The project machine name (AKA shortname).
- * @param string $version
- *   The version string to inject into the .info file(s).
- *
- * @return boolean
- *   TRUE on success, FALSE on any failures appending data to .info files.
- */
-function drush_pm_inject_info_file_metadata($project_dir, $project_name, $version) {
-  $info_files = drush_scan_directory($project_dir, '/.*\.info$/');
-  if (!empty($info_files)) {
-    // Construct the string of metadata to append to all the .info files.
-    // Taken straight from: http://drupalcode.org/project/drupalorg.git/blob/refs/heads/6.x-3.x:/drupalorg_project/plugins/release_packager/DrupalorgProjectPackageRelease.class.php#l192
-    $info = "\n\n; Information added by drush on " . date('Y-m-d') . "\n";
-    $info .= "version = \"$version\"\n";
-    // .info files started with 5.x, so we don't have to worry about version
-    // strings like "4.7.x-1.0" in this regular expression. If we can't parse
-    // the version (also from an old "HEAD" release), or the version isn't at
-    // least 6.x, don't add any "core" attribute at all.
-    $matches = array();
-    if (preg_match('/^((\d+)\.x)-.*/', $version, $matches) && $matches[2] >= 6) {
-      $info .= "core = \"$matches[1]\"\n";
-    }
-    $info .= "project = \"$project_name\"\n";
-    $info .= 'datestamp = "'. time() ."\"\n";
-    $info .= "\n";
-    foreach ($info_files as $info_file) {
-      if (!drush_file_append_data($info_file->filename, $info)) {
-        return FALSE;
-      }
-    }
-  }
-  return TRUE;
-}
index c56688f..0a2f34f 100644 (file)
@@ -16,12 +16,8 @@ define('RELEASE_INFO_DEFAULT_URL', 'http://updates.drupal.org/release-history');
  * @param Array &$request
  *   A project request as returned by pm_parse_project_version(). The array will
  *   be expanded with the project type.
- * @param String $restrict_to
- *   One of:
- *     'dev': Forces choosing a -dev release.
- *     'version': Forces choosing a point release.
- *     '': No restriction.
- *   Default is ''.
+ * @param Boolean $dev
+ *   Forces to choose a -dev release.
  * @param String $select
  *   Strategy for selecting a release, should be one of:
  *    - auto: Try to select the latest release, if none found allow the user
@@ -36,7 +32,7 @@ define('RELEASE_INFO_DEFAULT_URL', 'http://updates.drupal.org/release-history');
  * @return
  *  The selected release xml object.
  */
-function release_info_fetch(&$request, $restrict_to = '', $select = 'never', $all = FALSE) {
+function release_info_fetch(&$request, $dev = FALSE, $select = 'never', $all = FALSE) {
   if (!in_array($select, array('auto', 'never', 'always'))) {
     drush_log(dt("Error: select strategy must be one of: auto, never, always", array(), 'error'));
     return FALSE;
@@ -51,7 +47,7 @@ function release_info_fetch(&$request, $restrict_to = '', $select = 'never', $al
 
   if ($select != 'always') {
     // Try to identify the most appropriate release.
-    $release = updatexml_parse_release($request, $xml, $restrict_to);
+    $release = updatexml_parse_release($request, $xml, $dev);
     if ($release) {
       return $release;
     }
@@ -63,7 +59,7 @@ function release_info_fetch(&$request, $restrict_to = '', $select = 'never', $al
   }
 
   $project_info = updatexml_get_releases_from_xml($xml, $request['name']);
-  $releases = release_info_filter_releases($project_info['releases'], $all, $restrict_to);
+  $releases = release_info_filter_releases($project_info['releases'], $all, $dev);
   $options = array();
   foreach($releases as $version => $release) {
     $options[$version] = array($version, '-', gmdate('Y-M-d', $release['date']), '-', implode(', ', $release['release_status']));
@@ -263,13 +259,13 @@ function _release_info_compare_date($a, $b) {
  * @param $all
  *   Show all releases. If FALSE, shows only the first release that is
  *   Recommended or Supported or Security or Installed.
- * @param String $restrict_to
- *   If set to 'dev', show only development release.
+ * @param $dev
+ *   Show only development release.
  * @param $show_all_until_installed
  *   If TRUE, then all releases will be shown until the INSTALLED release
  *   is found, at which point the algorithm will stop.
  */
-function release_info_filter_releases($releases, $all = FALSE, $restrict_to = '', $show_all_until_installed = TRUE) {
+function release_info_filter_releases($releases, $all = FALSE, $dev = FALSE, $show_all_until_installed = TRUE) {
   // Start off by sorting releases by release date.
   uasort($releases, '_release_info_compare_date');
   // Find version_major for the installed release.
@@ -282,7 +278,6 @@ function release_info_filter_releases($releases, $all = FALSE, $restrict_to = ''
   // Now iterate through and filter out the releases we're interested in.
   $options = array();
   $limits_list = array();
-  $dev = $restrict_to == 'dev';
   foreach ($releases as $version => $release_info) {
     if (!$dev || ((array_key_exists('version_extra', $release_info)) && ($release_info['version_extra'] == 'dev'))) {
       $saw_unique_status = FALSE;
@@ -321,97 +316,62 @@ function release_info_filter_releases($releases, $all = FALSE, $restrict_to = ''
   // releases found.  If this is the case, then we will force ALL to TRUE
   // and show everything on the second iteration.
   if (($all === FALSE) && ($show_all_until_installed === TRUE)) {
-    $options = release_info_filter_releases($releases, empty($options), $restrict_to, FALSE);
+    $options = release_info_filter_releases($releases, empty($options), $dev, FALSE);
   }
   return $options;
 }
 
 /**
- * Pick most appropriate release from XML list or ask the user if no one fits.
+ * Pick most appropriate release from XML list  or ask the user if no one fits.
  *
  * @param array $request
- *   An array with project and version strings as returned by
- *   pm_parse_project_version().
+ *   An array of version specifications as returned by pm_parse_project_version().
  * @param resource $xml
  *   A handle to the XML document.
- * @param String $restrict_to
- *   One of:
- *     'dev': Forces a -dev release.
- *     'version': Forces a point release.
- *     '': No restriction (auto-selects latest recommended or supported release
-           if requested release is not found).
- *   Default is ''.
  */
-function updatexml_parse_release($request, $xml, $restrict_to = '') {
+function updatexml_parse_release($request, $xml, $dev = FALSE) {
   if (!empty($request['version'])) {
-    $matches = array();
-    // See if we only have a branch version.
-    if (preg_match('/^\d+\.x-(\d+)$/', $request['version'], $matches)) {
-      $xpath_releases = "/project/releases/release[status='published'][version_major=" . (string)$matches[1] . "]";
-      $releases = @$xml->xpath($xpath_releases);
-    }
-    else {
-      // In some cases, the request only says something like '7.x-3.x' but the
-      // version strings include '-dev' on the end, so we need to append that
-      // here for the xpath to match below.
-      if (substr($request['version'], -2) == '.x') {
-        $request['version'] .= '-dev';
-      }
-      $releases = $xml->xpath("/project/releases/release[status='published'][version='" . $request['version'] . "']");
-      if (empty($releases)) {
-        if (empty($restrict_to)) {
-          drush_log(dt("Could not locate !project version !version, will try to download latest recommended or supported release.", array('!project' => $request['name'], '!version' => $request['version'])), 'warning');
-        }
-        else {
-          drush_log(dt("Could not locate !project version !version.", array('!project' => $request['name'], '!version' => $request['version'])), 'warning');
-          return FALSE;
-        }
-      }
-    }
-  }
-
-  if ($restrict_to == 'dev') {
-    $releases = @$xml->xpath("/project/releases/release[status='published'][version_extra='dev']");
+    $releases = $xml->xpath("/project/releases/release[status='published'][version='" . $request['version'] . "']");
     if (empty($releases)) {
-      drush_print(dt('There is no development release for project !project.', array('!type' => $release_type, '!project' => $request['name'])));
-      return FALSE;
+      drush_log(dt("Could not locate specified project version, downloading latest stable version"), 'warning');
     }
   }
-
   // If that did not work, we will get the first published release for the
-  // recommended major version or fallback to other supported major versions.
+  // recommended major version.
   if (empty($releases)) {
-    foreach(array('recommended_major', 'supported_majors') as $release_type) {
-      if ($versions = $xml->xpath("/project/$release_type")) {
-        $xpath = "/project/releases/release[status='published'][version_major=" . (string)$versions[0] . "]";
-        $releases = @$xml->xpath($xpath);
-        if (!empty($releases)) {
-          break;
-        }
-      }
+    if ($recommended_major = $xml->xpath("/project/recommended_major")) {
+      $xpath_releases = "/project/releases/release[status='published'][version_major=" . (string)$recommended_major[0] . "]";
+      $releases = @$xml->xpath($xpath_releases);
     }
   }
-
-  // If there are releases found, let's try first to fetch one with no
-  // 'version_extra'. Otherwise, use all.
+  // If there are recommended releases (no 'version_extra' elements), then use
+  // only recommended releases.  Otherwise, use all; in this case, the
+  // recommended release defaults to the latest published release with the
+  // right recommended major version number.
+  $recommended_releases = array();
   if (!empty($releases)) {
-    $stable_releases = array();
     foreach ($releases as $one_release) {
       if (!array_key_exists('version_extra', $one_release)) {
-        $stable_releases[] = $one_release;
+        $recommended_releases[] = $one_release;
       }
     }
-    if (!empty($stable_releases)) {
-      $releases = $stable_releases;
-    }
+  }
+  if (!empty($recommended_releases)) {
+    $releases = $recommended_releases;
+  }
+  $release_type = 'recommended';
+  if ($dev) {
+    $releases = @$xml->xpath("/project/releases/release[status='published'][version_extra='dev']");
+    $release_type = 'development';
   }
 
   if (empty($releases)) {
-    drush_print(dt('There are no releases for project !project.', array('!project' => $request['name'])));
+    drush_print(dt('There is no !type release for project !project.', array('!type' => $release_type, '!project' => $request['name'])));
     return FALSE;
   }
 
-  // First published release is just the first value in $releases.
+  // First published release for the recommended major version is just the
+  // first value in $releases.
   return (array)$releases[0];
 }
 
@@ -540,7 +500,7 @@ function updatexml_get_releases_from_xml($xml, $project) {
 function updatexml_determine_project_type($xml) {
   $project_types = array(
     'core' => 'Drupal core',
-    'profile' => 'Distributions',
+    'profile' => 'Installation profiles',
     'module' => 'Modules',
     'theme' => 'Themes',
     'theme engine' => 'Theme engines',
index 4318d74..fab49c4 100644 (file)
@@ -106,23 +106,19 @@ function _pm_get_update_info($projects) {
       unset($data[$project_name]);
       continue;
     }
-    // Discard projects with unknown installation path.
-    if ($project_name != 'drupal' && !isset($projects[$project_name]['path'])) {
-      unset($data[$project_name]);
-      continue;
-    }
     // Allow to update disabled projects.
     if (in_array($project['project_type'], array('module-disabled', 'theme-disabled'))) {
       $data[$project_name]['project_type'] = substr($project['project_type'], 0, strpos($project['project_type'], '-'));
     }
-    // Add some info from the project to $data.
-    $data[$project_name] += array(
-      'path'  => $projects[$project_name]['path'],
-      'label' => $projects[$project_name]['label'],
-    );
+    // Set project label (title + name).
+    $data[$project_name]['label'] = $projects[$project_name]['label'];
+    // Set 'includes' key to all extensions. By default it only contain enabled
+    // extensions and _pm_get_project_path() needs all of them.
+    $data[$project_name]['includes'] = drupal_map_assoc($projects[$project_name]['extensions']);
     // Store all releases, not just the ones selected by update.module.
     $data[$project_name]['releases'] = $available[$project_name]['releases'];
   }
+  $data = _pm_get_project_path($data, 'includes');
 
   return $data;
 }
index b0356b0..d5da191 100644 (file)
@@ -73,21 +73,17 @@ function _pm_get_update_info($projects) {
       unset($data[$project_name]);
       continue;
     }
-    // Discard projects with unknown installation path.
-    if ($project_name != 'drupal' && !isset($projects[$project_name]['path'])) {
-      unset($data[$project_name]);
-      continue;
-    }
     // Allow to update disabled projects.
     if (in_array($project['project_type'], array('disabled-module', 'disabled-theme'))) {
       $data[$project_name]['project_type'] = substr($project['project_type'], strpos($project['project_type'], '-') + 1);
     }
-    // Add some info from the project to $data.
-    $data[$project_name] += array(
-      'path'  => $projects[$project_name]['path'],
-      'label' => $projects[$project_name]['label'],
-    );
+    // Set project label (title + name).
+    $data[$project_name]['label'] = $projects[$project_name]['label'];
+    // Set 'includes' key to all extensions. By default it only contain enabled
+    // extensions and _pm_get_project_path() needs all of them.
+    $data[$project_name]['includes'] = drupal_map_assoc($projects[$project_name]['extensions']);
   }
+  $data = _pm_get_project_path($data, 'includes');
 
   return $data;
 }
index 598d506..bcbd9a3 100644 (file)
@@ -140,8 +140,7 @@ function drush_pm_updatecode() {
 
   // If specific project updates were requested then remove releases for all
   // others.
-  $requested = func_get_args();
-  if (!empty($requested)) {
+  if (!empty($requests)) {
     foreach ($updateable as $name => $project) {
       if (!isset($requests[$name])) {
         unset($updateable[$name]);
@@ -274,10 +273,6 @@ function _pm_update_core(&$project, $tmpfile) {
       $skip_list[] = $item;
       unset($items_to_test[$item]);
     }
-    elseif (is_link($item)) {
-      $skip_list[] = $item;
-      unset($items_to_test[$item]);
-    }
   }
   $project['skip_list'] = $skip_list;
 
index c9ce313..d860580 100644 (file)
@@ -12,7 +12,7 @@
 class DrupalServer extends HTTPServer {
   // We pass in variables, rather than querying options here, to allow this to
   // potentially be used in other commands.
-  public $path, $debug, $env, $site;
+  public $site, $path, $conf_inject, $user, $watchdog, $debug, $first_request_complete;
 
   /**
    * This is the equivalent of .htaccess, passing requests to files if they
@@ -20,7 +20,28 @@ class DrupalServer extends HTTPServer {
    * of CGI environment variables here.
    */
   function route_request($request) {
-    $cgi_env = $this->env;
+    $cgi_env = array();
+
+    // We pass in the effective base_url to our auto_prepend_script via the cgi
+    // environment. This allows Drupal to generate working URLs to this http
+    // server, whilst finding the correct multisite from the HTTP_HOST header.
+    $cgi_env['RUNSERVER_BASE_URL'] = 'http://' . $this->addr . ':' . $this->port;
+
+    // We pass in an array of $conf overrides using the same approach.
+    // By default we set drupal_http_request_fails to FALSE, as the httpserver
+    // is unable to process simultaneous requests on some systems.
+    // This is available as an option for developers to pass in their own
+    // favorite $conf overrides (e.g. disabling css aggregation).
+    $conf_inject = $this->conf_inject;
+    $cgi_env['RUNSERVER_CONF'] = urlencode(serialize($conf_inject));
+
+    // We pass in the specified user (if set) - should be a fully loaded user
+    // object. This will automatically log this user in the browser during the
+    // first request (but not subsequent requests, to allow logging out).
+    if (!empty($this->user) && $this->user->uid && $this->first_request_complete !== TRUE) {
+      $this->first_request_complete = TRUE;
+      $cgi_env['RUNSERVER_USER'] = urlencode(serialize($this->user));
+    }
 
     // Handle static files and php scripts accessed directly
     $uri = $request->uri;
@@ -51,6 +72,7 @@ class DrupalServer extends HTTPServer {
    * Override get started event.
    */
   function listening() {
+    drush_print(dt('HTTP server listening on !addr, port !port (see http://!hostname:!port/), serving site !site...', array('!addr' => $this->addr, '!hostname' => $this->hostname, '!port' => $this->port, '!site' => $this->site)));
     if (!empty($this->path)) {
       drush_start_browser($this->path);
     }
@@ -61,7 +83,16 @@ class DrupalServer extends HTTPServer {
    */
   function request_done($request) {
     drush_print(trim($this->get_log_line($request), "\n"));
-
+    $headers = $request->response->headers;
+    if ($this->watchdog && isset($headers['X-Runserver-Watchdog'])) {
+      $results = unserialize(urldecode($headers['X-Runserver-Watchdog']));
+      foreach ($results as $result) {
+        $result = (object)$result;
+        $result->uid = $result->user->uid;
+        $result = core_watchdog_format_result($result, TRUE);
+        drush_print("Watchdog: {$result->date} ({$result->severity}, {$result->type}, {$result->user->name}) {$result->message}", 2);
+      }
+    }
     if ($this->debug) {
       drush_print_r($request);
     }
index fbac527..15a835c 100644 (file)
@@ -3,48 +3,30 @@
 // We set the base_url so that Drupal generates correct URLs for runserver
 // (e.g. http://127.0.0.1:8888/...), but can still select and serve a specific
 // site in a multisite configuration (e.g. http://mysite.com/...).
-$base_url = runserver_env('RUNSERVER_BASE_URL');
+$base_url = $_SERVER['RUNSERVER_BASE_URL'];
 
-// Complete $_GET['q'] for Drupal 6 with built in server
-// - this uses the Drupal 7 method.
-if (!isset($_GET['q']) && isset($_SERVER['REQUEST_URI'])) {
-    // This request is either a clean URL, or 'index.php', or nonsense.
-    // Extract the path from REQUEST_URI.
-    $request_path = strtok($_SERVER['REQUEST_URI'], '?');
-    $base_path_len = strlen(rtrim(dirname($_SERVER['SCRIPT_NAME']), '\/'));
-    // Unescape and strip $base_path prefix, leaving q without a leading slash.
-  $_GET['q'] = substr(urldecode($request_path), $base_path_len + 1);
-}
-
-// We hijack filter_init (which core filter module does not implement) as
-// a convenient place to affect early changes.
-if (!function_exists('filter_init')) {
+// We hijack system_boot (which core system module does not implement) as
+// a convenient place to affect mid-bootstrap changes.
+if (!function_exists('system_boot')) {
   // Check function_exists as a safety net in case it is added in future.
-  function filter_init() {
+  function system_boot() {
     global $conf, $user;
     // Inject values into the $conf array - will apply to all sites.
     // This can be a useful place to apply generic development settings.
-    $conf_inject = unserialize(urldecode(runserver_env('RUNSERVER_CONF')));
+    $conf_inject = unserialize(urldecode($_SERVER['RUNSERVER_CONF']));
     // Merge in the injected conf, overriding existing items.
     $conf = array_merge($conf, $conf_inject);
-
-    // Log in user if needed.
-    if (isset($_GET['login'])) {
-      $uid = runserver_env('RUNSERVER_USER');
-      if (!empty($uid) && $user->uid !== $uid) {
-        // If a user was provided, log in as this user.
-        $user = user_load($uid);
-        if (function_exists('drupal_session_regenerate')) {
-          // Drupal 7
-          drupal_session_regenerate();
-        }
-        else {
-          // Drupal 6
-          sess_regenerate();
-        }
+    if (isset($_SERVER['RUNSERVER_USER'])) {
+      // If a user was provided, log in as this user.
+      $user = unserialize(urldecode($_SERVER['RUNSERVER_USER']));
+      if (function_exists('drupal_session_regenerate')) {
+        // Drupal 7
+        drupal_session_regenerate();
+      }
+      else {
+        // Drupal 6
+        sess_regenerate();
       }
-      // Refresh the page (in case access denied has been called already).
-      drupal_goto($_GET['q']);
     }
   }
 }
@@ -54,25 +36,16 @@ if (!function_exists('filter_init')) {
 if (!function_exists('system_watchdog')) {
   // Check function_exists as a safety net in case it is added in future.
   function system_watchdog($log_entry = array()) {
-    $message = strtr('Watchdog: !message | severity: !severity | type: !type | uid: !uid | !ip | !request_uri | !referer | !link', array(
-      '!message'     => strip_tags(!isset($log_entry['variables']) ? $log_entry['message'] : strtr($log_entry['message'], $log_entry['variables'])),
-      '!severity'    => $log_entry['severity'],
-      '!type'        => $log_entry['type'],
-      '!ip'          => $log_entry['ip'],
-      '!request_uri' => $log_entry['request_uri'],
-      '!referer'     => $log_entry['referer'],
-      '!uid'         => $log_entry['user']->uid,
-      '!link'        => strip_tags($log_entry['link']),
-    ));
-    error_log($message);
+    static $logs = array();
+    $logs[] = $log_entry;
+    $data = urlencode(serialize($logs));
+    if (function_exists('drupal_add_http_header')) {
+      // Drupal 7
+      drupal_add_http_header('X-Runserver-Watchdog', $data);
+    }
+    else {
+      // Drupal 6
+      drupal_set_header('X-Runserver-Watchdog: ' . $data);
+    }
   }
 }
-
-function runserver_env($key) {
-  if (isset($_SERVER[$key])) {
-    return $_SERVER[$key];
-  }
-  else {
-    return getenv($key);
-  }
-}
\ No newline at end of file
index 711412f..76a12c4 100644 (file)
@@ -52,12 +52,11 @@ function runserver_drush_command() {
       'addr:port/path' => 'Host IP address and port number to bind to and path to open in web browser. Format is addr:port/path, default 127.0.0.1:8888, all elements optional. See examples for shorthand.',
     ),
     'options' => array(
-      'server' => 'Which http server to use - either: "cgi" for a CGI based httpserver (default, requires php 5.3 and php-cgi binary) or "builtin" for php 5.4 built in http server.',
       'php-cgi' => 'Name of the php-cgi binary. If it is not on your current $PATH you should include the full path. You can include command line parameters to pass into php-cgi.',
-      'variables' => 'Key-value array of variables to override in the $conf array for the running site. By default disables drupal_http_request_fails to avoid errors on Windows (which supports only one connection at a time). Comma delimited list of name=value pairs (or array in drushrc).',
+      'conf-inject' => 'Key-value array of variables to override in the $conf array for the running site. By default disables drupal_http_request_fails to avoid errors on Windows (which supports only one connection at a time). Note that as this is a key-value array, it can only be specified in a drushrc or alias file, and not on the command line.',
       'default-server' => 'A default addr:port/path to use for any values not specified as an argument.',
-      'user' => 'If opening a web browser, automatically log in as this user (user ID or username).',
-      'browser' => 'If opening a web browser, which browser to user (defaults to operating system default).',
+      'user' => 'If opening a web browser, automatically log in as this user (user ID or username)',
+      'watchdog' => 'Collect and integrate watchdog messages from each request into the log',
       'dns' => 'Resolve hostnames/IPs using DNS/rDNS (if possible) to determine binding IPs and/or human friendly hostnames for URLs and browser.',
     ),
     'aliases' => array('rs'),
@@ -67,7 +66,7 @@ function runserver_drush_command() {
       'drush rs --php-cgi=php5-cgi --dns localhost:8888/user' => 'Start runserver on localhost (using rDNS to determine binding IP), port 8888, and open /user in browser. Use "php5-cgi" as the php-cgi binary.',
       'drush rs /' => 'Start runserver on default IP/port (127.0.0.1, port 8888), and open / in browser.',
       'drush rs --default-server=127.0.0.1:8080/ -' => 'Use a default (would be specified in your drushrc) that starts runserver on port 8080, and opens a browser to the front page. Set path to a single hyphen path in argument to prevent opening browser for this session.',
-      'drush rs --server=builtin :9000/admin' => 'Start builtin php 5.4 runserver on 127.0.0.1, port 9000, and open /admin in browser. Note that you need a colon when you specify port and path, but no IP.',
+      'drush rs --watchdog :9000/admin' => 'Start runserver on 127.0.0.1, port 9000, and open /admin in browser, including any watchdog messages for the session in the log. Note that you need a colon when you specify port and path, but no IP.',
     ),
   );
   return $items;
@@ -78,53 +77,10 @@ function runserver_drush_command() {
  */
 function drush_core_runserver_validate() {
   if (version_compare(PHP_VERSION, '5.3.0') < 0) {
-    return drush_set_error('RUNSERVER_PHP53_VERSION', dt('The runserver command requires php 5.3, which could not be found.'));
+    return drush_set_error('RUNSERVER_PHP_VERSION', dt('The runserver command requires php 5.3, which could not be found.'));
   }
-  $php_cgi = drush_shell_exec('which ' . drush_get_option('php-cgi', 'php-cgi'));
-  $builtin = version_compare(PHP_VERSION, '5.4.0') >= 0;
-  $server = drush_get_option('server', FALSE);
-  if (!$server) {
-    // No server specified, try and find a valid server option, preferring cgi.
-    if ($php_cgi) {
-      $server = 'cgi';
-      drush_set_option('server', 'cgi');
-    }
-    else if ($builtin) {
-      $server = 'builtin';
-      drush_set_option('server', 'builtin');
-    }
-    else {
-      return drush_set_error('RUNSERVER_PHP_CGI54', dt('The runserver command requires either the php-cgi binary, or php 5.4 (or higher). Neither could not be found.'));
-    }
-  }
-  else if ($server == 'cgi' && !$php_cgi) {
-    // Validate user specified cgi server option.
-    return drush_set_error('RUNSERVER_PHP_CGI', dt('The runserver command with the "cgi" server requires the php-cgi binary, which could not be found.'));
-  }
-  else if ($server == 'builtin' && !$builtin) {
-    // Validate user specified builtin server option.
-    return drush_set_error('RUNSERVER_PHP_CGI', dt('The runserver command with the "builtin" server requires php 5.4 (or higher), which could not be found.'));
-  }
-
-  // Update with detected configuration.
-  $server = drush_get_option('server', FALSE);
-  if ($server == 'cgi') {
-    // Fetch httpserver cgi based server to our /lib directory, if needed.
-    $lib = drush_get_option('lib', DRUSH_BASE_PATH . '/lib');
-    $httpserverfile = $lib . '/' . DRUSH_HTTPSERVER_DIR_BASE . substr(DRUSH_HTTPSERVER_VERSION, 0, 7) . '/httpserver.php';
-    if (!drush_file_not_empty($httpserverfile)) {
-      // Download and extract httpserver, and confirm success.
-      drush_lib_fetch(DRUSH_HTTPSERVER_BASE_URL . DRUSH_HTTPSERVER_VERSION);
-      if (!drush_file_not_empty($httpserverfile)) {
-        // Something went wrong - the library is still not present.
-        return drush_set_error('RUNSERVER_HTTPSERVER_LIB_NOT_FOUND', dt("The runserver command needs a copy of the httpserver library in order to function, and the attempt to download this file automatically failed. To continue you will need to download the package from !url, extract it into the !lib directory, such that httpserver.php exists at !httpserverfile.", array('!version' => DRUSH_HTTPSERVER_VERSION, '!url' => DRUSH_HTTPSERVER_BASE_URL . DRUSH_HTTPSERVER_VERSION, '!httpserverfile' => $httpserverfile, '!lib' => $lib)));
-      }
-    }
-  }
-
-  // Check we have a valid server.
-  if (!in_array($server, array('cgi', 'builtin'))) {
-    return drush_set_error('RUNSERVER_INVALID', dt('Invalid server specified.'));
+  if (!drush_shell_exec('which ' . drush_get_option('php-cgi', 'php-cgi'))) {
+    return drush_set_error('RUNSERVER_PHP_CGI', dt('The runserver command requires the php-cgi binary, which could not be found.'));
   }
 }
 
@@ -134,6 +90,29 @@ function drush_core_runserver_validate() {
 function drush_core_runserver($uri = NULL) {
   global $user;
 
+  // Fetch httpserver to our /lib directory, if needed.
+  $lib = drush_get_option('lib', DRUSH_BASE_PATH . '/lib');
+  $httpserverfile = $lib . '/' . DRUSH_HTTPSERVER_DIR_BASE . substr(DRUSH_HTTPSERVER_VERSION, 0, 7) . '/httpserver.php';
+  if (!drush_file_not_empty($httpserverfile)) {
+    // Download and extract httpserver, and confirm success.
+    drush_lib_fetch(DRUSH_HTTPSERVER_BASE_URL . DRUSH_HTTPSERVER_VERSION);
+    if (!drush_file_not_empty($httpserverfile)) {
+      // Something went wrong - the library is still not present.
+      return drush_set_error('RUNSERVER_HTTPSERVER_LIB_NOT_FOUND', dt("The runserver command needs a copy of the httpserver library in order to function, and the attempt to download this file automatically failed. To continue you will need to download the package from !url, extract it into the !lib directory, such that httpserver.php exists at !httpserverfile.", array('!version' => DRUSH_HTTPSERVER_VERSION, '!url' => DRUSH_HTTPSERVER_BASE_URL . DRUSH_HTTPSERVER_VERSION, '!httpserverfile' => $httpserverfile, '!lib' => $lib)));
+    }
+  }
+
+  // Include the library and our class that extends it.
+  require_once $httpserverfile;
+  require_once 'runserver-drupal.inc';
+
+  // We pass in the currently logged in user (if set via the --user option),
+  // which will automatically log this user in the browser during the first
+  // request.
+  if (drush_get_option('user', FALSE)) {
+    drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_LOGIN);
+  }
+
   // Determine active configuration.
   $drush_default = array(
     'host' => '127.0.0.1',
@@ -159,89 +138,24 @@ function drush_core_runserver($uri = NULL) {
   }
   drush_set_context('DRUSH_URI', 'http://' . $hostname . ':' . $uri['port']);
 
-
-  // We pass in the currently logged in user (if set via the --user option),
-  // which will automatically log this user in the browser during the first
-  // request.
-  if (drush_get_option('user', FALSE)) {
-    drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_LOGIN);
-    // Add a querystring parameter to indicate that this is the first page request, so that the user can be logged in.
-    if (!empty($uri['path'])) {
-      if (strpos($uri['path'], '?') === FALSE) {
-        $uri['path'] .= '?login';
-      }
-      else {
-        $uri['path'] .= '&login';
-      }
-    }
-  }
-
   // We delete any registered files here, since they are not caught by Ctrl-C.
   _drush_delete_registered_files();
 
-  // We pass in the effective base_url to our auto_prepend_script via the cgi
-  // environment. This allows Drupal to generate working URLs to this http
-  // server, whilst finding the correct multisite from the HTTP_HOST header.
-  $env['RUNSERVER_BASE_URL'] = 'http://' . $addr . ':' . $uri['port'];
-
-  // We pass in an array of $conf overrides using the same approach.
-  // By default we set drupal_http_request_fails to FALSE, as the httpserver
-  // is unable to process simultaneous requests on some systems.
-  // This is available as an option for developers to pass in their own
-  // favorite $conf overrides (e.g. disabling css aggregation).
-  $current_override = drush_get_option_list('variables', array('drupal_http_request_fails' => FALSE));
-  foreach ($current_override as $name => $value) {
-    if (is_numeric($name) && (strpos($value, '=') !== FALSE)) {
-      list($name, $value) = explode('=', $value, 2);
-    }
-    $override[$name] = $value;
-  }
-  $env['RUNSERVER_CONF'] = urlencode(serialize($override));
-
-  // We pass in the specified user ID (if set). This will automatically log
-  // this user in the browser during the first request (but not subsequent
-  // requests, to allow logging out).
-  $user_message = '';
-  if ($user->uid) {
-    $env['RUNSERVER_USER'] = $user->uid;
-    $user_message = ', logged in as ' . $user->name;
-  }
-
-  drush_print(dt('HTTP server listening on !addr, port !port (see http://!hostname:!port!path), serving site !site!user...', array('!addr' => $addr, '!hostname' => $hostname, '!port' => $uri['port'], '!path' => $uri['path'], '!site' => drush_get_context('DRUSH_DRUPAL_SITE', 'default'), '!user' => $user_message)));
-  if (drush_get_option('server', FALSE) == 'builtin') {
-    // Start php 5.4 builtin server.
-    // Store data used by runserver-prepend.php in the shell environment.
-    foreach ($env as $key => $value) {
-      putenv($key . '=' . $value);
-    }
-    if (!empty($uri['path'])) {
-      // Start a browser if desired. Include a 2 second delay to allow the server to come up.
-      drush_start_browser($uri['path'], 2);
-    }
-    // Start the server using 'php -S'.
-    $php = drush_get_option('php');
-    drush_shell_exec_interactive($php . ' -S ' . $addr . ':' . $uri['port'] . ' --define auto_prepend_file="' . DRUSH_BASE_PATH . '/commands/runserver/runserver-prepend.php"');
-  }
-  else {
-    // Include the library and our class that extends it.
-    $lib = drush_get_option('lib', DRUSH_BASE_PATH . '/lib');
-    $httpserverfile = $lib . '/' . DRUSH_HTTPSERVER_DIR_BASE . substr(DRUSH_HTTPSERVER_VERSION, 0, 7) . '/httpserver.php';
-    require_once $httpserverfile;
-    require_once 'runserver-drupal.inc';
-    // Create a new httpserver instance and start it running.
-    $server = new DrupalServer(array(
-      'addr' => $addr,
-      'port' => $uri['port'],
-      'path' => $uri['path'],
-      'hostname' => $hostname,
-      'site' => drush_get_context('DRUSH_DRUPAL_SITE', 'default'),
-      'server_id' => 'Drush runserver',
-      'php_cgi' => drush_get_option('php-cgi', 'php-cgi') . ' --define auto_prepend_file="' . DRUSH_BASE_PATH . '/commands/runserver/runserver-prepend.php"',
-      'env' => $env,
-      'debug' => drush_get_context('DRUSH_DEBUG'),
-    ));
-    $server->run_forever();
-  }
+  // Create a new server instance and start it running.
+  $server = new DrupalServer(array(
+    'addr' => $addr,
+    'port' => $uri['port'],
+    'path' => $uri['path'],
+    'hostname' => $hostname,
+    'site' => drush_get_context('DRUSH_DRUPAL_SITE', 'default'),
+    'server_id' => 'Drush runserver',
+    'php_cgi' => drush_get_option('php-cgi', 'php-cgi') . ' --define auto_prepend_file="' . DRUSH_BASE_PATH . '/commands/runserver/runserver-prepend.php"',
+    'conf_inject' => drush_get_option('conf-inject', array('drupal_http_request_fails' => FALSE)),
+    'user' => $user,
+    'watchdog' => drush_get_option('watchdog', FALSE),
+    'debug' => drush_get_context('DRUSH_DEBUG'),
+  ));
+  $server->run_forever();
 }
 
 /**
@@ -282,4 +196,4 @@ function runserver_parse_uri($uri) {
     unset($uri['host']);
   }
   return $uri;
-}
+}
\ No newline at end of file
index f129f88..b1a4509 100644 (file)
@@ -24,10 +24,7 @@ function sql_drush_command() {
   if (drush_drupal_major_version() >= 7) {
     $options['target'] = 'The name of a target within the specified database.';
   }
-  $db_url['db-url'] = array(
-    'description' => 'A Drupal 6 style database URL. Only required for initial install - not re-install.',
-    'example-value' => 'mysql://root:pass@127.0.0.1/db',
-  );
+  $db_url['db-url'] = 'A Drupal 6 style database URL. Useful when you don\'t have a configured Drupal instance.';
 
   $items['sql-drop'] = array(
     'description' => 'Drop all tables in a given database.',
@@ -36,10 +33,7 @@ function sql_drush_command() {
     'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
     'options' => array(
       'yes' => 'Skip confirmation and proceed.',
-      'result-file' => array(
-        'description' => 'Save to a file. The file should be relative to Drupal root. Recommended.',
-        'example-value' => '/path/to/file',
-      ),
+      'result-file' => 'Save to a file. The file should be relative to Drupal root. Recommended.',
     ) + $options + $db_url,
     'topics' => array('docs-policy'),
   );
@@ -66,21 +60,17 @@ function sql_drush_command() {
     'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
     'examples' => array(
       'drush sql-dump --result-file=../18.sql' => 'Save SQL dump to the directory above Drupal root.',
-      'drush sql-dump --skip-tables-key=common' => 'Skip standard tables. @see example.drushrc.php',
+      'drush sql-dump --skip-tables-key=common' => 'Skip standard tables. @see example.drushrc.com',
     ),
     'options' => array(
-      'result-file' => array(
-        'description' => 'Save to a file. The file should be relative to Drupal root. If --result-file is provided with no value, then date based filename will be created under ~/drush-backups directory.',
-        'example-value' => '/path/to/file',
-        'value' => 'optional',
-      ),
+      'result-file' => 'Save to a file. The file should be relative to Drupal root. If --result-file is provided with no value, then date based filename will be created under ~/drush-backups directory.',
       'skip-tables-key' => 'A key in the $skip_tables array. @see example.drushrc.php. Optional.',
       'structure-tables-key' => 'A key in the $structure_tables array. @see example.drushrc.php. Optional.',
       'tables-key' => 'A key in the $tables array. Optional.',
       'tables-list' => 'A comma-separated list of tables to transfer. Optional.',
       'ordered-dump' => 'Use this option to output ordered INSERT statements in the sql-dump.Useful when backups are managed in a Version Control System. Optional.',
-      'create-db' => array('hidden' => TRUE, 'description' => 'Omit DROP TABLE statements. Postgres and Oracle only.  Used by sql-sync, since including the DROP TABLE statements interfere with the import when the database is created.'),
-      'data-only' => 'Dump data without statements to create any of the schema.',
+      'create-db' => 'Wipe existing tables.',
+      'data-only' => 'Omit CREATE TABLE statements. Postgres only.',
       'ordered-dump' => 'Order by primary key and add line breaks for efficient diff in revision control. Also, faster rsync. Slows down the dump. Mysql only.',
       'gzip' => 'Compress the dump using the gzip program which must be in your $PATH.',
     ) + $options + $db_url,
@@ -92,17 +82,14 @@ function sql_drush_command() {
       'drush sql-query "SELECT * FROM users WHERE uid=1"' => 'Browse user record. Table prefixes, if used, must be added to table names by hand.',
       'drush sql-query --db-prefix "SELECT * FROM {users} WHERE uid=1"' => 'Browse user record. Table prefixes are honored.  Caution: curly-braces will be stripped from all portions of the query.',
       '`drush sql-connect` < example.sql' => 'Import sql statements from a file into the current database.',
-      'drush sql-query --file=example.sql' => 'Alternate way to import sql statements from a file.',
+      'drush sql-query --input-file=example.sql' => 'Alternate way to import sql statements from a file.',
     ),
     'arguments' => array(
        'query' => 'An SQL query. Ignored if \'file\' is provided.',
     ),
     'options' => array(
-      'result-file' => array(
-        'description' => 'Save to a file. The file should be relative to Drupal root. Optional.',
-        'example-value' => '/path/to/file',
-      ),
-      'file' => 'Path to a file containing the SQL to be run.',
+      'result-file' => 'Save to a file. The file should be relative to Drupal root. Optional.',
+      'input-file' => 'Path to a file containing the SQL to be run.',
       'extra' => 'Add custom options to the mysql command.',
       'db-prefix' => 'Enable replacement of braces in your query.',
     ) + $options + $db_url,
@@ -150,11 +137,11 @@ function sql_drush_command() {
     'sub-options' => array(
       'sanitize' => array(
         'sanitize-password' => 'The password to assign to all accounts in the sanitization operation, or "no" to keep passwords unchanged.  Default is "password".',
-        'sanitize-email' => 'The pattern for test email addresses in the sanitization operation, or "no" to keep email addresses unchanged.  May contain replacement patterns %uid, %mail or %name.  Default is "user+%uid@localhost".',
+        'sanitize-email' => 'The username for test email addresses in the sanitization operation, or "no" to keep email addresses unchanged.  May contain replacement patterns %uid, %mail or %name.  Default is "user+%uid@localhost".',
         'confirm-sanitizations' => 'Prompt yes/no after importing the database, but before running the sanitizations',
       ),
     ),
-    'topics' => array('docs-aliases', 'docs-policy', 'docs-example-sync-via-http', 'docs-example-sync-extension'),
+    'topics' => array('docs-aliases', 'docs-policy'),
   );
   if (drush_drupal_major_version() >= 7) {
 
@@ -175,7 +162,7 @@ function sql_drush_command() {
     'hidden' => TRUE,
     'options' => array(
       'sanitize-password' => 'The password to assign to all accounts in the sanitization operation, or "no" to keep passwords unchanged.  Default is "password".',
-      'sanitize-email' => 'The pattern for test email addresses in the sanitization operation, or "no" to keep email addresses unchanged.  May contain replacement patterns %uid, %mail or %name.  Default is "user+%uid@localhost".',
+      'sanitize-email' => 'The username for test email addresses in the sanitization operation, or "no" to keep email addresses unchanged.  May contain replacement patterns %uid, %mail or %name.  Default is "user+%uid@localhost".',
     ) + $db_url,
     'aliases' => array('sqlsan'),
   );
@@ -239,16 +226,6 @@ function _drush_sql_connect($db_spec = NULL) {
     case 'sqlsrv':
       $command = 'sqlcmd';
       break;
-    case 'oracle':
-      // use rlwrap if available for readline support
-      if ($handle = popen('rlwrap -v', 'r')) {
-        $command = 'rlwrap sqlplus';
-        pclose($handle);
-      }
-      else {
-        $command = 'sqlplus';
-      }
-      break;
   }
   $command .= _drush_sql_get_credentials($db_spec);
   return $command;
@@ -268,7 +245,6 @@ function drush_sql_dump_execute() {
   drush_sql_bootstrap_further();
   list($exec, $file) = drush_sql_dump();
   // Avoid the php memory of the $output array in drush_shell_exec().
-
   if (!$return = drush_op_system($exec)) {
     if ($file) {
       drush_log(dt('Database dump saved to !path', array('!path' => $file)), 'success');
@@ -341,7 +317,7 @@ function drush_sql_build_dump_command($table_selection, $db_spec = NULL, $file =
   if ($file) {
     if ($file === TRUE) {
       // User did not pass a specific value for --result-file. Make one.
-      $backup = drush_include_engine('version_control', 'backup');
+      $backup = drush_include_engine('version-control', 'backup');
       $backup_dir = $backup->prepare_backup_dir($db_spec['database']);
       if (empty($backup_dir)) {
         $backup_dir = "/tmp";
@@ -352,7 +328,6 @@ function drush_sql_build_dump_command($table_selection, $db_spec = NULL, $file =
   }
 
   switch (_drush_sql_get_scheme($db_spec)) {
-    case 'mysqli':
     case 'mysql':
       $exec = 'mysqldump';
       if ($file) {
@@ -441,42 +416,15 @@ function drush_sql_build_dump_command($table_selection, $db_spec = NULL, $file =
       }
       break;
     case 'sqlsrv':
-      // Change variable '$file' by reference in order to get drush_log() to report.
-      if (!$file) {
-        $file = $db_spec['database'] . '_' . date('Ymd_His') . '.bak';
-      }
-      $exec =  "sqlcmd -U \"" . $db_spec['username'] . "\" -P \"" . $db_spec['password'] . "\" -S \"" . $db_spec['host'] . "\" -Q \"BACKUP DATABASE [" . $db_spec['database'] . "] TO DISK='" . $file . "'\"";
-      break;
-    case 'oracle':
-      $create_db = drush_get_option('create-db');
-      $exec = 'exp ' . _drush_sql_get_credentials($db_spec);
-      // Change variable '$file' by reference in order to get drush_log() to report.
-      if (!$file) {
-        $file = $db_spec['username'] . '.dmp';
-      }
-      $exec .= ' file=' . $file;
-
-      if (!empty($tables))
-        $exec .= ' tables="(' . implode(',', $tables) . ')"';
-      else
-        $exec .= ' owner=' . $db_spec['username'];
-
+      $exec = "echo 'sqlsrv sql-dump not yet supported'";
       break;
   }
 
   if (drush_get_option('gzip')) {
     if ($file) {
-      $escfile = drush_escapeshellarg($file);
-      if (drush_get_context('DRUSH_AFFIRMATIVE')) {
-        // Gzip the result-file without Gzip confirmation
-        $exec .= " && gzip -f $escfile";
-        $file .= '.gz';
-      }
-      else {
-        // Gzip the result-file
-        $exec .= " && gzip $escfile";
-        $file .= '.gz';
-      }
+      // Gzip the result-file
+      $exec .= "; gzip $file";
+      $file .= '.gz';
     }
     else {
       // gzip via pipe since user has not specified a file.
@@ -530,13 +478,9 @@ function _drush_sql_get_table_list($option_name) {
  * Command callback. Executes the given SQL query on the Drupal database.
  */
 
-function drush_sql_query($query = NULL) {
+function drush_sql_query($query) {
   drush_sql_bootstrap_further();
   $filename = drush_get_option('file', NULL);
-  // Enable prefix processing when db-prefix option is used.
-  if (drush_get_option('db-prefix')) {
-    drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_DATABASE);
-  }
   return _drush_sql_query($query, NULL, $filename);
 }
 
@@ -551,7 +495,6 @@ function drush_sql_query($query = NULL) {
  *   A path to a file containing the SQL to be executed.
  */
 function _drush_sql_query($query, $db_spec = NULL, $filename = NULL) {
-  $suffix = '';
   $scheme = _drush_sql_get_scheme($db_spec);
 
   // Inject table prefixes as needed.
@@ -570,12 +513,6 @@ function _drush_sql_query($query, $db_spec = NULL, $filename = NULL) {
     }
   }
 
-  // is this an oracle query
-  if ($scheme == 'oracle') {
-    $query = drush_sql_format_oracle($query);
-    $suffix = '.sql';
-  }
-
   // Convert mysql 'show tables;' query into something pgsql understands
   if (($scheme == 'pgsql') && ($query == 'show tables;')) {
     $query = drush_sql_show_tables_pgsql();
@@ -583,7 +520,7 @@ function _drush_sql_query($query, $db_spec = NULL, $filename = NULL) {
 
   // Save $query to a tmp file if needed. We will redirect it in.
   if (!$filename) {
-    $filename = drush_save_data_to_temp_file($query, $suffix);
+    $filename = drush_save_data_to_temp_file($query);
   }
   $exec = drush_sql_build_exec($db_spec, $filename);
   if ($output_file = drush_get_option('result-file')) {
@@ -595,12 +532,7 @@ function _drush_sql_query($query, $db_spec = NULL, $filename = NULL) {
   if (drush_get_context('DRUSH_SIMULATE')) {
     drush_print('sql-query: ' . $query);
   }
-  if ($exec) {
-    $return = drush_op_system($exec) == 0;
-  }
-  else {
-    $return = drush_set_error('DRUSH_SQL_NO_QUERY', 'No query provided');
-  }
+  $return = drush_op_system($exec) == 0;
   return $return;
 }
 
@@ -619,7 +551,6 @@ function drush_sql_drop() {
 function _drush_sql_drop($db_spec = NULL) {
   // TODO: integrate with _drush_sql_get_table_list?
 
-  $suffix = '';
   $scheme = _drush_sql_get_scheme($db_spec);
   switch ($scheme) {
     case 'pgsql':
@@ -631,15 +562,11 @@ function _drush_sql_drop($db_spec = NULL) {
     case 'sqlsrv':
       $query = 'SELECT TABLE_NAME FROM information_schema.tables';
       break;
-    case 'oracle':
-      $query = "SELECT TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME NOT IN ('BLOBS','LONG_IDENTIFIERS')";
-      $suffix = '.sql';
-      break;
     default:
       $query = 'SHOW TABLES;';
   }
 
-  $filename = drush_save_data_to_temp_file($query, $suffix);
+  $filename = drush_save_data_to_temp_file($query);
   $exec = drush_sql_build_exec($db_spec, $filename);
 
   // Actually run this prep query no matter if in SIMULATE.
@@ -757,7 +684,7 @@ function _drush_sql_get_db_spec() {
   elseif (($databases = drush_get_option('databases')) && (array_key_exists($database, $databases)) && (array_key_exists($target, $databases[$database]))) {
     return $databases[$database][$target];
   }
-  elseif (drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION)) {
+  elseif (drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION)) {
     switch (drush_drupal_major_version()) {
       case 6:
         if ($url = isset($GLOBALS['db_url']) ? $GLOBALS['db_url'] : drush_get_option('db-url', NULL)) {
@@ -849,7 +776,7 @@ function drush_sql_dump_file(&$site_record) {
     if (!isset($dump_dir)) {
       // If this is a remote site, try to find a writable tmpdir.
       if (isset($site_record['remote-host'])) {
-        $result = drush_invoke_process($site_record, 'ev', array('drush_print(drush_find_tmp())'), array(), array('integrate' => FALSE));
+        $result = drush_invoke_process($site_record, 'ev', array('drush_print(drush_find_tmp())'));
         // If the call to invoke process does not work for some reason (e.g. drush not
         // installed on the target machine), then we will just presume that the tmp dir is '/tmp'.
         if (!array_key_exists('output', $result) || empty($result['output'])) {
@@ -953,13 +880,6 @@ function _drush_sql_get_credentials($db_spec = NULL) {
       $host = empty($db_spec['server']) ? '.\SQLEXPRESS' : $db_spec['server'];
       return ' -S ' . $host . ' -d ' . $database . ' -U ' . $db_spec['username'] . ' -P ' . $db_spec['password'];
       break;
-
-
-    case 'oracle':
-      // Return an Oracle connection string
-      return ' ' . $db_spec['username'] .'/' . $db_spec['password'] . ($db_spec['host']=='USETNS' ? '@' . $db_spec['database'] : '@//' . $db_spec['host'] . ':' . ($db_spec['port'] ? $db_spec['port'] : '1521') . '/' . $db_spec['database']);
-      break;
-
   }
 
   // Turn each parameter into a valid parameter string.
@@ -1045,38 +965,6 @@ function drush_sql_show_tables_pgsql() {
   return "select tablename from pg_tables where schemaname='public';";
 }
 
-// Format queries to work with Oracle and SqlPlus
-function drush_sql_format_oracle($query) {
-
-  // remove trailing semicolon from query if we have it
-  $query = preg_replace('/\;$/', '', $query);
-
-  // some sqlplus settings
-  $settings[] = "set TRIM ON";
-  $settings[] = "set FEEDBACK OFF";
-  $settings[] = "set UNDERLINE OFF";
-  $settings[] = "set PAGES 0";
-  $settings[] = "set PAGESIZE 50000";
-
-  // are we doing a describe ?
-  if (!preg_match('/^ *desc/i', $query)) {
-    $settings[] = "set LINESIZE 32767";
-  }
-
-  // are we doing a show tables ?
-  if (preg_match('/^ *show tables/i', $query)) {
-    $settings[] = "set HEADING OFF";
-    $query = "select object_name from user_objects where object_type='TABLE' order by object_name asc";
-  }
-
-  // create settings string
-  $sqlp_settings = implode("\n", $settings)."\n";
-
-  // important for sqlplus to exit correctly
-  return "${sqlp_settings}${query};\nexit;\n";
-}
-
-
 /*
  * Drop all tables (if DB exists) or CREATE target database.
  *
@@ -1187,7 +1075,7 @@ function drush_sql_db_exists($db_spec) {
     case 'sqlsrv':
       // TODO: untested, but the gist is here.
       $sql = "if db_id('$database') IS NOT NULL print 1";
-      drush_shell_exec("$connect_no_db -Q \"$sql\"");
+      drush_shell_exec("$connect_no_db -q \"$sql\"");
       $output = drush_shell_exec_output();
       return $output[0] == 1;
   }
@@ -1195,26 +1083,25 @@ function drush_sql_db_exists($db_spec) {
 
 function drush_sql_build_exec($db_spec, $filepath) {
   $scheme = _drush_sql_get_scheme($db_spec);
-  $exec = '';
   switch ($scheme) {
     case 'mysql':
       $exec = 'mysql';
       $exec .= _drush_sql_get_credentials($db_spec);
       $exec .= ' ' . drush_get_option('extra');
-      $exec .= " < " . drush_escapeshellarg($filepath);
+      $exec .= " < $filepath";
 
       break;
     case 'pgsql':
       $exec = 'psql -q ';
       $exec .= _drush_sql_get_credentials($db_spec);
       $exec .= ' ' . (drush_get_option('extra') ? drush_get_option('extra') : "--no-align --field-separator='\t' --pset footer=off");
-      $exec .= " --file " . drush_escapeshellarg($filepath);
+      $exec .= " --file $filepath";
       break;
     case 'sqlite':
       $exec = 'sqlite3';
       $exec .= ' ' . drush_get_option('extra');
       $exec .= _drush_sql_get_credentials($db_spec);
-      $exec .= " < " . drush_escapeshellarg($filepath);
+      $exec .= " < $filepath";
       break;
     case 'sqlsrv':
       $exec = 'sqlcmd';
@@ -1222,12 +1109,6 @@ function drush_sql_build_exec($db_spec, $filepath) {
       $exec .= _drush_sql_get_credentials($db_spec);
       $exec .= ' -h -1 -i "' . $filepath . '"';
       break;
-    case 'oracle':
-      $exec = 'sqlplus';
-      $exec .= ' ' . drush_get_option('extra');
-      $exec .= _drush_sql_get_credentials($db_spec);
-      $exec .= " @" . drush_escapeshellarg($filepath);
-      break;
   }
   return $exec;
 }
index 19fe2cb..44ddcd0 100644 (file)
@@ -16,9 +16,6 @@ function drush_sql_sync_init($source = NULL, $destination = NULL) {
   // After preflight, get source and destination settings
   $source_settings = drush_sitealias_get_record($source);
   $destination_settings = drush_sitealias_get_record($destination);
-  // Apply command-specific options.
-  drush_sitealias_command_default_options($source_settings, 'source-');
-  drush_sitealias_command_default_options($destination_settings, 'target-');
 
   // Insure that we have database records for the source and destination
   // alias records.  sitealias_get_databases_from_record will cache the
@@ -116,11 +113,8 @@ function sql_drush_sql_sync_sanitize($site) {
         $email_map = array('%uid' => "', uid, '", '%mail' => "', replace(mail, '@', '_'), '", '%name' => "', replace(name, ' ', '_'), '");
         $newmail =  "concat('" . str_replace(array_keys($email_map), array_values($email_map), $newemail) . "')";
       }
-      $user_table_updates[] = "mail = $newmail";
-    }
-    else {
-      $user_table_updates[] = "mail = '$newemail'";
     }
+    $user_table_updates[] = "mail = $newmail";
     $message_list[] = 'email addresses';
   }
 
@@ -227,8 +221,7 @@ function drush_sql_sync($source = NULL, $destination = NULL) {
     }
     // If the 'cache' option is set, then we will set the no-dump option iff the
     // target file exists and its modification date is less than "cache" hours.
-    // If --no-sync or --no-dump are already set, then this check is unnecessary.
-    if (isset($cache) && !isset($no_sync) && !isset($no_dump)) {
+    if (isset($cache)) {
       if (file_exists($local_file) && (filesize($local_file) > 0)) {
         if ((time() - filemtime($local_file)) < ($cache * 60 * 60)) {
           drush_log(dt('Modification time of local dump file !file is less than !cache hours old. Use the --no-cache option to force a refresh.', array('!file' => $local_file, '!cache' => $cache)), 'warning');
@@ -326,7 +319,7 @@ function drush_sql_sync($source = NULL, $destination = NULL) {
         // from different users sharing the same dump file.
         if (!drush_is_windows($source_os) && (!$source_settings['dump-is-temp'])) {
           $source_intermediate = $source_dump . '-' . date("U");
-          $mv_intermediate = '&& mv -f ' . $source_intermediate . ' ' . $source_dump;
+          $mv_intermediate = '; mv -f ' . $source_intermediate . ' ' . $source_dump;
         }
         list($dump_exec, $dump_file) = drush_sql_build_dump_command($table_selection, $source_db_url, $source_intermediate);
         $dump_exec .= $mv_intermediate;
@@ -354,7 +347,7 @@ function drush_sql_sync($source = NULL, $destination = NULL) {
     if (!isset($no_sync)) {
       // If the source file is a temporary file, then we will have rsync
       // delete it for us (remove-source-files option set above).
-      if (!drush_core_call_rsync($source_remote_user . $source_at . $source_db_url['remote-host'] . ':' . $source_dump, drush_correct_absolute_path_for_exec($local_file, "RSYNC"), $source_rsync_options)) {
+      if (!drush_core_call_rsync($source_remote_user . $source_at . $source_db_url['remote-host'] . ':' . $source_dump, $local_file, $source_rsync_options)) {
         return FALSE;
       }
     }
@@ -413,11 +406,11 @@ function drush_sql_sync($source = NULL, $destination = NULL) {
           $target_remote_pass = array_key_exists('remote-pass', $destination_settings) ? ':' . $destination_settings['remote-pass'] : '';
         }
 
-        if (!drush_core_call_rsync(drush_correct_absolute_path_for_exec($local_file, "RSYNC"), $target_remote_user . $target_at . $target_db_url['remote-host'] . ':' . $target_dump, $target_rsync_options)) {
+        if (!drush_core_call_rsync($local_file, $target_remote_user . $target_at . $target_db_url['remote-host'] . ':' . $target_dump, $target_rsync_options)) {
           return FALSE;
         }
 
-        $import_exec = $pre_import_commands . $import_command . ' < ' . drush_escapeshellarg($target_dump, $target_os);
+        $import_exec = $pre_import_commands . $import_command . ' < ' . $target_dump;
         // Delete the remote target file if it is a temporary file.
         if (!drush_is_windows($target_os) && $destination_settings['dump-is-temp']) {
           $import_exec .= '; rm -f ' . drush_escapeshellarg($target_dump, $target_os);
@@ -425,7 +418,7 @@ function drush_sql_sync($source = NULL, $destination = NULL) {
         // TODO: make sure that the remote tmp file is deleted on remote Windows machines.
       }
       else {
-        $import_exec = $pre_import_commands . $import_command . ' < ' . drush_escapeshellarg($local_file);
+        $import_exec = $pre_import_commands . $import_command . ' < ' . $local_file;
       }
       $import_exec = _drush_backend_generate_command($destination_settings, $import_exec);
 
index 252e160..6686f0c 100644 (file)
@@ -151,11 +151,7 @@ function user_drush_command() {
     ),
     'required-arguments' => TRUE,
     'options' => array(
-      'password' => array(
-        'description' => 'The new password for the account.',
-        'required' => TRUE,
-        'example-value' => 'foo',
-      ),
+      'password' => array('description' => 'The new password for the account.'),
     ),
     'examples' => array(
       'drush user-password someuser --password="correct horse battery staple"' =>
@@ -186,13 +182,71 @@ function user_drush_command() {
   return $items;
 }
 
+// Implementation of hook_drush_init().
+function user_drush_init() {
+  $command_info = drush_get_command();
+  $command = $command_info['command'];
+  $needs_parse_args = array('user-block', 'user-unblock', 'user-add-role', 'user-remove-role');
+  if (in_array($command, $needs_parse_args)) {
+    // parse args and call drush_set_option for --uids
+    $users = array();
+    foreach (array('uid', 'name', 'mail' ) as $user_attr) {
+      if ($arg = drush_get_option($user_attr)) {
+        foreach(explode(',', $arg) as $search) {
+          $uid_query = FALSE;
+          switch ($user_attr) {
+            case 'uid':
+              if (drush_drupal_major_version() >= 7) {
+                $uid_query = db_query("SELECT uid FROM {users} WHERE uid = :uid", array(':uid' => $search));
+              }
+              else {
+                $uid_query = db_query("SELECT uid FROM {users} WHERE uid = %d", $search);
+              }
+              break;
+            case 'name':
+              if (drush_drupal_major_version() >= 7) {
+                $uid_query = db_query("SELECT uid FROM {users} WHERE name = :name", array(':name' => $search));
+              }
+              else {
+                $uid_query = db_query("SELECT uid FROM {users} WHERE name = '%s'", $search);
+              }
+              break;
+            case 'mail':
+              if (drush_drupal_major_version() >= 7) {
+                $uid_query = db_query("SELECT uid FROM {users} WHERE mail = :mail", array(':mail' => $search));
+              }
+              else {
+                $uid_query = db_query("SELECT uid FROM {users} WHERE mail = '%s'", $search);
+              }
+              break;
+          }
+          if ($uid_query !== FALSE) {
+            if ($uid = drush_db_result($uid_query)) {
+              $users[] = $uid;
+            }
+            else {
+              drush_set_error("Could not find a uid for $user_attr = $search");
+            }
+          }
+        }
+      }
+    }
+    if (!empty($users)) {
+      drush_set_option('uids', $users);
+    }
+  }
+}
+
 /**
  * Prints information about the specified user(s).
  */
 function drush_user_information($users) {
-  $uids = _drush_user_get_users_from_arguments($users);
-  foreach($uids as $uid) {
-    _drush_user_print_info($uid);
+  $users = explode(',', $users);
+  foreach($users as $user) {
+    $uid = _drush_user_get_uid($user);
+    if ($uid !== FALSE) {
+        _drush_user_print_info($uid);
+    }
   }
 }
 
@@ -200,7 +254,16 @@ function drush_user_information($users) {
  * Block the specified user(s).
  */
 function drush_user_block($users = '') {
-  $uids = _drush_user_get_users_from_options_and_arguments($users);
+  $uids = drush_get_option('uids');
+  if ($users !== '') {
+    $users = explode(',', $users);
+    foreach($users as $user) {
+      $uid = _drush_user_get_uid($user);
+      if ($uid !== FALSE) {
+        $uids[] = $uid;
+      }
+    }
+  }
   if (!empty($uids)) {
     drush_op('user_user_operations_block', $uids);
   }
@@ -213,7 +276,16 @@ function drush_user_block($users = '') {
  * Unblock the specified user(s).
  */
 function drush_user_unblock($users = '') {
-  $uids = _drush_user_get_users_from_options_and_arguments($users);
+  $uids = drush_get_option('uids');
+  if ($users !== '') {
+    $users = explode(',', $users);
+    foreach($users as $user) {
+      $uid = _drush_user_get_uid($user);
+      if ($uid !== FALSE) {
+        $uids[] = $uid;
+      }
+    }
+  }
   if (!empty($uids)) {
     drush_op('user_user_operations_unblock', $uids);
   }
@@ -226,7 +298,16 @@ function drush_user_unblock($users = '') {
  * Add a role to the specified user accounts.
  */
 function drush_user_add_role($role, $users = '') {
-  $uids = _drush_user_get_users_from_options_and_arguments($users);
+  $uids = drush_get_option('uids');
+  if ($users !== '') {
+    $users = explode(',', $users);
+    foreach($users as $user) {
+      $uid = _drush_user_get_uid($user);
+      if ($uid !== FALSE) {
+        $uids[] = $uid;
+      }
+    }
+  }
   if (drush_drupal_major_version() >= 7) {
     $rid_query = db_query("SELECT rid FROM {role} WHERE name = :role", array(':role' => $role));
   }
@@ -237,11 +318,11 @@ function drush_user_add_role($role, $users = '') {
     if ($rid = drush_db_result($rid_query)) {
       drush_op('user_multiple_role_edit', $uids, 'add_role', $rid);
       foreach($uids as $uid) {
-        drush_log(dt("Added the !role role to uid !uid", array('!role' => $role, '!uid' => $uid)), 'success');
+        drush_log(dt("Added the %role role to uid %uid", array('%role' => $role, '%uid' => $uid)), 'success');
       }
     }
     else {
-      return drush_set_error(dt("There is no role named: !role", array('!role' => $role)));
+      return drush_set_error("There is no role named: \"$role\"!");
     }
   }
   else {
@@ -253,7 +334,16 @@ function drush_user_add_role($role, $users = '') {
  * Remove a role from the specified user accounts.
  */
 function drush_user_remove_role($role, $users = '') {
-  $uids = _drush_user_get_users_from_options_and_arguments($users);
+  $uids = drush_get_option('uids');
+  if ($users !== '') {
+    $users = explode(',', $users);
+    foreach($users as $user) {
+      $uid = _drush_user_get_uid($user);
+      if ($uid !== FALSE) {
+        $uids[] = $uid;
+      }
+    }
+  }
   if (drush_drupal_major_version() >= 7) {
     $rid_query = db_query("SELECT rid FROM {role} WHERE name = :role", array(':role' => $role));
   }
@@ -264,11 +354,11 @@ function drush_user_remove_role($role, $users = '') {
     if ($rid = drush_db_result($rid_query)) {
       drush_op('user_multiple_role_edit', $uids, 'remove_role', $rid);
       foreach($uids as $uid) {
-        drush_log(dt("Removed the !role role from uid !uid", array('!role' => $role, '!uid' => $uid)), 'success');
+        drush_log(dt("Removed the %role role from uid %uid", array('%role' => $role, '%uid' => $uid)), 'success');
       }
     }
     else {
-      return drush_set_error(dt("There is no role named: !role", array('!role' => $role)));
+      return drush_set_error("There is no role named: \"$role\"!");
     }
   }
   else {
@@ -454,75 +544,6 @@ function _drush_user_print_info($uid) {
 }
 
 /**
- * Given a comma-separated list of users, return uids
- * for users that match either by uid or email address.
- */
-function _drush_user_get_users_from_arguments($users) {
-  $uids = array();
-  if ($users !== '') {
-    $users = explode(',', $users);
-    foreach($users as $user) {
-      $uid = _drush_user_get_uid($user);
-      if ($uid !== FALSE) {
-        $uids[] = $uid;
-      }
-    }
-  }
-  return $uids;
-}
-
-/**
- * Return the list of matching uids given
- */
-function _drush_user_get_users_from_options_and_arguments($users) {
-  $uids = drush_get_option_list('uids');
-
-  foreach (array('uid', 'name', 'mail' ) as $user_attr) {
-    if ($arg = drush_get_option($user_attr)) {
-      foreach(explode(',', $arg) as $search) {
-        $uid_query = FALSE;
-        switch ($user_attr) {
-          case 'uid':
-            if (drush_drupal_major_version() >= 7) {
-              $uid_query = db_query("SELECT uid FROM {users} WHERE uid = :uid", array(':uid' => $search));
-            }
-            else {
-              $uid_query = db_query("SELECT uid FROM {users} WHERE uid = %d", $search);
-            }
-            break;
-          case 'name':
-            if (drush_drupal_major_version() >= 7) {
-              $uid_query = db_query("SELECT uid FROM {users} WHERE name = :name", array(':name' => $search));
-            }
-            else {
-              $uid_query = db_query("SELECT uid FROM {users} WHERE name = '%s'", $search);
-            }
-            break;
-          case 'mail':
-            if (drush_drupal_major_version() >= 7) {
-              $uid_query = db_query("SELECT uid FROM {users} WHERE mail = :mail", array(':mail' => $search));
-            }
-            else {
-              $uid_query = db_query("SELECT uid FROM {users} WHERE mail = '%s'", $search);
-            }
-            break;
-        }
-        if ($uid_query !== FALSE) {
-          if ($uid = drush_db_result($uid_query)) {
-            $uids[] = $uid;
-          }
-          else {
-            drush_set_error("Could not find a uid for $user_attr = $search");
-          }
-        }
-      }
-    }
-  }
-
-  return array_merge($uids, _drush_user_get_users_from_arguments($users));
-}
-
-/**
  * Get uid(s) from a uid, user name, or email address.
  * Returns a uid, or FALSE if none found.
  */
index f35fba3..1811b8b 100644 (file)
@@ -5,6 +5,8 @@ four simple steps.
 <ol>
 <li>Create a command file called COMMANDFILE.drush.inc
 
+<li>Implement the function COMMANDFILE_drush_help(). Optional.
+
 <li>Implement the function COMMANDFILE_drush_command()
 
 <li>Implement the functions that your commands will call.
@@ -37,9 +39,7 @@ Drush searches for commandfiles in the following locations:
 
 <li>The ".drush" folder in the user's HOME folder.
 
-<li>sites/all/drush in the current Drupal installation
-
-<li>All enabled modules in the current Drupal installation
+<li>All modules in the current Drupal installation
 
 <li>Folders and files containing other versions of drush in their names will
 be *skipped* (e.g. devel.drush4.inc or drush4/devel.drush.inc).  Names
@@ -50,7 +50,7 @@ be considered if drush has bootstrapped to at least the DRUSH_BOOSTRAP_SITE
 level.  Usually, when working with a Drupal site, drush will
 bootstrap to DRUSH_BOOTSTRAP_FULL; in this case, only the drush
 commandfiles in enabled modules will be considered eligible for
-loading.  If drush only bootstraps to DRUSH_BOOTSTRAP_SITE,
+loading.  If a drush only bootstraps to DRUSH_BOOTSTRAP_SITE,
 though, then all drush commandfiles will be considered, whether the
 module is enabled or not.  See `drush topic docs-bootstrap` for
 more information on bootstrapping.
@@ -59,6 +59,25 @@ Additionally, drush commandfiles may optionally define a function
 COMMANDFILE_drush_load() in the file COMMANDFILE.drush.load.inc.
 If this function returns FALSE, then the commandfile will not be loaded.
 
+<h2>Implement COMMANDFILE_drush_help()</h2>
+<p>
+The drush_help hook is an optional place to describe a command in long form. If
+the command only requires a brief description, use the description key in
+COMMANDFILE_drush_command(). The drush_help hook for the 'sandwich' commandfile looks
+like this:
+<pre>
+        function sandwich_drush_help($section) {
+          switch ($section) {
+            case 'drush:make-me-a-sandwich':
+              return dt("... brief help summary goes here ...");
+          }
+        }
+</pre><p>
+Note that the command name is prepended with 'drush:' in
+the drush_help hook.  Your commandfile may implement
+multiple commands; to do so, just add more 'case' statements
+to the switch, one for each command.
+
 <h2>Implement COMMANDFILE_drush_command()</h2>
 <p>
 The drush_command hook is the most important part of the
@@ -128,9 +147,7 @@ are shown below.
 
      <ul>
           <li>'description': A description of the option.
-          <li>'example_value': An example value to show in help.
-          <li>'value': optional|required.
-          <li>'required': This option must be passed.
+          <li>'required': The option must be specified, or an error is returned.
           <li>'hidden': The option is not shown in the help output (rare).
      </ul>
 
@@ -191,7 +208,7 @@ are shown below.
  <li>'topics':
 
      Provides a list of topic commands that are related in
-     some way to this command.  Used by `drush help`.
+     some way to this command.  Used by `drush help` only.
 
 <li>'topic':
 
index c37789e..50e5c96 100644 (file)
  * order, for the command "COMMAND":
  *
  * 0. drush_COMMAND_init()
- * 1. drush_hook_COMMAND_pre_validate()
- * 2. drush_hook_COMMAND_validate()
- * 3. drush_hook_pre_COMMAND()
- * 4. drush_hook_COMMAND()
- * 5. drush_hook_post_COMMAND()
+ * 1. drush_hook_COMMAND_validate()
+ * 2. drush_hook_pre_COMMAND()
+ * 3. drush_hook_COMMAND()
+ * 4. drush_hook_post_COMMAND()
  *
  * For example, here are the hook opportunities for a mysite.drush.inc file
  * that wants to hook into the `pm-download` command.
  *
- * 1. drush_mysite_pm_download_pre_validate()
- * 2. drush_mysite_pm_download_validate()
- * 3. drush_mysite_pre_pm_download()
- * 4. drush_mysite_pm_download()
- * 5. drush_mysite_post_pm_download()
+ * 1. drush_mysite_pm_download_validate()
+ * 2. drush_mysite_pre_pm_download()
+ * 3. drush_mysite_pm_download()
+ * 4. drush_mysite_post_pm_download()
  *
  * Note that the drush_COMMAND_init() hook is only for use by the
  * commandfile that defines the command.
@@ -50,7 +48,6 @@
  * 2. drush_mysite_pm_download_rollback()
  * 3. drush_mysite_pre_pm_download_rollback()
  * 4. drush_mysite_pm_download_validate_rollback()
- * 5. drush_mysite_pm_download_pre_validate_rollback()
  *
  * Before any command is called, hook_drush_init() is also called.
  * hook_drush_exit() is called at the very end of command invocation.
@@ -59,7 +56,6 @@
  *
  * @see hook_drush_init()
  * @see drush_COMMAND_init()
- * @see drush_hook_COMMAND_pre_validate()
  * @see drush_hook_COMMAND_validate()
  * @see drush_hook_pre_COMMAND()
  * @see drush_hook_COMMAND()
@@ -68,7 +64,6 @@
  * @see drush_hook_COMMAND_rollback()
  * @see drush_hook_pre_COMMAND_rollback()
  * @see drush_hook_COMMAND_validate_rollback()
- * @see drush_hook_COMMAND_pre_validate_rollback()
  * @see hook_drush_exit()
  */
 
@@ -101,18 +96,6 @@ function drush_COMMAND_init() {
 }
 
 /**
- * Run before a specific command validates.
- *
- * Logging an error stops command execution, and the rollback function (if any)
- * for each hook implementation is invoked.
- *
- * @see drush_hook_COMMAND_pre_validate_rollback()
- */
-function drush_hook_COMMAND_pre_validate() {
-
-}
-
-/**
  * Run before a specific command executes.
  *
  * Logging an error stops command execution, and the rollback function (if any)
@@ -217,29 +200,14 @@ function hook_drush_pm_download_destination_alter(&$project, $release) {
 }
 
 /**
- * Automatically download project dependencies at pm-enable time.
- * Use a pre-pm_enable hook to download before your module is enabled,
- * or a post-pm_enable hook (drush_hook_post_pm_enable) to run after
- * your module is enabled.
- *
- * Your hook will be called every time pm-enable is executed; you should
- * only download dependencies when your module is being enabled.  Respect
- * the --skip flag, and take no action if it is present.
+ * Add information to the upgrade project map; this information
+ * will be shown to the user when upgrading Drupal to the next
+ * major version if the module containing this hook is enabled.
+ *
+ * @see drush_upgrade_project_map().
  */
-function drush_hook_pre_pm_enable() {
-  // Get the list of modules being enabled; only download dependencies if our module name appears in the list
-  $modules = drush_get_context('PM_ENABLE_MODULES');
-  if (in_array('hook', $modules) && !drush_get_option('skip')) {
-    $url = 'http://server.com/path/MyLibraryName.tgz';
-    $path = drush_get_context('DRUSH_DRUPAL_ROOT');
-    if (module_exists('libraries')) {
-      $path .= '/' . libraries_get_path('MyLibraryName') . '/MyLibraryName.tgz';
-    }
-    else {
-      $path .= '/'. drupal_get_path('module', 'hook') . '/MyLibraryName.tgz';
-    }
-    drush_download_file($url, $path) && drush_tarball_extract($path);
-  }
+function hook_drush_upgrade_project_map_alter(&$project_map) {
+  $project_map['warning']['hook'] = dt("You need to take special action before upgrading this module. See http://mysite.com/mypage for more information.");
 }
 
 /**
@@ -256,6 +224,19 @@ function hook_drush_sql_sync_sanitize($source) {
 }
 
 /**
+ * Take action before modules are disabled in a major upgrade.
+ * Note that when this hook fires, it will be operating on a
+ * copy of the database.
+ */
+function drush_hook_pre_site_upgrade_prepare() {
+  // site upgrade prepare will disable contrib_extensions and
+  // uninstall the uninstall_extension
+  $contrib_extensions = func_get_args();
+  $uninstall_extensions = explode(',', drush_get_option('uninstall', ''));
+}
+
+
+/**
  * Add help components to a command
  */
 function hook_drush_help_alter(&$command) {
diff --git a/docs/strict-options.html b/docs/strict-options.html
deleted file mode 100644 (file)
index 2283ecf..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-<h1>Strict Option Handling</h1>
-<p>
-Some Drush commands use strict option handling; these commands require
-that all Drush global option appear on the command line before the Drush
-command name.
-<p>
-One example of this is the core-rsync command:
-<p>
-<pre>
-  drush --simulate core-rsync -v @site1 @site2
-</pre>
-
-The --simulate option is a Drush global option that causes Drush to
-print out what it would do if the command is executed, without actually
-taking any action.  Commands such as core-rsync that use strict option
-handling require that --simulate, if used, must appear before the command
-name.  Most Drush commands allow the --simulate to be placed anywhere,
-such as at the end of the command line.
-<p>
-The -v option above is an rsync option.  In this usage, it will cause
-the rsync command to run in verbose mode.  It will not cause Drush to
-run in verbose mode, though, because it appears after the core-rsync
-command name.  Most Drush commands would be run in verbose mode if a
--v option appeared in the same location.
-<p>
-The advantage of strict option handling is that it allows Drush to
-pass options and arguments through to a shell command.  Some shell commands,
-such as rsync and ssh, either have options that cannot be represented
-in Drush.  For example, rsync allows the --exclude option to appear multiple
-times on the command line, but Drush only allows one instance of an
-option at a time for most Drush commands.  Strict option handling overcomes
-this limitation, plus possible conflict between Drush options and 
-shell command options with the same name, at the cost of greater restriction
-on where global options can be placed.
-
index 57052ca..34c1947 100644 (file)
--- a/drush.bat
+++ b/drush.bat
@@ -13,4 +13,4 @@ rem set HOME=H:/drush
 rem set TEMP=H:/drush
 
 REM See http://drupal.org/node/506448 for more information.
-@php.exe "%~dp0drush.php" --php="php.exe" %*
+@php.exe "%~dp0drush.php" %* --php="php.exe"
index 2c814d6..295a06f 100644 (file)
@@ -1 +1 @@
-drush_version=6.0-dev
+drush_version=5.0-dev
index 8895222..6013d3d 100755 (executable)
--- a/drush.php
+++ b/drush.php
@@ -108,15 +108,7 @@ function _drush_bootstrap_and_dispatch() {
   }
 
   if (!$command_found) {
-    // If we reach this point, command doesn't fit requirements or we have not
-    // found either a valid or matching command.
-
-    // If no command was found check if it belongs to a disabled module.
-    if (!$command) {
-      $command = drush_command_belongs_to_disabled_module();
-    }
-
-    // Set errors related to this command.
+    // If we reach this point, we have not found either a valid or matching command.
     $args = implode(' ', drush_get_arguments());
     if (isset($command) && is_array($command)) {
       foreach ($command['bootstrap_errors'] as $key => $error) {
@@ -135,37 +127,3 @@ function _drush_bootstrap_and_dispatch() {
   }
   return $return;
 }
-
-/**
- * Check if the given command belongs to a disabled module
- *
- * @return
- *   Array with a command-like bootstrap error or FALSE if Drupal was not
- * bootstrapped fully or the command does not belong to a diabled module.
- */
-function drush_command_belongs_to_disabled_module() {
-  if (drush_has_boostrapped(DRUSH_BOOTSTRAP_DRUPAL_FULL)) {
-    _drush_find_commandfiles(DRUSH_BOOTSTRAP_DRUPAL_SITE, DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION);
-    $commands = drush_get_commands();
-    $command_name = array_shift(drush_get_arguments());
-    if (isset($commands[$command_name])) {
-      // We found it. Load its module name and set an error.
-      if (is_array($commands[$command_name]['drupal dependencies']) && count($commands[$command_name]['drupal dependencies'])) {
-        $modules = implode(', ', $commands[$command_name]['drupal dependencies']);
-      } else {
-        // The command does not define Drupal dependencies. Derive them.
-        $command_files = drush_get_context('DRUSH_COMMAND_FILES', array());
-        $command_path = $commands[$command_name]['path'] . DIRECTORY_SEPARATOR . $commands[$command_name]['commandfile'] . '.drush.inc';
-        $modules = array_search($command_path, $command_files);
-      }
-      return array(
-        'bootstrap_errors' => array(
-          'DRUSH_COMMAND_DEPENDENCY_ERROR' =>
-        dt('Command !command needs the following module(s) enabled to run: !dependencies.', array('!command' => $command_name, '!dependencies' => $modules)),
-        )
-      );
-    }
-  }
-
-  return FALSE;
-}
index 35ac9cd..df9e1c3 100644 (file)
  *      on the command line.
  *   2. In one of the default locations:
  *        a. /etc/drush
- *        b. $HOME/.drush
- *        c. The sites/all/drush folder for the current Drupal site
+ *        b. In the drush installation folder
+ *        c. Inside the 'aliases' folder in the drush installation folder
+ *        d. $HOME/.drush
+ *        e. The sites/all/drush folder for the current Drupal site
  *   3. Inside the sites folder of any bootstrapped Drupal site,
  *      or any local Drupal site indicated by an alias used as
  *      a parameter to a command
  *     is not set, and 'Linux' (or $options['remote-os']) if it is.
  * - 'ssh-options': If the target requires special options, such as a non-
  *     standard port, alternative identity file, or alternative
- *     authentication method, ssh-options can contain a string of extra
+ *     authentication method, ssh- options can contain a string of extra
  *     options that are used with the ssh command, eg "-p 100"
  * - 'parent': The name of a parent alias (e.g. '@server') to use as a basis
  *     for this alias.  Any value of the parent will appear in the child
index e165410..ccd9be1 100644 (file)
@@ -31,7 +31,6 @@
 # Aliases for drush commands that work on the current drupal site:
 #
 #       cc               - drush cache-clear
-#       cca              - drush cache-clear all
 #       dis              - drush pm-disable
 #       en               - drush pm-enable
 #       i                - drush pm-info
@@ -40,7 +39,7 @@
 #       unin             - drush pm-uninstall
 #       up               - drush pm-update
 #       upc              - drush pm-updatecode
-#       updb             - drush updatedb
+#       updb             - drush uptatedb
 #       q                - drush sql-query
 #
 # Provides several common shell commands to work better with drush:
@@ -88,7 +87,6 @@ alias use='drush site-set'
 
 # Aliases for drush commands that work on the current drupal site
 alias cc='drush cache-clear'
-alias cca='drush cache-clear all'
 alias dis='drush pm-disable'
 alias en='drush pm-enable'
 alias pmi='drush pm-info'
@@ -97,7 +95,7 @@ alias rf='drush pm-refresh'
 alias unin='drush pm-uninstall'
 alias up='drush pm-update'
 alias upc='drush pm-updatecode'
-alias updb='drush updatedb'
+alias updb='drush uptatedb'
 alias q='drush sql-query'
 
 # Overrides for standard shell commands. Uncomment to enable.  Alias
@@ -259,7 +257,7 @@ function lsd() {
   fi
 }
 
-# Copy files from or to @site or @site:%files, etc; local sites only.
+# Copy from or two @site or @site:%files, etc; local sites only.
 function cpd() {
   p=()
   for a in "$@" ; do
index 3005d7a..2837354 100644 (file)
 # $options['shell-aliases']['unsuck'] = 'pm-disable -y overlay,dashboard';
 # $options['shell-aliases']['offline'] = 'variable-set -y --always-set maintenance_mode 1';
 # $options['shell-aliases']['online'] = 'variable-delete -y --exact maintenance_mode';
-# $options['shell-aliases']['dis-all'] = '!drush -y dis $(drush pml --status=enabled --type=module --no-core --pipe)';
 
-// You can create a local cache of all projects checked out using
-// --package-handler=git_drupalorg; this can be faster for repeated
-// downloads, but can be dangerous. See: http://randyfay.com/node/119
-# $options['cache'] = TRUE;
+// Add a 'pm-clone' to simplify (cached) git cloning from drupal.org.
+# $options['shell-aliases']['pm-clone'] = 'pm-download --gitusername=YOURUSERNAME --package-handler=git_drupalorg --cache';
 
 /**
  * Historical (deprecated) aliases:
 # $options['no-label'] = TRUE;
 
 /**
- * An explicit list of tables which should be included in sql-dump and sql-sync.
- */
-# $options['tables']['common'] = array('user', 'permissions', 'role_permission', 'role');
-
-/**
  * List of tables whose *data* is skipped by the 'sql-dump' and 'sql-sync'
  * commands when the "--structure-tables-key=common" option is provided.
  * You may add specific tables to the existing array or add a new element.
  * git repository.  Example script below by grayside.  Customize as desired.
  * @see: http://grayside.org/node/93.
  */
-#exec('git rev-parse --show-toplevel 2> /dev/null', $output);
+#exec('git rev-parse --git-dir 2> /dev/null', $output);
 #if (!empty($output)) {
 #  $repo = $output[0];
-#  $options['config'] = $repo . '/drush/drushrc.php';
-#  $options['include'] = $repo . '/drush/commands';
-#  $options['alias-path'] = $repo . '/drush/aliases';
+#  $options['config'] = $repo . '/../drush/drushrc.php';
+#  $options['include'] = $repo . '/../drush/commands';
+#  $options['alias-path'] = $repo . '/../drush/aliases';
 #}
index c13f507..f74922f 100644 (file)
@@ -28,16 +28,15 @@ function drush_policy_sql_sync_validate($source = NULL, $destination = NULL) {
 }
 
 /**
- * Implement hook help alter.
- *
- * When a hook extends a command with additional options, it must
- * implement help alter and declare the option(s).  Doing so will add
- * the option to the help text for the modified command, and will also
- * allow the new option to be specified on the command line.  Without
- * this, Drush will fail with an error when a user attempts to use
- * the option.
+ * Implementation of drush_hook_help_alter
  */
-function policy_drush_help_alter($command) {
+function drush_policy_help_alter($command) {
+  // If a command hook (e.g. drush_hook_COMMAND_validate, below)
+  // calls drush_get_option('abc'), then it is required to add
+  // the new option to the 'global-options' command (or, alternatively,
+  // to the single command hooked).  Otherwise, the user will get
+  // an "unknown option" error when they try to specify the option on the
+  // commandline.
   if ($command['command'] == 'updatedb') {
     $command['options']['token'] = 'Per site policy, you must specify a token in the --token option for all commands.';
   }
index f2ad74f..eb058f9 100644 (file)
@@ -42,10 +42,7 @@ function sandwich_drush_command() {
       'filling' => 'The type of the sandwich (turkey, cheese, etc.). Defaults to ascii.',
     ),
     'options' => array(
-      'spreads' => array(
-        'description' => 'Comma delimited list of spreads.',
-        'example-value' => 'mayonnaise,mustard',
-      ),
+      'spreads' => 'Comma delimited list of spreads (e.g. mayonnaise, mustard)',
     ),
     'examples' => array(
       'drush mmas turkey --spreads=ketchup,mustard' => 'Make a terrible-tasting sandwich that is lacking in pickles.',
diff --git a/examples/sync_enable.drush.inc b/examples/sync_enable.drush.inc
deleted file mode 100644 (file)
index a3d168c..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-
-/**
- * Sync_enable adds options to sql-sync to enable and disable
- * modules after an sql-sync operation.  One use case for this
- * is to use Drush site aliases to automatically enable your 
- * development modules whenever you sync from your live site to
- * your dev site.
- *
- * For example:
- *
- * $aliases['dev'] = array (
- *   'root' => '/srv/www/drupal',
- *   'uri' => 'site.com',
- *   'target-command-specific' => array(
- *     'sql-sync'  => array(
- *       'enable'  => array('devel', 'hacked'),
- *       'disable' => array('securepages'),
- *     ),
- *   ),
- * );
- *
- * To use this feature, copy the 'target-command-specific'
- * item from the example alias above, place it in your development
- * site aliases, and customize the development module list
- * to suit.  You must also copy the sync_enable.drush.inc
- * file to a location where Drush will find it, such as
- * $HOME/.drush.  See `drush topic docs-commands` for more
- * information.
- */
-
-/**
- * Implement hook help alter.
- *
- * When a hook extends a command with additional options, it must 
- * implement help alter and declare the option(s).  Doing so will add 
- * the option to the help text for the modified command, and will also
- * allow the new option to be specified on the command line.  Without
- * this, Drush will fail with an error when a user attempts to use
- * the option.
- */
-function sync_enable_drush_help_alter(&$command) {
-  if ($command['command'] == 'sql-sync') {
-    $command['options']['enable']  = "Enable the specified modules in the target database after the sync operation has completed.";
-    $command['options']['disable'] = "Disable the specified modules in the target database after the sync operation has completed.";
-  }
-}
-
-/**
- * Implement hook post sql sync.
- *
- * The post hook is only called if the sql-sync operation completes
- * without an error.  When called, we check to see if the user specified
- * any modules to enable/disable.  If so, we will call pm-enable/pm-disable on each module.
- */
-function drush_sync_enable_post_sql_sync($source = NULL, $destination = NULL) {
-  $modules_to_enable = drush_get_option_list('enable');
-  if (!empty($modules_to_enable)) {
-    drush_log(dt("Enable !modules post-sql-sync", array('!modules' => implode(',', $modules_to_enable))), 'ok');
-    drush_invoke_process($destination, 'pm-enable', $modules_to_enable, array('yes' => TRUE));
-  }
-  $modules_to_disable = drush_get_option_list('disable');
-  if (!empty($modules_to_disable)) {
-    drush_log(dt("Disable !modules post-sql-sync", array('!modules' => implode(',', $modules_to_disable))), 'ok');
-    drush_invoke_process($destination, 'pm-disable', $modules_to_disable, array('yes' => TRUE));
-  }
-}
diff --git a/examples/sync_via_http.drush.inc b/examples/sync_via_http.drush.inc
deleted file mode 100644 (file)
index 3ed58e9..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-<?php
-
-/**
- * Sync_via_http allows you to sql-sync your database using HTTP
- * (e.g. wget or curl) instead of rsync.  This is helpful for
- * exporting your database to colaborators without shell access
- * to the production or staging server.
- *
- * For example:
- *
- * $aliases['staging'] = array (
- *   'root' => '/srv/www/drupal',
- *   'uri' => 'staging.site.com',
- *   'source-command-specific' => array(
- *     'sql-sync'  => array(
- *       'http-sync'  => 'https://staging.site.com/protected-directory/site-database-dump.sql',
- *       'http-sync-user' => 'wwwadmin',
- *       'http-sync-password' => 'secretsecret',
- *     ),
- *   ),
- * );
- *
- * To use this feature, copy the 'source-command-specific'
- * item from the example alias above, place it in your staging
- * site aliases, and custom the access credentials as
- * necessary.  You must also copy the sync_via_http.drush.inc
- * file to a location where Drush will find it, such as
- * $HOME/.drush.  See `drush topic docs-commands` for more
- * information.
- *
- * IMPORTANT NOTE:  This example does not cause the sql dump
- * to be performed; it is presumed that the dump file already
- * exists at the provided URL.  For a full solution, a web page
- * that initiated an sql-dump (or perhaps a local sql-sync followed
- * by an sql-sanitize and then an sql-dump) would be necessary.
- */
-
-/**
- * Implement hook help alter.
- *
- * When a hook extends a command with additional options, it must 
- * implement help alter and declare the option(s).  Doing so will add 
- * the option to the help text for the modified command, and will also
- * allow the new option to be specified on the command line.  Without
- * this, Drush will fail with an error when a user attempts to use
- * the option.
- */
-function sync_via_http_drush_help_alter(&$command) {
-  if ($command['command'] == 'sql-sync') {
-    $command['options']['http-sync']  = "Copy the database via http instead of rsync.  Value is the url that the existing database dump can be found at.";
-    $command['sub-options']['http-sync']['http-sync-user']  = "Username for the protected directory containing the sql dump.";
-    $command['sub-options']['http-sync']['http-sync-password']  = "Password for the same directory.";
-  }
-}
-
-/**
- * Implement hook pre sql sync.
- *
- * During the pre hook, determine if the http-sync option has been
- * specified.  If it has been, then disable the normal ssh + rsync
- * dump-and-transfer that sql-sync usually does, and transfer the
- * database dump via an http download.
- */
-function drush_sync_via_http_pre_sql_sync($source = NULL, $destination = NULL) {
-  $sql_dump_download_url = drush_get_option('http-sync');
-  if (!empty($sql_dump_download_url)) {
-    $user = drush_get_option('http-sync-user', FALSE);
-    $password = drush_get_option('http-sync-password', FALSE);
-    $source_dump_file = _drush_sync_via_http_download_file($sql_dump_download_url, $user, $password);
-    if ($source_dump_file === FALSE) {
-      return drush_set_error('DRUSH_CANNOT_DOWNLOAD', dt("The URL !url could not be downloaded.", array('!url' => $sql_dump_download_url)));
-    }
-    drush_set_option('target-dump', $source_dump_file);
-    drush_set_option('no-dump', TRUE);
-    drush_set_option('no-sync', TRUE);
-  }
-}
-
-/**
- * Download a file, optionaly with user authentication, using either wget or
- * curl, as available.
- */
-function _drush_sync_via_http_download_file($url, $user = FALSE, $password = FALSE, $destination = FALSE, $overwrite = TRUE) {
-  static $use_wget;
-  if ($use_wget === NULL) {
-    $use_wget = drush_shell_exec('which wget');
-  }
-
-  $destination_tmp = drush_tempnam('download_file');
-  if ($use_wget) {
-    if ($user && $password) {
-      drush_shell_exec("wget -q --timeout=30 --user=%s --password=%s -O %s %s", $user, $password, $destination_tmp, $url);
-    }
-    else {
-      drush_shell_exec("wget -q --timeout=30 -O %s %s", $destination_tmp, $url);
-    }
-  }
-  else {
-    if ($user && $password) {
-      drush_shell_exec("curl -s -L --connect-timeout 30 --user %s:%s -o %s %s", $user, $password, $destination_tmp, $url);
-    }
-    else {
-      drush_shell_exec("curl -s -L --connect-timeout 30 -o %s %s", $destination_tmp, $url);
-    }
-  }
-  if (!drush_get_context('DRUSH_SIMULATE')) {
-    if (!drush_file_not_empty($destination_tmp) && $file = @file_get_contents($url)) {
-      @file_put_contents($destination_tmp, $file);
-    }
-    if (!drush_file_not_empty($destination_tmp)) {
-      // Download failed.
-      return FALSE;
-    }
-  }
-  if ($destination) {
-    drush_move_dir($destination_tmp, $destination, $overwrite);
-    return $destination;
-  }
-  return $destination_tmp;
-}
index bce0979..5b057ee 100644 (file)
@@ -263,14 +263,7 @@ function drush_backend_parse_output($string, $backend_options = array(), $output
  *    Whether output has already been handled.
  */
 function _drush_backend_integrate($data, $backend_options, $outputted) {
-  // In 'integrate' mode, logs and errors have already been handled
-  // by drush_backend_packet (sender) drush_backend_parse_packets (reciever - us)
-  // during incremental output.  We therefore do not need to call drush_set_error
-  // or drush_log here.  The exception is if the sender is an older version of
-  // Drush (version 4.x) that does not send backend packets, then we will
-  // not have processed the log entries yet, and must print them here.
-  $received_packets = drush_get_context('DRUSH_RECEIVED_BACKEND_PACKETS', FALSE);
-  if (is_array($data['log']) && $backend_options['log'] && (!$received_packets)) {
+  if (is_array($data['log']) && $backend_options['log'] && !$outputted) {
     foreach($data['log'] as $log) {
       $message = is_array($log['message']) ? implode("\n", $log['message']) : $log['message'];
       if (isset($backend_options['#output-label'])) {
@@ -297,12 +290,6 @@ function _drush_backend_integrate($data, $backend_options, $outputted) {
 }
 
 /**
- * Supress log message output during backend integrate.
- */
-function _drush_backend_integrate_log($entry) {
-}
-
-/**
  * Call an external command using proc_open.
  *
  * @param cmds
@@ -325,20 +312,23 @@ function _drush_backend_integrate_log($entry) {
  *   If it executed successfully, it returns an associative array containing the command
  *   called, the output of the command, and the error code of the command.
  */
-function _drush_backend_proc_open($cmds, $process_limit, $context = NULL) {
+function _drush_proc_open($cmds, $process_limit, $context = NULL) {
   $descriptorspec = array(
     0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
     1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
+    2 => array("pipe", "w")   // stderr is a pipe the child will write to
   );
 
+  if (drush_is_windows()) {
+    /*  http://bg2.php.net/manual/en/function.proc-open.php#97012 */
+    $descriptorspec[2][1] = "a";
+  }
+
   $open_processes = array();
   $bucket = array();
   $process_limit = max($process_limit, 1);
-  $is_windows = drush_is_windows();
-  // Loop through processes until they all close, having a nap as needed.
-  $nap_time = 0;
+  // Loop through processes until they all close.
   while (sizeof($open_processes) || sizeof($cmds)) {
-    $nap_time++;
     if (sizeof($cmds) && (sizeof($open_processes) < $process_limit)) {
       // Pop the site and command (key / value) from the cmds array
       end($cmds);
@@ -369,9 +359,11 @@ function _drush_backend_proc_open($cmds, $process_limit, $context = NULL) {
         // this open longer.
         fclose($process['pipes'][0]);
 
-        $process['info'] = stream_get_meta_data($process['pipes'][1]);
-        stream_set_blocking($process['pipes'][1], FALSE);
-        stream_set_timeout($process['pipes'][1], 1);
+        foreach (array(1, 2) as $pipe) {
+          $process['info'] = stream_get_meta_data($process['pipes'][$pipe]);
+          stream_set_blocking($process['pipes'][$pipe], FALSE);
+          stream_set_timeout($process['pipes'][$pipe], 1);
+        }
         $bucket[$site]['cmd'] = $c;
         $bucket[$site]['output'] = '';
         $bucket[$site]['backend-options'] = $backend_options;
@@ -379,97 +371,64 @@ function _drush_backend_proc_open($cmds, $process_limit, $context = NULL) {
         $bucket[$site]['outputted'] = FALSE;
         $open_processes[$site] = $process;
       }
-      // Reset the $nap_time variable as there might be output to process next
-      // time around:
-      $nap_time = 0;
-    }
-    // Set up to call stream_select(). See:
-    // http://php.net/manual/en/function.stream-select.php
-    // We can't use stream_select on Windows, because it doesn't work for
-    // streams returned by proc_open.
-    if (!$is_windows) {
-      $ss_result = 0;
-      $read_streams = array();
-      $write_streams = array();
-      $except_streams = array();
-      foreach ($open_processes as $site => &$current_process) {
-        if (isset($current_process['pipes'][1])) {
-          $read_streams[] = $current_process['pipes'][1];
-        }
-      }
-      // Wait up to 2s for data to become ready on one of the read streams.
-      if (sizeof($read_streams)) {
-        $ss_result = stream_select($read_streams, $write_streams, $except_streams, 2);
-        // If stream_select returns a error, then fallback to using $nap_time.
-        if ($ss_result !== FALSE) {
-          $nap_time = 0;
-        }
-      }
     }
-
     foreach ($open_processes as $site => &$current_process) {
-      if (isset($current_process['pipes'][1])) {
-        // Collect output from stdout
-        $bucket[$site][1] = '';
-        $info = stream_get_meta_data($current_process['pipes'][1]);
-
-        if (!feof($current_process['pipes'][1]) && !$info['timed_out']) {
-          $string = fread($current_process['pipes'][1], 4096);
-          $output_end_pos = strpos($string, 'DRUSH_BACKEND_OUTPUT_START>>>');
-          if ($output_end_pos !== FALSE) {
-            $trailing_string = substr($string, 0, $output_end_pos);
-            if (!empty($trailing_string)) {
-              drush_backend_parse_packets($trailing_string, $bucket[$site]['backend-options']);
-              _drush_backend_print_output($trailing_string, $bucket[$site]['backend-options']);
-              $bucket[$site]['outputted'] = TRUE;
+      if (isset($current_process['pipes'][1]) || isset($current_process['pipes'][2])) {
+        // Collect output from stdout and stderr
+        foreach (array(1, 2) as $pipe) {
+          if (isset($current_process['pipes'][$pipe])) {
+            $bucket[$site][$pipe] = '';
+            $info = stream_get_meta_data($current_process['pipes'][$pipe]);
+
+            if (!feof($current_process['pipes'][$pipe]) && !$info['timed_out']) {
+              $string = fread($current_process['pipes'][$pipe], 4096);
+              $output_end_pos = strpos($string, 'DRUSH_BACKEND_OUTPUT_START>>>');
+              if ($output_end_pos !== FALSE) {
+                $trailing_string = substr($string, 0, $output_end_pos);
+                if (!empty($trailing_string)) {
+                  drush_backend_parse_packets($trailing_string, $bucket[$site]['backend-options']);
+                  _drush_backend_print_output($trailing_string, $bucket[$site]['backend-options']);
+                  $bucket[$site]['outputted'] = TRUE;
+                }
+                $bucket[$site]['end_of_output'] = TRUE;
+              }
+              if (!$bucket[$site]['end_of_output']) {
+                drush_backend_parse_packets($string, $bucket[$site]['backend-options']);
+                // Pass output through.
+                _drush_backend_print_output($string, $bucket[$site]['backend-options']);
+                if (!empty($string)) {
+                  $bucket[$site]['outputted'] = TRUE;
+                }
+              }
+              $bucket[$site][$pipe] .= $string;
+              $bucket[$site]['output'] .= $string;
+              $info = stream_get_meta_data($current_process['pipes'][$pipe]);
+              flush();
             }
-            $bucket[$site]['end_of_output'] = TRUE;
-          }
-          if (!$bucket[$site]['end_of_output']) {
-            drush_backend_parse_packets($string, $bucket[$site]['backend-options']);
-            // Pass output through.
-            _drush_backend_print_output($string, $bucket[$site]['backend-options']);
-            if (!empty($string)) {
-              $bucket[$site]['outputted'] = TRUE;
+            else {
+              fclose($current_process['pipes'][$pipe]);
+              unset($current_process['pipes'][$pipe]);
+              // close the pipe , set a marker
+
+              if (drush_is_windows() && ($pipe == 1)) {
+                /* On windows STDERR doesn't give EOF even if STDOUT gets closed
+                * Because of this this loop goes indefinitely
+                * and this manual unset is needed on windows in order the script to work
+                */
+                fclose($current_process['pipes'][2]);
+                unset($current_process['pipes'][2]);
+              }
+
             }
           }
-          $bucket[$site][1] .= $string;
-          $bucket[$site]['output'] .= $string;
-          $info = stream_get_meta_data($current_process['pipes'][1]);
-          flush();
-
-          // Reset the $nap_time variable as there might be output to process
-          // next time around:
-          if (!empty($string)) {
-            $nap_time = 0;
-          }
-        }
-        else {
-          fclose($current_process['pipes'][1]);
-          unset($current_process['pipes'][1]);
-          // close the pipe , set a marker
-
-          // Reset the $nap_time variable as there might be output to process
-          // next time around:
-          $nap_time = 0;
         }
       }
       else {
         // if both pipes are closed for the process, remove it from active loop and add a new process to open.
         $bucket[$site]['code'] = proc_close($current_process['process']);
         unset($open_processes[$site]);
-
-        // Reset the $nap_time variable as there might be output to process next
-        // time around:
-        $nap_time = 0;
       }
     }
-
-    // We should sleep for a bit if we need to, up to a maximum of 1/10 of a
-    // second.
-    if ($nap_time > 0) {
-      usleep(max($nap_time * 500, 100000));
-    }
   }
   return $bucket;
   // TODO: Handle bad proc handles
@@ -477,8 +436,6 @@ function _drush_backend_proc_open($cmds, $process_limit, $context = NULL) {
   //return FALSE;
 }
 
-
-
 /**
  * Print the output received from a call to backend invoke,
  * adding the label to the head of each line if necessary.
@@ -506,7 +463,6 @@ function _drush_backend_print_output($output_string, $backend_options) {
 function drush_backend_parse_packets(&$string, $backend_options) {
   $packet_regex = strtr(sprintf(DRUSH_BACKEND_PACKET_PATTERN, "([^\0]*)"), array("\0" => "\\0"));
   if (preg_match_all("/$packet_regex/s", $string, $match, PREG_PATTERN_ORDER)) {
-    drush_set_context('DRUSH_RECEIVED_BACKEND_PACKETS', TRUE);
     foreach ($match[1] as $packet_data) {
       $entry = (array) json_decode($packet_data);
       if (is_array($entry) && isset($entry['packet'])) {
@@ -687,7 +643,7 @@ function drush_backend_invoke_concurrent($invocations, $common_options = array()
       '%drush-script' => NULL,
     );
 
-    $site = (array_key_exists('#name', $site_record) && !array_key_exists($site_record['#name'], $invocation_options)) ? $site_record['#name'] : $index++;
+    $site = array_key_exists('#name', $site_record) ? $site_record['#name'] : $index++;
     $invocation_options[$site] = array(
       'site-record' => $site_record,
       'command' => $command,
@@ -818,7 +774,6 @@ function _drush_backend_classify_options($site_record, $command_options, &$backe
   $post_options = array();
   $commandline_options = array();
   $drush_global_options = array();
-  $drush_local_options = array();
   $additional_backend_options = array();
   foreach ($site_record as $key => $value) {
     if (!in_array($key, drush_sitealias_site_selection_keys())) {
@@ -830,16 +785,6 @@ function _drush_backend_classify_options($site_record, $command_options, &$backe
       }
     }
   }
-  if (array_key_exists('drush-local-options', $backend_options)) {
-    $drush_local_options = $backend_options['drush-local-options'];
-    $command_options += $drush_local_options;
-  }
-  if (!empty($backend_options['backend']) && empty($backend_options['interactive'])) {
-    $drush_global_options['backend'] = '2';
-  }
-  if (!empty($backend_options['interactive'])) {
-    $drush_global_options['invoke'] = TRUE;
-  }
   foreach ($command_options as $key => $value) {
     $global = array_key_exists($key, $global_option_list);
     $propagate = TRUE;
@@ -896,11 +841,7 @@ function _drush_backend_classify_options($site_record, $command_options, &$backe
  *
  * @return
  *   If the command could not be completed successfully, FALSE.
- *   If one command was executed, this will return an associative array containing 
- *   the data from drush_backend_output().
- *   If multiple commands were executed, this will return an associative array
- *   containing one item, 'concurrent', which will contain a list of the different
- *   backend invoke results from each concurrent command.
+ *   If the command was completed, this will return an associative array containing the data from drush_backend_output().
  */
 function _drush_backend_invoke($cmds, $common_backend_options = array(), $context = NULL) {
   if (drush_get_context('DRUSH_SIMULATE') && !array_key_exists('override-simulated', $common_backend_options)) {
@@ -912,24 +853,20 @@ function _drush_backend_invoke($cmds, $common_backend_options = array(), $contex
   foreach ($cmds as $cmd) {
     drush_log(dt('Backend invoke: !cmd', array('!cmd' => $cmd['cmd'])), 'command');
   }
-  if (array_key_exists('interactive', $common_backend_options) || array_key_exists('fork', $common_backend_options)) {
+  if (array_key_exists('interactive', $common_backend_options)) {
     foreach ($cmds as $cmd) {
-      $exec_cmd = $cmd['cmd'];
-      if (array_key_exists('fork', $common_backend_options)) {
-        $exec_cmd .= ' --quiet &';
-      }
-      drush_log(dt("executing !cmd", array('!cmd' => $exec_cmd)));
-      $ret = drush_shell_proc_open($exec_cmd);
+      drush_log(dt("executing !cmd", array('!cmd' => $cmd['cmd'])));
+      $ret = drush_shell_proc_open($cmd['cmd']);
     }
     return $ret;
   }
   else {
     $process_limit = drush_get_option_override($common_backend_options, 'concurrency', 4);
-    $procs = _drush_backend_proc_open($cmds, $process_limit, $context);
+    $procs = _drush_proc_open($cmds, $process_limit, $context);
     $procs = is_array($procs) ? $procs : array($procs);
 
-    $ret = array();
     foreach ($procs as $site => $proc) {
+      $ret = array();
       if (($proc['code'] == DRUSH_APPLICATION_ERROR) && isset($common_backend_options['integrate'])) {
         drush_set_error('DRUSH_APPLICATION_ERROR', dt("The external command could not be executed due to an application error."));
       }
@@ -941,15 +878,15 @@ function _drush_backend_invoke($cmds, $common_backend_options = array(), $contex
           if (empty($ret)) {
             $ret = $values;
           }
-          elseif (!array_key_exists('concurrent', $ret)) {
-            $ret = array('concurrent' => array($ret, $values));
+          elseif (!array_key_exists('multiple', $ret)) {
+            $ret = array('multiple' => array($ret['site'] => $ret, $values['site'] => $values));
           }
           else {
-            $ret['concurrent'][] = $values;
+            $ret['multiple'][$values['site']] = $values;
           }
         }
         else {
-          $ret = drush_set_error('DRUSH_FRAMEWORK_ERROR', dt("The command could not be executed successfully (returned: !return, code: !code)", array("!return" => $proc['output'], "!code" =>  $proc['code'])));
+          $ret = drush_set_error('DRUSH_FRAMEWORK_ERROR', dt("The command could not be executed successfully (returned: !return, code: %code)", array("!return" => $proc['output'], "%code" =>  $proc['code'])));
         }
       }
     }
@@ -1051,6 +988,12 @@ function _drush_backend_generate_command($site_record, $command, $args = array()
   if (!empty($option_str)) {
     $cmd[] = " " . $option_str;
   }
+  if (!empty($backend_options['backend']) && empty($backend_options['interactive'])) {
+    $cmd[] = "--backend=2";
+  }
+  if (!empty($backend_options['interactive'])) {
+    $cmd[] .= '--invoke';
+  }
   $command = implode(' ', array_filter($cmd, 'strlen'));
   if (!is_null($hostname)) {
     if (drush_is_windows($os)) {
index c13db27..f303798 100644 (file)
@@ -205,11 +205,6 @@ function drush_bootstrap($phase, $phase_max = FALSE) {
 /**
  * Determine whether a given bootstrap phase has been completed
  *
- * This function name has a typo which makes me laugh so we choose not to
- * fix it. Take a deep breath, and smile. See
- * http://en.wikipedia.org/wiki/HTTP_referer
- *
- *
  * @param phase
  *   The bootstrap phase to test
  *
@@ -516,8 +511,6 @@ function _drush_bootstrap_drush() {
       drush_load_config('site');
     }
   }
-  // Check to see if we 'use'd a site alias with site-set
-  drush_sitealias_check_site_env();
 
   _drush_bootstrap_global_options();
 }
@@ -832,10 +825,8 @@ function _drush_bootstrap_drupal_configuration() {
     'cron_safe_threshold' => 0,
   );
   $current_override = drush_get_option_list('variables');
-  foreach ($current_override as $name => $value) {
-    if (is_numeric($name) && (strpos($value, '=') !== FALSE)) {
-      list($name, $value) = explode('=', $value, 2);
-    }
+  foreach ( $current_override as $pair) {
+    list($name, $value) = explode('=', $pair, 2);
     $override[$name] = $value;
   }
   $conf = is_array($conf) ? array_merge($conf, $override) : $conf;
index edf162e..1a04e65 100644 (file)
@@ -105,7 +105,7 @@ function drush_cache_get_multiple(array &$cids, $bin = 'default') {
  */
 function drush_cache_set($cid, $data, $bin = 'default', $expire = DRUSH_CACHE_PERMANENT) {
   $ret = _drush_cache_get_object($bin)->set($cid, $data, $expire);
-  if ($ret) drush_log(dt("Cache SET cid: !cid", array('!cid' => $cid)), 'debug');
+  if ($ret) drush_log(dt("Cache SET cid: %cid", array('%cid' => $cid)), 'debug');
   return $ret;
 }
 
index 4b4ad33..70d26bc 100644 (file)
@@ -77,12 +77,6 @@ function drush_invoke($command, $arguments = array()) {
  *        database information for sql-sync via sql-conf.
  *      'interactive'
  *        Overrides the backend invoke process to run commands interactively.
- *      'fork'
- *        Overrides the backend invoke process to run non blocking commands in
- *        the background. Forks a new process by adding a '&' at the end of the
- *        command. The calling process does not receive any output from the child
- *        process. The fork option is used to spawn a process that outlives its
- *        parent.
  *
  * @return
  *   If the command could not be completed successfully, FALSE.
@@ -93,7 +87,7 @@ function drush_invoke($command, $arguments = array()) {
  * Do not change the signature of this function!  drush_invoke_process
  * is one of the key Drush APIs.  See http://drupal.org/node/1152908
  */
-function drush_invoke_process($site_alias_record, $command_name, $commandline_args = array(), $commandline_options = array(), $backend_options = TRUE) {
+function drush_invoke_process($site_alias_record, $command_name, $commandline_args = array(), $commandline_options = array(), $backend_options = FALSE) {
   if (is_array($site_alias_record) && array_key_exists('site-list', $site_alias_record)) {
     $site_alias_records = drush_sitealias_resolve_sitespecs($site_alias_record['site-list']);
     $site_alias_records = drush_sitealias_simplify_names($site_alias_records);
@@ -112,7 +106,7 @@ function drush_invoke_process($site_alias_record, $command_name, $commandline_ar
       'args' => $commandline_args);
     $invoke_multiple = drush_get_option_override($backend_options, 'invoke-mult