core.drush.inc

  1. 8.0.x commands/core/core.drush.inc
  2. 6.x commands/core/core.drush.inc
  3. 7.x commands/core/core.drush.inc
  4. 3.x commands/core/core.drush.inc
  5. 4.x commands/core/core.drush.inc
  6. 5.x commands/core/core.drush.inc
  7. master commands/core/core.drush.inc

Core drush commands.

Functions

Namesort descending Description
core_core_config_complete Command argument complete callback.
core_core_rsync_complete Command argument complete callback.
core_drush_command Implementation of hook_drush_command().
core_drush_engine_drupal Implements hook_drush_engine_ENGINE_TYPE().
core_drush_engine_type_info Implementation of hook_drush_engine_type_info().
core_drush_help Implementation of hook_drush_help().
core_help_complete Command argument complete callback.
core_site_install_complete Command argument complete callback.
drush_core_batch_process Process sets from the specified batch.
drush_core_config Command callback. Edit drushrc and alias files.
drush_core_config_load
drush_core_cron Command callback. Runs all cron hooks.
drush_core_drupal_directory Command callback.
drush_core_execute Command callback. Execute specified shell code. Often used by shell aliases that start with !.
drush_core_global_options
drush_core_php_eval
drush_core_php_script Command callback. Runs "naked" php scripts and drush "shebang" scripts ("#!/usr/bin/env drush").
drush_core_quick_drupal Callback for core-quick-drupal command.
drush_core_quick_drupal_options Include options and engines for core-quick-drupal command, aggregated from other command options that are available. We prefix option descriptons, to make the long list more navigable.
drush_core_requirements Command callback. Provides information from the 'Status Reports' admin page.
drush_core_status Command callback. Provides a birds-eye view of the current Drupal installation.
drush_core_updatedb Command handler. Execute update.php code from drush.
drush_core_updatedb_batch_process Process outstanding updates during updatedb.
drush_core_updatedb_status Command handler. List pending DB updates.
drush_core_version Called for `drush version` or `drush --version`
_core_path_aliases
_core_site_credentials
_core_site_status_table
_drush_core_config_php_ini_files
_drush_core_directory Given a target (e.g. @site:%modules), return the evaluated directory path.
_drush_core_eval_shebang_script Evaluate a script that begins with #!drush php-script
_drush_core_execute_cmd Helper function for drush_core_execute: run one shell command
_drush_core_is_named_in_array
_drush_core_qd_cache_uri
_drush_core_status_format_table_data

File

commands/core/core.drush.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * Core drush commands.
  5. */
  6. use Drush\Log\LogLevel;
  7. /**
  8. * Implementation of hook_drush_help().
  9. *
  10. * This function is called whenever a drush user calls
  11. * 'drush help <name-of-your-command>'
  12. *
  13. * @param
  14. * A string with the help section (prepend with 'drush:')
  15. *
  16. * @return
  17. * A string with the help text for your command.
  18. */
  19. function core_drush_help($section) {
  20. switch ($section) {
  21. case 'meta:core:title':
  22. return dt("Core Drush commands");
  23. case 'drush:help':
  24. return dt("Drush provides an extensive help system that describes both drush commands and topics of general interest. Use `drush help --filter` to present a list of command categories to view, and `drush topic` for a list of topics that go more in-depth on how to use and extend drush.");
  25. case 'drush:php-script':
  26. return dt("Runs the given php script(s) after a full Drupal bootstrap. A useful alternative to eval command when your php is lengthy or you can't be bothered to figure out bash quoting. If you plan to share a script with others, consider making a full drush command instead, since that's more self-documenting. Drush provides commandline options to the script via drush_get_option('option-name'), and commandline arguments can be accessed either via drush_get_arguments(), which returns all arguments in an array, or drush_shift(), which removes the next argument from the list and returns it.");
  27. case 'drush:rsync':
  28. return dt("Sync the entire drupal directory or a subdirectory to a <destination> using ssh. Excludes reserved files and directories for supported VCSs. Useful for pushing copies of your tree to a staging server, or retrieving a files directory from a remote site. Relative paths start from the Drupal root directory if a site alias is used; otherwise they start from the current working directory.");
  29. case 'error:DRUSH_DRUPAL_DB_ERROR':
  30. $message = dt("Drush was not able to start (bootstrap) the Drupal database.\n");
  31. $message .= dt("Hint: This may occur when Drush is trying to:\n");
  32. $message .= dt(" * bootstrap a site that has not been installed or does not have a configured database. In this case you can select another site with a working database setup by specifying the URI to use with the --uri parameter on the command line. See `drush topic docs-aliases` for details.\n");
  33. $message .= dt(" * connect the database through a socket. The socket file may be wrong or the php-cli may have no access to it in a jailed shell. See http://drupal.org/node/1428638 for details.\n");
  34. $message .= dt("\nDrush was attempting to connect to: \n!credentials\n", array('!credentials' => _core_site_credentials(12)));
  35. return $message;
  36. case 'error:DRUSH_DRUPAL_BOOTSTRAP_ERROR':
  37. $message = dt("Drush was not able to start (bootstrap) Drupal.\n");
  38. $message .= dt("Hint: This error can only occur once the database connection has already been successfully initiated, therefore this error generally points to a site configuration issue, and not a problem connecting to the database.\n");
  39. $message .= dt("\nDrush was attempting to connect to: \n!credentials\n", array('!credentials' => _core_site_credentials(12)));
  40. return $message;
  41. break;
  42. }
  43. }
  44. /**
  45. * Implementation of hook_drush_command().
  46. *
  47. * In this hook, you specify which commands your
  48. * drush module makes available, what it does and
  49. * description.
  50. *
  51. * Notice how this structure closely resembles how
  52. * you define menu hooks.
  53. *
  54. * @return
  55. * An associative array describing your command(s).
  56. */
  57. function core_drush_command() {
  58. $items = array();
  59. $items['help'] = array(
  60. 'description' => 'Print this help message. See `drush help help` for more options.',
  61. 'bootstrap' => DRUSH_BOOTSTRAP_NONE, // No bootstrap.
  62. 'allow-additional-options' => TRUE,
  63. 'options' => array(
  64. 'sort' => 'Sort commands in alphabetical order. Drush waits for full bootstrap before printing any commands when this option is used.',
  65. 'filter' => array(
  66. 'description' => 'Restrict command list to those commands defined in the specified file. Omit value to choose from a list of names.',
  67. 'example-value' => 'category',
  68. 'value' => 'optional',
  69. ),
  70. 'format' => 'Format to output . Allowed values are: json, var_export, html.',
  71. 'html' => 'Print help for all commands in HTML format. Deprecated - see --format option.',
  72. 'pipe' => 'A list of available commands, one per line.',
  73. ),
  74. 'arguments' => array(
  75. 'command' => 'A command name, or command alias.',
  76. ),
  77. 'examples' => array(
  78. 'drush' => 'List all commands.',
  79. 'drush --filter=devel_generate' => 'Show only commands defined in devel_generate.drush.inc',
  80. 'drush help pm-download' => 'Show help for one command.',
  81. 'drush help dl' => 'Show help for one command using an alias.',
  82. ),
  83. 'topics' => array('docs-readme'),
  84. );
  85. $items['version'] = array(
  86. 'description' => 'Show drush version.',
  87. 'bootstrap' => DRUSH_BOOTSTRAP_NONE, // No bootstrap.
  88. 'options' => array(
  89. 'pipe' => 'Print just the version number, and nothing else.',
  90. ),
  91. 'outputformat' => array(
  92. 'default' => 'key-value',
  93. 'pipe-format' => 'string',
  94. 'label' => 'Drush Version',
  95. 'output-data-type' => 'format-single',
  96. ),
  97. );
  98. $items['core-cron'] = array(
  99. 'description' => 'Run all cron hooks in all active modules for specified site.',
  100. 'aliases' => array('cron'),
  101. 'topics' => array('docs-cron'),
  102. );
  103. $items['updatedb'] = array(
  104. 'description' => 'Apply any database updates required (as with running update.php).',
  105. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_SITE,
  106. 'global-options' => array(
  107. 'cache-clear',
  108. ),
  109. 'aliases' => array('updb'),
  110. );
  111. $items['updatedb-status'] = array(
  112. 'description' => 'List any pending database updates.',
  113. 'outputformat' => array(
  114. 'default' => 'table',
  115. 'pipe-format' => 'csv',
  116. 'field-labels' => array('module' => 'Module', 'update_id' => 'Update ID', 'description' => 'Description'),
  117. 'fields-default' => array('module', 'update_id', 'description'),
  118. 'output-data-type' => 'format-table',
  119. ),
  120. 'aliases' => array('updbst'),
  121. );
  122. $items['core-config'] = array(
  123. 'description' => 'Edit drushrc, site alias, and Drupal settings.php files.',
  124. 'bootstrap' => DRUSH_BOOTSTRAP_MAX,
  125. 'arguments' => array(
  126. 'filter' => 'A substring for filtering the list of files. Omit this argument to choose from loaded files.',
  127. ),
  128. 'options' => array(
  129. 'bg' => 'Run editor in the background. Does not work with editors such as `vi` that run in the terminal.',
  130. ),
  131. 'examples' => array(
  132. 'drush core-config' => 'Pick from a list of config/alias/settings files. Open selected in editor.',
  133. 'drush --bg core-config' => 'Return to shell prompt as soon as the editor window opens.',
  134. 'drush core-config etc' => 'Edit the global configuration file.',
  135. 'drush core-config demo.alia' => 'Edit a particular alias file.',
  136. 'drush core-config sett' => 'Edit settings.php for the current Drupal site.',
  137. 'drush core-config --choice=2' => 'Edit the second file in the choice list.',
  138. ),
  139. 'aliases' => array('conf', 'config'),
  140. );
  141. $items['core-status'] = array(
  142. 'description' => 'Provides a birds-eye view of the current Drupal installation, if any.',
  143. 'bootstrap' => DRUSH_BOOTSTRAP_MAX,
  144. 'aliases' => array('status', 'st'),
  145. 'examples' => array(
  146. 'drush core-status version' => 'Show all status lines that contain version information.',
  147. 'drush core-status --pipe' => 'A list key=value items separated by line breaks.',
  148. 'drush core-status drush-version --pipe' => 'Emit just the drush version with no label.',
  149. 'drush core-status config-active --pipe' => 'Emit just the active Config directory with no label.',
  150. ),
  151. 'arguments' => array(
  152. 'item' => 'Optional. The status item line(s) to display.',
  153. ),
  154. 'options' => array(
  155. 'show-passwords' => 'Show database password.',
  156. 'full' => 'Show all file paths and drush aliases in the report, even if there are a lot.',
  157. 'project' => array(
  158. 'description' => 'One or more projects that should be added to the path list',
  159. 'example-value' => 'foo,bar',
  160. ),
  161. ),
  162. 'outputformat' => array(
  163. 'default' => 'key-value',
  164. 'pipe-format' => 'json',
  165. 'field-labels' => array('drupal-version' => 'Drupal version', 'uri' => 'Site URI', 'db-driver' => 'Database driver', 'db-hostname' => 'Database hostname', 'db-port' => 'Database port', 'db-username' => 'Database username', 'db-password' => 'Database password', 'db-name' => 'Database name', 'db-status' => 'Database', 'bootstrap' => 'Drupal bootstrap', 'user' => 'Drupal user', 'theme' => 'Default theme', 'admin-theme' => 'Administration theme', 'php-bin' => 'PHP executable', 'php-conf' => 'PHP configuration', 'php-os' => 'PHP OS', 'drush-script' => 'Drush script', 'drush-version' => 'Drush version', 'drush-temp' => 'Drush temp directory', 'drush-conf' => 'Drush configuration', 'drush-alias-files' => 'Drush alias files', 'install-profile' => 'Install profile', 'root' => 'Drupal root', 'site-path' => 'Site path', 'root' => 'Drupal root', 'site' => 'Site path', 'themes' => 'Themes path', 'modules' => 'Modules path', 'files' => 'File directory path', 'private' => 'Private file directory path', 'temp' => 'Temporary file directory path', 'config-active' => 'Active config path', 'config-staging' => 'Staging config path', 'files-path' => 'File directory path', 'temp-path' => 'Temporary file directory path', '%paths' => 'Other paths'),
  166. 'formatted-filter' => '_drush_core_status_format_table_data',
  167. 'private-fields' => 'db-password',
  168. 'simplify-single' => TRUE,
  169. 'table-metadata' => array(
  170. 'list-separator' => ' ',
  171. ),
  172. 'output-data-type' => 'format-list',
  173. ),
  174. 'topics' => array('docs-readme'),
  175. );
  176. $items['core-requirements'] = array(
  177. 'description' => 'Provides information about things that may be wrong in your Drupal installation, if any.',
  178. 'aliases' => array('status-report','rq'),
  179. 'examples' => array(
  180. 'drush core-requirements' => 'Show all status lines from the Status Report admin page.',
  181. 'drush core-requirements --severity=2' => 'Show only the red lines from the Status Report admin page.',
  182. 'drush core-requirements --pipe' => 'Print out a short report in JSON format, where severity 2=error, 1=warning, and 0/-1=OK',
  183. ),
  184. 'options' => array(
  185. 'severity' => array(
  186. 'description' => 'Only show status report messages with a severity greater than or equal to the specified value.',
  187. 'value' => 'required',
  188. 'example-value' => '3',
  189. ),
  190. 'ignore' => 'Comma-separated list of requirements to remove from output. Run with --pipe to see key values to use.',
  191. ),
  192. 'outputformat' => array(
  193. 'default' => 'table',
  194. 'pipe-format' => 'json',
  195. 'field-labels' => array('title' => 'Title', 'severity' => 'Severity', 'sid' => 'SID', 'description' => 'Description', 'value' => 'Summary', 'reason' => 'Reason', 'weight' => 'Weight'),
  196. 'fields-default' => array('title', 'severity', 'description'),
  197. 'column-widths' => array('severity' => 8),
  198. 'concatenate-columns' => array('description' => array('value', 'description')),
  199. 'strip-tags' => TRUE,
  200. 'ini-item' => 'sid',
  201. 'key-value-item' => 'severity',
  202. 'list-metadata' => array(
  203. 'list-item' => 'severity',
  204. ),
  205. 'output-data-type' => 'format-table',
  206. ),
  207. );
  208. $items['php-eval'] = array(
  209. 'description' => 'Evaluate arbitrary php code after bootstrapping Drupal (if available).',
  210. 'examples' => array(
  211. 'drush php-eval \'variable_set("hello", "world");\'' => 'Sets the hello variable using Drupal API.',
  212. 'drush php-eval \'$node = node_load(1); print $node->title;\'' => 'Loads node with nid 1 and then prints its title.',
  213. 'drush php-eval "file_unmanaged_copy(\'$HOME/Pictures/image.jpg\', \'public://image.jpg\');"' => 'Copies a file whose path is determined by an environment\'s variable. Note the use of double quotes so the variable $HOME gets replaced by its value.',
  214. ),
  215. 'arguments' => array(
  216. 'code' => 'PHP code',
  217. ),
  218. 'required-arguments' => TRUE,
  219. 'allow-additional-options' => TRUE,
  220. 'bootstrap' => DRUSH_BOOTSTRAP_MAX,
  221. 'aliases' => array('eval', 'ev'),
  222. 'outputformat' => array(
  223. 'default' => 'var_export',
  224. ),
  225. );
  226. $items['php-script'] = array(
  227. 'description' => "Run php script(s).",
  228. 'examples' => array(
  229. 'drush php-script scratch' => 'Run scratch.php script. See commands/core directory.',
  230. 'drush php-script example --script-path=/path/to/scripts:/another/path' => 'Run script from specified paths',
  231. 'drush php-script' => 'List all available scripts.',
  232. '' => '',
  233. "#!/usr/bin/env drush\n<?php\nvariable_set('key', drush_shift());" => "Execute php code with a full Drupal bootstrap directly from a shell script.",
  234. ),
  235. 'arguments' => array(
  236. '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.',
  237. ),
  238. 'options' => array(
  239. 'script-path' => array(
  240. 'description' => "Additional paths to search for scripts, separated by : (Unix-based systems) or ; (Windows).",
  241. 'example-value' => '~/scripts',
  242. ),
  243. ),
  244. 'allow-additional-options' => TRUE,
  245. 'bootstrap' => DRUSH_BOOTSTRAP_MAX,
  246. 'aliases' => array('scr'),
  247. 'topics' => array('docs-examplescript', 'docs-scripts'),
  248. );
  249. $items['core-execute'] = array(
  250. 'description' => 'Execute a shell command. Usually used with a site alias.',
  251. 'bootstrap' => DRUSH_BOOTSTRAP_NONE, // No bootstrap.
  252. 'arguments' => array(
  253. 'command' => 'The shell command to be executed.',
  254. ),
  255. 'options' => array(
  256. 'escape' => 'Escape parameters before executing them with the shell. Default is escape; use --escape=0 to disable.',
  257. ) + drush_shell_exec_proc_build_options(),
  258. 'required-arguments' => TRUE,
  259. 'allow-additional-options' => TRUE,
  260. 'handle-remote-commands' => TRUE,
  261. 'strict-option-handling' => TRUE,
  262. 'examples' => array(
  263. 'drush core-execute git pull origin rebase' => 'Retrieve latest code from git',
  264. ),
  265. 'aliases' => array('exec', 'execute'),
  266. 'topics' => array('docs-aliases'),
  267. );
  268. $items['core-rsync'] = array(
  269. 'description' => 'Rsync the Drupal tree to/from another server using ssh.',
  270. 'bootstrap' => DRUSH_BOOTSTRAP_NONE, // No bootstrap.
  271. 'arguments' => array(
  272. 'source' => 'May be rsync path or site alias. See rsync documentation and example.aliases.drushrc.php.',
  273. 'destination' => 'May be rsync path or site alias. See rsync documentation and example.aliases.drushrc.php.',
  274. ),
  275. 'options' => array(
  276. 'mode' => 'The unary flags to pass to rsync; --mode=rultz implies rsync -rultz. Default is -akz.',
  277. 'exclude-conf' => 'Excludes settings.php from being rsynced. Default.',
  278. 'include-conf' => 'Allow settings.php to be rsynced. Default is to exclude settings.php.',
  279. 'include-vcs' => 'Include special version control directories (e.g. .svn). Default is to exclude vcs files.',
  280. 'exclude-files' => 'Exclude the files directory.',
  281. 'exclude-sites' => 'Exclude all directories in "sites/" except for "sites/all".',
  282. 'exclude-other-sites' => 'Exclude all directories in "sites/" except for "sites/all" and the site directory for the site being synced. Note: if the site directory is different between the source and destination, use --exclude-sites followed by "drush rsync @from:%site @to:%site"',
  283. 'exclude-paths' => 'List of paths to exclude, seperated by : (Unix-based systems) or ; (Windows).',
  284. 'include-paths' => 'List of paths to include, seperated by : (Unix-based systems) or ; (Windows).',
  285. '{rsync-option-name}' => "Replace {rsync-option-name} with the rsync option (or option='value') that you would like to pass through to rsync. Examples include --delete, --exclude=*.sql, --filter='merge /etc/rsync/default.rules', etc. See the rsync documentation for a complete explanation of all the rsync options and values.",
  286. 'rsync-version' => 'Set to the version of rsync you are using to signal Drush to go into backwards-compatibility mode when using very old versions of rsync. For example, --rsync-version=2.6.8 or earlier will cause Drush to avoid the --remove-source-files flag.',
  287. ),
  288. 'strict-option-handling' => TRUE,
  289. 'examples' => array(
  290. 'drush rsync @dev @stage' => 'Rsync Drupal root from Drush alias dev to the alias stage (one of which must be local).',
  291. 'drush rsync ./ @stage:%files/img' => 'Rsync all files in the current directory to the \'img\' directory in the file storage folder on the Drush alias stage.',
  292. 'drush -s rsync @dev @stage --exclude=*.sql --delete' => "Simulate Rsync Drupal root from the Drush alias dev to the alias stage (one of which must be local), excluding all files that match the filter '*.sql' and delete all files on the destination that are no longer on the source.",
  293. ),
  294. 'aliases' => array('rsync'),
  295. 'topics' => array('docs-aliases'),
  296. );
  297. $items['drupal-directory'] = array(
  298. 'description' => 'Return the filesystem path for modules/themes and other key folders.',
  299. 'arguments' => array(
  300. 'target' => 'A module/theme name, or special names like root, files, private, or an alias : path alias string such as @alias:%files. Defaults to root.',
  301. ),
  302. 'options' => array(
  303. 'component' => "The portion of the evaluated path to return. Defaults to 'path'; 'name' returns the site alias of the target.",
  304. 'local-only' => "Reject any target that specifies a remote site.",
  305. ),
  306. 'examples' => array(
  307. 'cd `drush dd devel`' => 'Navigate into the devel module directory',
  308. 'cd `drush dd` ' => 'Navigate to the root of your Drupal site',
  309. 'cd `drush dd files`' => 'Navigate to the files directory.',
  310. 'drush dd @alias:%files' => 'Print the path to the files directory on the site @alias.',
  311. 'edit `drush dd devel`/devel.module' => "Open devel module in your editor (customize 'edit' for your editor)",
  312. ),
  313. 'aliases' => array('dd'),
  314. 'bootstrap' => DRUSH_BOOTSTRAP_NONE,
  315. );
  316. $items['batch-process'] = array(
  317. 'description' => 'Process operations in the specified batch set',
  318. 'hidden' => TRUE,
  319. 'arguments' => array(
  320. 'batch-id' => 'The batch id that will be processed.',
  321. ),
  322. 'required-arguments' => TRUE,
  323. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_LOGIN,
  324. );
  325. $items['updatedb-batch-process'] = array(
  326. 'description' => 'Perform update functions',
  327. 'hidden' => TRUE,
  328. 'arguments' => array(
  329. 'batch-id' => 'The batch id that will be processed',
  330. ),
  331. 'required-arguments' => TRUE,
  332. // Drupal 7 needs DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION, while Drupal 8 needs _FULL.
  333. // Therefore we bootstrap to _FULL in commands/core/drupal/update.inc.
  334. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION,
  335. );
  336. $items['core-global-options'] = array(
  337. 'description' => 'All global options',
  338. 'hidden' => TRUE,
  339. 'topic' => TRUE,
  340. 'bootstrap' => DRUSH_BOOTSTRAP_NONE,
  341. 'outputformat' => array(
  342. 'default' => 'table',
  343. 'pipe-format' => 'csv',
  344. 'field-labels' => array('label' => 'Label', 'description' => 'Description'),
  345. 'output-data-type' => 'format-table',
  346. ),
  347. );
  348. $items['core-quick-drupal'] = array(
  349. 'description' => 'Download, install, serve and login to Drupal with minimal configuration and dependencies.',
  350. 'bootstrap' => DRUSH_BOOTSTRAP_NONE,
  351. 'aliases' => array('qd', 'cutie'),
  352. 'arguments' => array(
  353. 'site' => 'Short name for the site to be created - used as a directory name and as sqlite file name. Optional - if omitted timestamped "quick-drupal" directory will be used instead.',
  354. 'projects' => 'A list of projects to download into the new site. If projects contain extensions (modules or themes) with the same name they will be enabled by default. See --enable option to control this behaviour further.',
  355. ),
  356. 'examples' => array(
  357. 'drush qd' => 'Download and install stable release of Drupal into a timestamped directory, start server, and open the site logged in as admin.',
  358. 'drush qd --profile=minimal --cache --core=drupal-8.0.x --yes' => 'Fire up dev release of Drupal site with minimal install profile.',
  359. '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.',
  360. '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.',
  361. 'drush qd --makefile=mysite.make' => 'Create and install a site from a makefile.',
  362. ),
  363. );
  364. // Add in options/engines.
  365. drush_core_quick_drupal_options($items);
  366. // Add in topics for engines
  367. $items += drush_get_engine_topics();
  368. return $items;
  369. }
  370. /**
  371. * Command argument complete callback.
  372. *
  373. * @return
  374. * Array of available command names.
  375. */
  376. function core_help_complete() {
  377. return array('values' => array_keys(drush_get_commands()));
  378. }
  379. /**
  380. * Command argument complete callback.
  381. *
  382. * @return
  383. * Array of available profile names.
  384. */
  385. function core_site_install_complete() {
  386. $max = drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_ROOT);
  387. if ($max >= DRUSH_BOOTSTRAP_DRUPAL_ROOT) {
  388. return array('values' => array_keys(drush_find_profiles(DRUPAL_ROOT)));
  389. }
  390. }
  391. /**
  392. * Command argument complete callback.
  393. *
  394. * @return
  395. * Array of available site aliases.
  396. */
  397. function core_core_rsync_complete() {
  398. return array('values' => array_keys(_drush_sitealias_all_list()));
  399. }
  400. /**
  401. * @defgroup engines Engine types
  402. * @{
  403. */
  404. /**
  405. * Implementation of hook_drush_engine_type_info().
  406. */
  407. function core_drush_engine_type_info() {
  408. $info = array();
  409. $info['drupal'] = array();
  410. return $info;
  411. }
  412. /**
  413. * Implements hook_drush_engine_ENGINE_TYPE().
  414. */
  415. function core_drush_engine_drupal() {
  416. $engines = array(
  417. 'batch' => array(),
  418. 'update'=> array(),
  419. 'environment' => array(),
  420. 'site_install' => array(),
  421. 'pm' => array(),
  422. 'cache' => array(),
  423. 'image' => array(),
  424. );
  425. return $engines;
  426. }
  427. /**
  428. * @} End of "Engine types".
  429. */
  430. /**
  431. * Command handler. Execute update.php code from drush.
  432. */
  433. function drush_core_updatedb() {
  434. if (drush_get_context('DRUSH_SIMULATE')) {
  435. drush_log(dt('updatedb command does not support --simulate option.'), LogLevel::OK);
  436. return TRUE;
  437. }
  438. drush_include_engine('drupal', 'update');
  439. if (update_main() === FALSE) {
  440. return FALSE;
  441. }
  442. // Clear all caches in a new process. We just performed major surgery.
  443. drush_drupal_cache_clear_all();
  444. drush_log(dt('Finished performing updates.'), LogLevel::OK);
  445. }
  446. /**
  447. * Command handler. List pending DB updates.
  448. */
  449. function drush_core_updatedb_status() {
  450. require_once DRUSH_DRUPAL_CORE . '/includes/install.inc';
  451. drupal_load_updates();
  452. drush_include_engine('drupal', 'update');
  453. list($pending, $start) = updatedb_status();
  454. if (empty($pending)) {
  455. drush_log(dt("No database updates required"), LogLevel::OK);
  456. }
  457. return $pending;
  458. }
  459. function _core_site_credentials($right_margin = 0) {
  460. // Leave some space on the right so that we can put the result into the
  461. // drush_log, which will again wordwrap the result.
  462. $original_columns = drush_get_context('DRUSH_COLUMNS', 80);
  463. drush_set_context('DRUSH_COLUMNS', $original_columns - $right_margin);
  464. $status_table = _core_site_status_table();
  465. $metadata = drush_get_command_format_metadata('core-status');
  466. $output = drush_format($status_table, $metadata, 'key-value');
  467. drush_set_context('DRUSH_COLUMNS', $original_columns);
  468. return $output;
  469. }
  470. function _core_path_aliases($project = '') {
  471. $paths = array();
  472. $site_wide = drush_drupal_sitewide_directory();
  473. if ($drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT')) {
  474. $paths['%root'] = $drupal_root;
  475. if ($site_root = drush_get_context('DRUSH_DRUPAL_SITE_ROOT')) {
  476. $paths['%site'] = $site_root;
  477. if (is_dir($modules_path = conf_path() . '/modules')) {
  478. $paths['%modules'] = $modules_path;
  479. }
  480. else {
  481. $paths['%modules'] = ltrim($site_wide . '/modules', '/');
  482. }
  483. if (is_dir($themes_path = conf_path() . '/themes')) {
  484. $paths['%themes'] = $themes_path;
  485. }
  486. else {
  487. $paths['%themes'] = ltrim($site_wide . '/themes', '/');
  488. }
  489. if (drush_drupal_major_version() >= 8 && drush_has_boostrapped(DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION)) {
  490. try {
  491. if (isset($GLOBALS['config_directories'])) {
  492. foreach ($GLOBALS['config_directories'] as $label => $unused) {
  493. $paths["%config-$label"] = config_get_config_directory($label);
  494. }
  495. }
  496. }
  497. catch (Exception $e) {
  498. // Nothing to do.
  499. }
  500. }
  501. if (drush_has_boostrapped(DRUSH_BOOTSTRAP_DRUPAL_FULL)) {
  502. $paths['%files'] = drush_file_get_public();
  503. if ($private_path = drush_file_get_private()) {
  504. $paths['%private'] = $private_path;
  505. }
  506. }
  507. if (function_exists('file_directory_temp')) {
  508. $paths['%temp'] = file_directory_temp();
  509. }
  510. // If the 'project' parameter was specified, then search
  511. // for a project (or a few) and add its path to the path list
  512. if (!empty($project)) {
  513. drush_include_engine('drupal', 'environment');
  514. $projects = array_merge(drush_get_modules(), drush_get_themes());
  515. foreach(explode(',', $project) as $target) {
  516. if (array_key_exists($target, $projects)) {
  517. $paths['%' . $target] = $drupal_root . '/' . _drush_extension_get_path($projects[$target]);
  518. }
  519. }
  520. }
  521. }
  522. }
  523. // Add in all of the global paths from $options['path-aliases']
  524. $paths = array_merge($paths, drush_get_option('path-aliases', array()));
  525. return $paths;
  526. }
  527. function _core_site_status_table($project = '') {
  528. $phase = drush_get_context('DRUSH_BOOTSTRAP_PHASE');
  529. if ($drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT')) {
  530. $status_table['drupal-version'] = drush_drupal_version();
  531. if ($site_root = drush_get_context('DRUSH_DRUPAL_SITE_ROOT')) {
  532. $status_table['uri'] = drush_get_context('DRUSH_URI');
  533. try {
  534. $sql = drush_sql_get_class();
  535. $db_spec = $sql->db_spec();
  536. $status_table['db-driver'] = $db_spec['driver'];
  537. if (!empty($db_spec['unix_socket'])) {
  538. $status_table['db-socket'] = $db_spec['unix_socket'];
  539. }
  540. elseif (isset($db_spec['host'])) {
  541. $status_table['db-hostname'] = $db_spec['host'];
  542. }
  543. $status_table['db-username'] = isset($db_spec['username']) ? $db_spec['username'] : NULL;
  544. $status_table['db-password'] = isset($db_spec['password']) ? $db_spec['password'] : NULL;
  545. $status_table['db-name'] = isset($db_spec['database']) ? $db_spec['database'] : NULL;
  546. $status_table['db-port'] = isset($db_spec['port']) ? $db_spec['port'] : NULL;
  547. if ($phase > DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION) {
  548. $boot_object = drush_get_bootstrap_object();
  549. $status_table['install-profile'] = $boot_object->get_profile();
  550. if ($phase > DRUSH_BOOTSTRAP_DRUPAL_FULL) {
  551. $status_table['bootstrap'] = dt('Successful');
  552. if ($phase == DRUSH_BOOTSTRAP_DRUPAL_LOGIN) {
  553. $status_table['user'] = drush_user_get_class()->getCurrentUserAsSingle()->getUsername();
  554. }
  555. }
  556. }
  557. }
  558. catch (Exception $e) {
  559. // Don't worry be happy.
  560. }
  561. }
  562. if (drush_has_boostrapped(DRUSH_BOOTSTRAP_DRUPAL_FULL)) {
  563. $status_table['theme'] = drush_theme_get_default();
  564. $status_table['admin-theme'] = drush_theme_get_admin();
  565. }
  566. }
  567. if ($php_bin = drush_get_option('php')) {
  568. $status_table['php-bin'] = $php_bin;
  569. }
  570. $status_table['php-os'] = PHP_OS;
  571. if ($php_ini_files = _drush_core_config_php_ini_files()) {
  572. $status_table['php-conf'] = $php_ini_files;
  573. }
  574. $status_table['drush-script'] = DRUSH_COMMAND;
  575. $status_table['drush-version'] = DRUSH_VERSION;
  576. $status_table['drush-temp'] = drush_find_tmp();
  577. $status_table['drush-conf'] = drush_flatten_array(drush_get_context_options('context-path', ''));
  578. $alias_files = _drush_sitealias_find_alias_files();
  579. $status_table['drush-alias-files'] = $alias_files;
  580. $paths = _core_path_aliases($project);
  581. if (!empty($paths)) {
  582. foreach ($paths as $target => $one_path) {
  583. $name = $target;
  584. if (substr($name,0,1) == '%') {
  585. $name = substr($name,1);
  586. }
  587. $status_table[$name] = $one_path;
  588. }
  589. }
  590. // Store the paths into the '%paths' index; this will be
  591. // used by other code, but will not be included in the output
  592. // of the drush status command.
  593. $status_table['%paths'] = $paths;
  594. return $status_table;
  595. }
  596. // Adjust the status output for any non-pipe output format
  597. function _drush_core_status_format_table_data($output, $metadata) {
  598. if (drush_get_option('full', FALSE) == FALSE) {
  599. // Hide any key that begins with a %
  600. foreach ($output as $key => $value) {
  601. if ($key[0] == '%') {
  602. unset($output[$key]);
  603. }
  604. }
  605. // Hide 'Modules path' and 'Themes path' as well
  606. unset($output['modules']);
  607. unset($output['themes']);
  608. // Shorten the list of alias files if there are too many
  609. if (isset($output['drush-alias-files']) && count($output['drush-alias-files']) > 24) {
  610. $msg = dt("\nThere are !count more alias files. Run with --full to see the full list.", array('!count' => count($output['drush-alias-files']) - 1));
  611. $output['drush-alias-files'] = array($output['drush-alias-files'][0] , $msg);
  612. }
  613. }
  614. return $output;
  615. }
  616. /**
  617. * Command callback. Runs all cron hooks.
  618. */
  619. function drush_core_cron() {
  620. if (drush_drupal_major_version() < 8) {
  621. $result = drupal_cron_run();
  622. }
  623. else {
  624. $result = \Drupal::service('cron')->run();
  625. }
  626. if ($result) {
  627. drush_log(dt('Cron run successful.'), LogLevel::SUCCESS);
  628. }
  629. else {
  630. return drush_set_error('DRUSH_CRON_FAILED', dt('Cron run failed.'));
  631. }
  632. }
  633. /**
  634. * Command callback. Edit drushrc and alias files.
  635. */
  636. function drush_core_config($filter = NULL) {
  637. $all = drush_core_config_load();
  638. // Apply any filter that was supplied.
  639. if ($filter) {
  640. foreach ($all as $key => $file) {
  641. if (strpos($file, $filter) === FALSE) {
  642. unset($all[$key]);
  643. }
  644. }
  645. }
  646. $all = drush_map_assoc(array_values($all));
  647. $exec = drush_get_editor();
  648. if (count($all) == 1) {
  649. $filepath = current($all);
  650. }
  651. else {
  652. $choice = drush_choice($all, 'Enter a number to choose which file to edit.', '!key');
  653. if (!$choice) {
  654. return drush_user_abort();
  655. }
  656. $filepath = $all[$choice];
  657. }
  658. return drush_shell_exec_interactive($exec, $filepath, $filepath);
  659. }
  660. /**
  661. * Command argument complete callback.
  662. *
  663. * @return
  664. * Array of available configuration files for editing.
  665. */
  666. function core_core_config_complete() {
  667. return array('values' => drush_core_config_load(FALSE));
  668. }
  669. function drush_core_config_load($headers = TRUE) {
  670. $php_header = $php = $rcs_header = $rcs = $aliases_header = $aliases = $drupal_header = $drupal = array();
  671. $php = _drush_core_config_php_ini_files();
  672. if (!empty($php)) {
  673. if ($headers) {
  674. $php_header = array('phpini' => '-- PHP ini files --');
  675. }
  676. }
  677. drush_sitealias_load_all();
  678. if ($rcs = drush_get_context_options('context-path', TRUE)) {
  679. if ($headers) {
  680. $rcs_header = array('drushrc' => '-- Drushrc --');
  681. }
  682. }
  683. if ($aliases = drush_get_context('drush-alias-files')) {
  684. if ($headers) {
  685. $aliases_header = array('aliases' => '-- Aliases --');
  686. }
  687. }
  688. if ($site_root = drush_get_context('DRUSH_DRUPAL_SITE_ROOT')) {
  689. $drupal = array_merge(array(realpath($site_root . '/settings.php'), realpath(DRUPAL_ROOT . '/.htaccess')));
  690. if ($headers) {
  691. $drupal_header = array('drupal' => '-- Drupal --');
  692. }
  693. }
  694. return array_merge($php_header, $php, $rcs_header, $rcs, $aliases_header, $aliases, $drupal_header, $drupal);
  695. }
  696. function _drush_core_config_php_ini_files() {
  697. $ini_files = array();
  698. $ini_files[] = php_ini_loaded_file();
  699. if ($drush_ini = getenv('DRUSH_INI')) {
  700. if (file_exists($drush_ini)) {
  701. $ini_files[] = $drush_ini;
  702. }
  703. }
  704. foreach (array(DRUSH_BASE_PATH, '/etc/drush', drush_server_home() . '/.drush') as $ini_dir) {
  705. if (file_exists($ini_dir . "/drush.ini")) {
  706. $ini_files[] = realpath($ini_dir . "/drush.ini");
  707. }
  708. }
  709. return array_unique($ini_files);
  710. }
  711. /**
  712. * Command callback. Provides information from the 'Status Reports' admin page.
  713. */
  714. function drush_core_requirements() {
  715. include_once DRUSH_DRUPAL_CORE . '/includes/install.inc';
  716. $severities = array(
  717. REQUIREMENT_INFO => t('Info'),
  718. REQUIREMENT_OK => t('OK'),
  719. REQUIREMENT_WARNING => t('Warning'),
  720. REQUIREMENT_ERROR => t('Error'),
  721. );
  722. drupal_load_updates();
  723. drush_include_engine('drupal', 'environment');
  724. $requirements = drush_module_invoke_all('requirements', 'runtime');
  725. // If a module uses "$requirements[] = " instead of
  726. // "$requirements['label'] = ", then build a label from
  727. // the title.
  728. foreach($requirements as $key => $info) {
  729. if (is_numeric($key)) {
  730. unset($requirements[$key]);
  731. $new_key = strtolower(str_replace(' ', '_', $info['title']));
  732. $requirements[$new_key] = $info;
  733. }
  734. }
  735. $ignore_requirements = drush_get_option_list('ignore');
  736. foreach ($ignore_requirements as $ignore) {
  737. unset($requirements[$ignore]);
  738. }
  739. ksort($requirements);
  740. $min_severity = drush_get_option('severity', -1);
  741. foreach($requirements as $key => $info) {
  742. $severity = array_key_exists('severity', $info) ? $info['severity'] : -1;
  743. $requirements[$key]['sid'] = $severity;
  744. $requirements[$key]['severity'] = $severities[$severity];
  745. if ($severity < $min_severity) {
  746. unset($requirements[$key]);
  747. }
  748. }
  749. return $requirements;
  750. }
  751. /**
  752. * Command callback. Provides a birds-eye view of the current Drupal
  753. * installation.
  754. */
  755. function drush_core_status() {
  756. $status_table = _core_site_status_table(drush_get_option('project',''));
  757. // If args are specified, filter out any entry that is not named
  758. // (in other words, only show lines named by one of the arg values)
  759. $args = func_get_args();
  760. if (!empty($args)) {
  761. $field_list = $args;
  762. $metadata = drush_get_command_format_metadata('core-status');
  763. foreach ($metadata['field-labels'] as $field_key => $field_label) {
  764. if (_drush_core_is_named_in_array($field_label, $args)) {
  765. $field_list[] = $field_key;
  766. }
  767. }
  768. foreach ($status_table as $key => $value) {
  769. if (!_drush_core_is_named_in_array($key, $field_list)) {
  770. unset($status_table[$key]);
  771. }
  772. }
  773. }
  774. return $status_table;
  775. }
  776. // Command callback. Show all global options. Exposed via topic command.
  777. function drush_core_global_options() {
  778. drush_print(dt('These options are applicable to most drush commands.'));
  779. drush_print();
  780. $fake = drush_global_options_command(FALSE);
  781. return drush_format_help_section($fake, 'options');
  782. }
  783. function _drush_core_is_named_in_array($key, $the_array) {
  784. $is_named = FALSE;
  785. $simplified_key = str_replace(array(' ', '_', '-'), array('', '', ''), $key);
  786. foreach ($the_array as $name) {
  787. if (stristr($simplified_key, str_replace(array(' ', '_', '-'), array('', '', ''), $name))) {
  788. $is_named = TRUE;
  789. }
  790. }
  791. return $is_named;
  792. }
  793. /**
  794. * Callback for core-quick-drupal command.
  795. */
  796. function drush_core_quick_drupal() {
  797. $requests = FALSE;
  798. $make_projects = array();
  799. $args = func_get_args();
  800. $name = drush_get_option('use-name');
  801. drush_set_option('backend', TRUE);
  802. drush_set_option('strict', FALSE); // We fail option validation because do so much internal drush_invoke().
  803. $makefile = drush_get_option('makefile');
  804. if (drush_get_option('use-existing', FALSE)) {
  805. $root = drush_get_context('DRUSH_SELECTED_DRUPAL_ROOT');
  806. if (!$root) {
  807. return drush_set_error('QUICK_DRUPAL_NO_ROOT_SPECIFIED', 'Must specify site with --root when using --use-existing.');
  808. }
  809. // If a --db-url was not explicitly provided, and if there is already
  810. // a settings.php file provided, then use the db-url defined inside it.
  811. if (!drush_get_option('db-url', FALSE)) {
  812. $values = drush_invoke_process('@self', 'site-alias', array('@self'), array('with-db-url' => TRUE), array('integrate' => FALSE, 'override-simulated' => TRUE));
  813. if (!empty($values['object']['self']['db-url'])) {
  814. drush_set_option('db-url', $values['object']['self']['db-url']);
  815. }
  816. }
  817. if (empty($name)) {
  818. $name = basename($root);
  819. }
  820. $base = dirname($root);
  821. }
  822. else {
  823. if (!empty($args) && empty($name)) {
  824. $name = array_shift($args);
  825. }
  826. if (empty($name)) {
  827. $name = 'quick-drupal-' . gmdate('YmdHis', $_SERVER['REQUEST_TIME']);
  828. }
  829. $root = drush_get_option('root', FALSE);
  830. $core = drush_get_option('core', 'drupal');
  831. $project_rename = $core;
  832. if ($root) {
  833. $base = dirname($root);
  834. $project_rename = basename($root);
  835. }
  836. else {
  837. $base = getcwd() . '/' . $name;
  838. $root = $base . '/' . $core;
  839. }
  840. if (!empty($makefile)) {
  841. // Invoke 'drush make $makefile'.
  842. $result = drush_invoke_process('@none', 'make', array($makefile, $root));
  843. if ($result['error_status'] != 0) {
  844. return drush_set_error('DRUSH_QD_MAKE', 'Could not make; aborting.');
  845. }
  846. $make_projects = array_diff(array_keys($result['object']['projects']), array('drupal'));
  847. }
  848. else {
  849. drush_mkdir($base);
  850. drush_set_option('destination', $base);
  851. drush_set_option('drupal-project-rename', $project_rename);
  852. if (drush_invoke('pm-download', array($core)) === FALSE) {
  853. return drush_set_error('QUICK_DRUPAL_CORE_DOWNLOAD_FAIL', 'Drupal core download/extract failed.');
  854. }
  855. }
  856. }
  857. if (!drush_get_option('db-url', FALSE)) {
  858. drush_set_option('db-url', 'sqlite:' . $base . '/' . $name . '.sqlite');
  859. }
  860. // We have just created a site root where one did not exist before.
  861. // We therefore must manually reset DRUSH_SELECTED_DRUPAL_ROOT to
  862. // our new root, and force a bootstrap to DRUSH_BOOTSTRAP_DRUPAL_ROOT.
  863. drush_set_context('DRUSH_SELECTED_DRUPAL_ROOT', $root);
  864. if (!drush_bootstrap_to_phase(DRUSH_BOOTSTRAP_DRUPAL_ROOT)) {
  865. return drush_set_error('QUICK_DRUPAL_ROOT_LOCATE_FAIL', 'Unable to locate Drupal root directory.');
  866. }
  867. if (!empty($args)) {
  868. $requests = pm_parse_arguments($args, FALSE);
  869. }
  870. if ($requests) {
  871. // Unset --destination, so that downloads go to the site directories.
  872. drush_unset_option('destination');
  873. if (drush_invoke('pm-download', $requests) === FALSE) {
  874. return drush_set_error('QUICK_DRUPAL_PROJECT_DOWNLOAD_FAIL', 'Project download/extract failed.');
  875. }
  876. }
  877. drush_invoke('site-install', array(drush_get_option('profile')));
  878. // Log in with the admin user.
  879. // TODO: If site-install is given a sites-subdir other than 'default',
  880. // then it will bootstrap to DRUSH_BOOTSTRAP_DRUPAL_SITE get the installer
  881. // to recognize the desired site directory. This somehow interferes
  882. // with our desire to bootstrap to DRUSH_BOOTSTRAP_DRUPAL_LOGIN here.
  883. // We could do the last few steps in a new process if uri is not 'default'.
  884. drush_set_option('user', '1');
  885. if (!drush_bootstrap_to_phase(DRUSH_BOOTSTRAP_DRUPAL_LOGIN)) {
  886. return drush_set_error('QUICK_DRUPAL_INSTALL_FAIL', 'Drupal core install failed.');
  887. }
  888. $enable = array_merge(pm_parse_arguments(drush_get_option('enable', $requests)), $make_projects);
  889. if (!empty($enable)) {
  890. if (drush_invoke('pm-enable', $enable) === FALSE) {
  891. return drush_set_error('QUICK_DRUPAL_PROJECT_ENABLE_FAIL', 'Project enable failed.');
  892. }
  893. }
  894. $server = drush_get_option('server', '/');
  895. if ($server) {
  896. $server_uri = runserver_uri($server);
  897. _drush_core_qd_cache_uri($server_uri);
  898. }
  899. if (!drush_get_option('no-server', FALSE)) {
  900. if ($server) {
  901. // Current CLI user is also the web server user, which is for development
  902. // only. Hence we can safely make the site directory writable. This makes
  903. // it easier to delete and edit settings.php.
  904. @chmod(conf_path(), 0700);
  905. drush_invoke('runserver', array($server));
  906. }
  907. }
  908. else {
  909. drush_print(dt('Login URL: ') . drush_invoke('user-login'));
  910. }
  911. }
  912. // Write a drushrc.php to cache the server information for future Drush calls
  913. function _drush_core_qd_cache_uri($uri) {
  914. $server = $uri['host'];
  915. if (!empty($uri["port"])) {
  916. $server .= ':' . $uri["port"];
  917. }
  918. $dir = DRUPAL_ROOT . '/drush';
  919. $target_file = $dir . '/drushrc.php';
  920. drush_log(dt("Caching 'uri' !uri in !target", array('!uri' => $server, '!target' => $target_file)), LogLevel::OK);
  921. $create_file = TRUE;
  922. if (file_exists($target_file)) {
  923. // Don't bother to ask with --use-existing; just
  924. // continue.
  925. if (drush_get_option('use-existing', FALSE)) {
  926. $create_file = FALSE;
  927. }
  928. else {
  929. $create_file = drush_confirm(dt('!target already exists. Overwrite?', array('!target' => $target_file)));
  930. }
  931. }
  932. $content = <<<EOT
  933. <?php
  934. // Inserted by `drush quick-drupal`. This allows `drush user-login`
  935. // and similar commands to work seemlessly. Remove if using
  936. // Drupal multisite feature.
  937. \$options['uri'] = "$server";
  938. EOT;
  939. if ($create_file) {
  940. drush_mkdir($dir);
  941. file_put_contents($target_file, $content);
  942. }
  943. }
  944. /**
  945. * Include options and engines for core-quick-drupal command, aggregated from
  946. * other command options that are available. We prefix option descriptons,
  947. * to make the long list more navigable.
  948. *
  949. * @param $items
  950. * The core commandfile command array, by reference. Used to include
  951. * site-install options and add options and engines for core-quick-drupal.
  952. */
  953. function drush_core_quick_drupal_options(&$items) {
  954. $options = array(
  955. 'core' => 'Drupal core to download. Defaults to "drupal" (latest stable version).',
  956. 'use-existing' => 'Use an existing Drupal root, specified with --root. Overrides --core.',
  957. 'profile' => 'The install profile to use. Defaults to standard.',
  958. 'enable' => 'Specific extensions (modules or themes) to enable. By default, extensions with the same name as requested projects will be enabled automatically.',
  959. 'server' => 'Host IP address and port number to bind to and path to open in web browser (hyphen to clear a default path), all elements optional. See runserver examples for shorthand.',
  960. 'no-server' => 'Avoid starting runserver (and browser) for the created Drupal site.',
  961. 'browser' => 'Optional name of a browser to open site in. If omitted the OS default browser will be used. Set --no-browser to disable.',
  962. 'use-name' => array('hidden' => TRUE, 'description' => 'Overrides "name" argument.'),
  963. 'makefile' => array('description' => 'Makefile to use. Makefile must specify which version of Drupal core to build.', 'example-value' => 'mysite.make', 'value' => 'optional'),
  964. 'root' => array('description' => 'Path to Drupal root.', 'example-value' => '/path/to/root', 'value' => 'optional'),
  965. );
  966. $pm = pm_drush_command();
  967. foreach ($pm['pm-download']['options'] as $option => $description) {
  968. if (is_array($description)) {
  969. $description = $description['description'];
  970. }
  971. $options[$option] = 'Download option: ' . $description;
  972. }
  973. // Unset a few options that are not usable here, as we control them ourselves
  974. // or they are otherwise implied by the environment.
  975. unset($options['destination']);
  976. unset($options['drupal-project-rename']);
  977. unset($options['default-major']);
  978. unset($options['use-site-dir']);
  979. $si = site_install_drush_command();
  980. foreach ($si['site-install']['options'] as $option => $description) {
  981. if (is_array($description)) {
  982. $description = $description['description'];
  983. }
  984. $options[$option] = 'Site install option: ' . $description;
  985. }
  986. unset($options['sites-subdir']);
  987. $runserver = runserver_drush_command();
  988. foreach ($runserver['runserver']['options'] as $option => $description) {
  989. $options[$option] = 'Runserver option: ' . $description;
  990. }
  991. unset($options['user']);
  992. $items['core-quick-drupal']['options'] = $options;
  993. $items['core-quick-drupal']['engines'] = $pm['pm-download']['engines'];
  994. }
  995. /**
  996. * Command callback. Runs "naked" php scripts
  997. * and drush "shebang" scripts ("#!/usr/bin/env drush").
  998. */
  999. function drush_core_php_script() {
  1000. $found = FALSE;
  1001. $script = NULL;
  1002. if ($args = func_get_args()) {
  1003. $script = $args[0];
  1004. }
  1005. if ($script == '-') {
  1006. return eval(stream_get_contents(STDIN));
  1007. }
  1008. elseif (file_exists($script)) {
  1009. $found = $script;
  1010. }
  1011. else {
  1012. // Array of paths to search for scripts
  1013. $searchpath['DIR'] = dirname(__FILE__);
  1014. $searchpath['cwd'] = drush_cwd();
  1015. // Additional script paths, specified by 'script-path' option
  1016. if ($script_path = drush_get_option('script-path', FALSE)) {
  1017. foreach (explode(PATH_SEPARATOR, $script_path) as $path) {
  1018. $searchpath[] = $path;
  1019. }
  1020. }
  1021. drush_log(dt('Searching for scripts in ') . implode(',', $searchpath), LogLevel::DEBUG);
  1022. if (!isset($script)) {
  1023. // List all available scripts.
  1024. $all = array();
  1025. foreach($searchpath as $key => $path) {
  1026. $recurse = !(($key == 'cwd') || ($path == '/'));
  1027. $all = array_merge( $all , array_keys(drush_scan_directory($path, '/\.php$/', array('.', '..', 'CVS'), NULL, $recurse)) );
  1028. }
  1029. drush_print(implode("\n", $all));
  1030. }
  1031. else {
  1032. // Execute the specified script.
  1033. foreach($searchpath as $path) {
  1034. $script_filename = $path . '/' . $script;
  1035. if (file_exists($script_filename . '.php')) {
  1036. $script_filename .= '.php';
  1037. }
  1038. if (file_exists($script_filename)) {
  1039. $found = $script_filename;
  1040. break;
  1041. }
  1042. $all[] = $script_filename;
  1043. }
  1044. if (!$found) {
  1045. return drush_set_error('DRUSH_TARGET_NOT_FOUND', dt('Unable to find any of the following: @files', array('@files' => implode(', ', $all))));
  1046. }
  1047. }
  1048. }
  1049. if ($found) {
  1050. // Set the DRUSH_SHIFT_SKIP to two; this will cause
  1051. // drush_shift to skip the next two arguments the next
  1052. // time it is called. This allows scripts to get all
  1053. // arguments, including the 'php-script' and script
  1054. // pathname, via drush_get_arguments(), or it can process
  1055. // just the arguments that are relevant using drush_shift().
  1056. drush_set_context('DRUSH_SHIFT_SKIP', 2);
  1057. if (_drush_core_eval_shebang_script($found) === FALSE) {
  1058. return include($found);
  1059. }
  1060. }
  1061. }
  1062. function drush_core_php_eval($php) {
  1063. return eval($php . ';');
  1064. }
  1065. /**
  1066. * Evaluate a script that begins with #!drush php-script
  1067. */
  1068. function _drush_core_eval_shebang_script($script_filename) {
  1069. $found = FALSE;
  1070. $fp = fopen($script_filename, "r");
  1071. if ($fp !== FALSE) {
  1072. $line = fgets($fp);
  1073. if (_drush_is_drush_shebang_line($line)) {
  1074. $first_script_line = '';
  1075. while ($line = fgets($fp)) {
  1076. $line = trim($line);
  1077. if ($line == '<?php') {
  1078. $found = TRUE;
  1079. break;
  1080. }
  1081. elseif (!empty($line)) {
  1082. $first_script_line = $line . "\n";
  1083. break;
  1084. }
  1085. }
  1086. $script = stream_get_contents($fp);
  1087. // Pop off the first two arguments, the
  1088. // command (php-script) and the path to
  1089. // the script to execute, as a service
  1090. // to the script.
  1091. eval($first_script_line . $script);
  1092. $found = TRUE;
  1093. }
  1094. fclose($fp);
  1095. }
  1096. return $found;
  1097. }
  1098. /**
  1099. * Process sets from the specified batch.
  1100. *
  1101. * This is the default batch processor that will be used if the $command parameter
  1102. * to drush_backend_batch_process() has not been specified.
  1103. */
  1104. function drush_core_batch_process($id) {
  1105. drush_batch_command($id);
  1106. }
  1107. /**
  1108. * Process outstanding updates during updatedb.
  1109. *
  1110. * This is a batch processing command that makes use of the drush_backend_invoke
  1111. * api.
  1112. *
  1113. * This command includes the version specific update engine, which correctly
  1114. * initialises the environment to be able to successfully handle minor and major
  1115. * upgrades.
  1116. */
  1117. function drush_core_updatedb_batch_process($id) {
  1118. drush_include_engine('drupal', 'update');
  1119. _update_batch_command($id);
  1120. }
  1121. /**
  1122. * Given a target (e.g. @site:%modules), return the evaluated directory path.
  1123. *
  1124. * @param $target
  1125. * The target to evaluate. Can be @site or /path or @site:path
  1126. * or @site:%pathalias, etc. (just like rsync parameters)
  1127. * @param $component
  1128. * The portion of the evaluated path to return. Possible values:
  1129. * 'path' - the full path to the target (default)
  1130. * 'name' - the name of the site from the path (e.g. @site1)
  1131. * 'user-path' - the part after the ':' (e.g. %modules)
  1132. * 'root' & 'uri' - the Drupal root and URI of the site from the path
  1133. * 'path-component' - The ':' and the path
  1134. */
  1135. function _drush_core_directory($target = 'root', $component = 'path', $local_only = FALSE) {
  1136. // Normalize to a sitealias in the target.
  1137. $normalized_target = $target;
  1138. if (strpos($target, ':') === FALSE) {
  1139. if (substr($target, 0, 1) != '@') {
  1140. // drush_sitealias_evaluate_path() requires bootstrap to database.
  1141. if (!drush_bootstrap_to_phase(DRUSH_BOOTSTRAP_DRUPAL_DATABASE)) {
  1142. return drush_set_error('DRUPAL_SITE_NOT_FOUND', dt('You need to specify an alias or run this command within a drupal site.'));
  1143. }
  1144. $normalized_target = '@self:';
  1145. if (substr($target, 0, 1) != '%') {
  1146. $normalized_target .= '%';
  1147. }
  1148. $normalized_target .= $target;
  1149. }
  1150. }
  1151. $additional_options = array();
  1152. $values = drush_sitealias_evaluate_path($normalized_target, $additional_options, $local_only);
  1153. if (isset($values[$component])) {
  1154. // Hurray, we found the destination.
  1155. return $values[$component];
  1156. }
  1157. }
  1158. /**
  1159. * Command callback.
  1160. */
  1161. function drush_core_drupal_directory($target = 'root') {
  1162. $path = _drush_core_directory($target, drush_get_option('component', 'path'), drush_get_option('local-only', FALSE));
  1163. // If _drush_core_directory is working right, it will turn
  1164. // %blah into the path to the item referred to by the key 'blah'.
  1165. // If there is no such key, then no replacement is done. In the
  1166. // case of the dd command, we will consider it an error if
  1167. // any keys are -not- replaced in _drush_core_directory.
  1168. if ($path && (strpos($path, '%') === FALSE)) {
  1169. return $path;
  1170. }
  1171. else {
  1172. return drush_set_error('DRUSH_TARGET_NOT_FOUND', dt("Target '!target' not found.", array('!target' => $target)));
  1173. }
  1174. }
  1175. /**
  1176. * Called for `drush version` or `drush --version`
  1177. */
  1178. function drush_core_version() {
  1179. return DRUSH_VERSION;
  1180. }
  1181. /**
  1182. * Command callback. Execute specified shell code. Often used by shell aliases
  1183. * that start with !.
  1184. */
  1185. function drush_core_execute() {
  1186. $result = TRUE;
  1187. $escape = drush_get_option('escape', TRUE);
  1188. // Get all of the args and options that appear after the command name.
  1189. $args = drush_get_original_cli_args_and_options();
  1190. if ($escape) {
  1191. for ($x = 0; $x < count($args); $x++) {
  1192. // escape all args except for command separators.
  1193. if (!in_array($args[$x], array('&&', '||', ';'))) {
  1194. $args[$x] = drush_escapeshellarg($args[$x]);
  1195. }
  1196. }
  1197. }
  1198. $cmd = implode(' ', $args);
  1199. // If we selected a Drupal site, then cwd to the site root prior to exec
  1200. $cwd = FALSE;
  1201. if ($selected_root = drush_get_context('DRUSH_SELECTED_DRUPAL_ROOT')) {
  1202. if (is_dir($selected_root)) {
  1203. $cwd = getcwd();
  1204. drush_op('chdir', $selected_root);
  1205. }
  1206. }
  1207. if ($alias = drush_get_context('DRUSH_TARGET_SITE_ALIAS')) {
  1208. $site = drush_sitealias_get_record($alias);
  1209. if (!empty($site['site-list'])) {
  1210. $sites = drush_sitealias_resolve_sitelist($site);
  1211. foreach ($sites as $site_name => $site_spec) {
  1212. $result = _drush_core_execute_cmd($site_spec, $cmd);
  1213. if (!$result) {
  1214. break;
  1215. }
  1216. }
  1217. }
  1218. else {
  1219. $result = _drush_core_execute_cmd($site, $cmd);
  1220. }
  1221. }
  1222. else {
  1223. // Must be a local command.
  1224. $result = (drush_shell_proc_open($cmd) == 0);
  1225. }
  1226. // Restore the cwd if we changed it
  1227. if ($cwd) {
  1228. drush_op('chdir', $selected_root);
  1229. }
  1230. if (!$result) {
  1231. return drush_set_error('CORE_EXECUTE_FAILED', dt("Command !command failed.", array('!command' => $cmd)));
  1232. }
  1233. return $result;
  1234. }
  1235. /**
  1236. * Helper function for drush_core_execute: run one shell command
  1237. */
  1238. function _drush_core_execute_cmd($site, $cmd) {
  1239. if (!empty($site['remote-host'])) {
  1240. // Remote, so execute an ssh command with a bash fragment at the end.
  1241. $exec = drush_shell_proc_build($site, $cmd, TRUE);
  1242. return (drush_shell_proc_open($exec) == 0);
  1243. }
  1244. elseif (!empty($site['root']) && is_dir($site['root'])) {
  1245. return (drush_shell_proc_open('cd ' . drush_escapeshellarg($site['root']) . ' && ' . $cmd) == 0);
  1246. }
  1247. return (drush_shell_proc_open($cmd) == 0);
  1248. }