domain.drush.inc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. <?php
  2. /**
  3. * @file
  4. * Drush commands for Domain Access.
  5. */
  6. use Drupal\Component\Utility\Html;
  7. use Drupal\domain\DomainInterface;
  8. use GuzzleHttp\Exception\RequestException;
  9. /**
  10. * Implements hook_drush_command().
  11. */
  12. function domain_drush_command() {
  13. $items = array();
  14. $items['domain-list'] = array(
  15. 'description' => 'List active domains for the site.',
  16. 'examples' => array(
  17. 'drush domain-list',
  18. 'drush domains',
  19. ),
  20. 'aliases' => array('domains'),
  21. );
  22. $items['domain-add'] = array(
  23. 'description' => 'Add a new domain to the site.',
  24. 'examples' => array(
  25. 'drush domain-add example.com \'My Test Site\'',
  26. 'drush domain-add example.com \'My Test Site\' --inactive=1 --https==1',
  27. 'drush domain-add example.com \'My Test Site\' --weight=10',
  28. 'drush domain-add example.com \'My Test Site\' --validate=1',
  29. ),
  30. 'arguments' => array(
  31. 'hostname' => 'The domain hostname to register (e.g. example.com).',
  32. 'name' => 'The name of the site (e.g. Domain Two).',
  33. ),
  34. 'options' => array(
  35. 'inactive' => 'Set the domain to inactive status if set.',
  36. 'https' => 'Use https protocol for this domain if set.',
  37. 'weight' => 'Set the order (weight) of the domain.',
  38. 'is_default' => 'Set this domain as the default domain.',
  39. 'validate' => 'Force a check of the URL response before allowing registration.',
  40. ),
  41. );
  42. $items['domain-delete'] = array(
  43. 'description' => 'Delete a domain from the site.',
  44. 'examples' => array(
  45. 'drush domain-delete example.com',
  46. 'drush domain-delete 1',
  47. ),
  48. 'arguments' => array(
  49. 'domain' => 'The numeric id or hostname of the domain to delete.',
  50. ),
  51. );
  52. $items['domain-test'] = array(
  53. 'description' => 'Tests domains for proper response. If run from a subfolder, you must specify the --uri.',
  54. 'examples' => array(
  55. 'drush domain-test',
  56. 'drush domain-test example.com',
  57. 'drush domain-test 1',
  58. ),
  59. 'arguments' => array(
  60. 'domain_id' => 'The numeric id or hostname of the domain to test. If no value is passed, all domains are tested.',
  61. ),
  62. 'options' => array(
  63. 'base_path' => 'The subdirectory name if Drupal is installed in a folder other than server root.',
  64. ),
  65. );
  66. $items['domain-default'] = array(
  67. 'description' => 'Sets the default domain. If run from a subfolder, you must specify the --uri.',
  68. 'examples' => array(
  69. 'drush domain-default example.com',
  70. 'drush domain-default 1',
  71. 'drush domain-default 1 --validate=1',
  72. ),
  73. 'arguments' => array(
  74. 'domain_id' => 'The numeric id or hostname of the domain to make default.',
  75. ),
  76. 'options' => array(
  77. 'validate' => 'Force a check of the URL response before allowing registration.',
  78. ),
  79. );
  80. $items['domain-disable'] = array(
  81. 'description' => 'Sets a domain status to off.',
  82. 'examples' => array(
  83. 'drush domain-disable example.com',
  84. 'drush domain-disable 1',
  85. ),
  86. 'arguments' => array(
  87. 'domain_id' => 'The numeric id or hostname of the domain to disable.',
  88. ),
  89. );
  90. $items['domain-enable'] = array(
  91. 'description' => 'Sets a domain status to on.',
  92. 'examples' => array(
  93. 'drush domain-disable example.com',
  94. 'drush domain-disable 1',
  95. ),
  96. 'arguments' => array(
  97. 'domain_id' => 'The numeric id or hostname of the domain to enable.',
  98. ),
  99. );
  100. $items['domain-name'] = array(
  101. 'description' => 'Changes a domain label.',
  102. 'examples' => array(
  103. 'drush domain-name example.com Foo',
  104. 'drush domain-name 1 Foo',
  105. ),
  106. 'arguments' => array(
  107. 'domain_id' => 'The numeric id or hostname of the domain to relabel.',
  108. 'name' => 'The name to use for the domain.',
  109. ),
  110. );
  111. $items['domain-machine-name'] = array(
  112. 'description' => 'Changes a domain name.',
  113. 'examples' => array(
  114. 'drush domain-machine-name example.com foo',
  115. 'drush domain-machine-name 1 foo',
  116. ),
  117. 'arguments' => array(
  118. 'domain_id' => 'The numeric id or hostname of the domain to rename.',
  119. 'name' => 'The machine-readable name to use for the domain.',
  120. ),
  121. );
  122. $items['domain-scheme'] = array(
  123. 'description' => 'Changes a domain scheme.',
  124. 'examples' => array(
  125. 'drush domain-scheme example.com https',
  126. 'drush domain-scheme 1 https',
  127. ),
  128. 'arguments' => array(
  129. 'domain_id' => 'The numeric id or hostname of the domain to change.',
  130. 'scheme' => 'The URL schema (http or https) to use for the domain.',
  131. ),
  132. );
  133. $items['generate-domains'] = array(
  134. 'description' => 'Generate domains for testing.',
  135. 'arguments' => array(
  136. 'primary' => 'The primary domain to use. This will be created and used for *.example.com hostnames.',
  137. ),
  138. 'options' => array(
  139. 'count' => 'The count of extra domains to generate. Default is 15.',
  140. 'empty' => 'Pass empty=1 to truncate the {domain} table before creating records.'
  141. ),
  142. 'examples' => array(
  143. 'drush domain-generate example.com',
  144. 'drush domain-generate example.com --count=25',
  145. 'drush domain-generate example.com --count=25 --empty=1',
  146. 'drush gend',
  147. 'drush gend --count=25',
  148. 'drush gend --count=25 --empty=1',
  149. ),
  150. 'aliases' => array('gend'),
  151. );
  152. return $items;
  153. }
  154. /**
  155. * Implements hook_drush_help().
  156. */
  157. function domain_drush_help($section) {
  158. $items = domain_drush_command();
  159. $name = str_replace('domain:', '', $section);
  160. if (isset($items[$name])) {
  161. return dt($items[$name]['description']);
  162. }
  163. }
  164. /**
  165. * Shows the domain list.
  166. */
  167. function drush_domain_list() {
  168. $domains = \Drupal::service('entity_type.manager')->getStorage('domain')->loadMultipleSorted(NULL, TRUE);
  169. if (empty($domains)) {
  170. drush_print(dt('No domains have been created. Use drush domain-add to create one.'));
  171. return;
  172. }
  173. $header = array(
  174. 'weight' => dt('Weight'),
  175. 'name' => dt('Name'),
  176. 'hostname' => dt('Hostname'),
  177. 'scheme' => dt('Scheme'),
  178. 'status' => dt('Status'),
  179. 'is_default' => dt('Default'),
  180. 'domain_id' => dt('Domain Id'),
  181. 'id' => dt('Machine name'),
  182. );
  183. $rows = array(array_values($header));
  184. foreach ($domains as $domain) {
  185. $row = array();
  186. foreach ($header as $key => $name) {
  187. $row[] = Html::escape($domain->get($key));
  188. }
  189. $rows[] = $row;
  190. }
  191. drush_print_table($rows, TRUE);
  192. }
  193. /**
  194. * Generates a list of domains for testing.
  195. *
  196. * In my environment, I name hostnames one.* two.* up to ten. I also use
  197. * foo.* bar.* and baz.*. We also want a non-hostname here and use
  198. * myexample.com.
  199. *
  200. * The script may also add test1, test2, test3 up to any number to test a
  201. * large number of domains. This test is mostly for UI testing.
  202. *
  203. * @param $primary
  204. * The root domain to use for domain creation.
  205. *
  206. * @return
  207. * A list of the domains created.
  208. */
  209. function drush_domain_generate_domains($primary = 'example.com') {
  210. // Check the number of domains to create.
  211. $count = drush_get_option('count');
  212. $domains = \Drupal::service('entity_type.manager')->getStorage('domain')->loadMultiple(NULL, TRUE);
  213. if (empty($count)) {
  214. $count = 15;
  215. }
  216. // Ensure we don't duplicate any domains.
  217. $existing = array();
  218. if (!empty($domains)) {
  219. foreach ($domains as $domain) {
  220. $existing[] = $domain->getHostname();
  221. }
  222. }
  223. // Set up one.* and so on.
  224. $names = array(
  225. 'one',
  226. 'two',
  227. 'three',
  228. 'four',
  229. 'five',
  230. 'six',
  231. 'seven',
  232. 'eight',
  233. 'nine',
  234. 'ten',
  235. 'foo',
  236. 'bar',
  237. 'baz',
  238. );
  239. // Set the creation array.
  240. $new = array($primary);
  241. foreach ($names as $name) {
  242. $new[] = $name . '.' . $primary;
  243. }
  244. // Include a non hostname.
  245. $new[] = 'my' . $primary;
  246. // Filter against existing so we can count correctly.
  247. $prepared = array();
  248. foreach ($new as $key => $value) {
  249. if (!in_array($value, $existing)) {
  250. $prepared[] = $value;
  251. }
  252. }
  253. // Add any test domains that have numeric prefixes. We don't expect these URLs to work,
  254. // and mainly use these for testing the user interface.
  255. $needed = $count - count($prepared);
  256. for ($i = 1; $i <= $needed; $i++) {
  257. $prepared[] = 'test' . $i . '.' . $primary;
  258. }
  259. // Get the initial item weight for sorting.
  260. $start_weight = count($domains);
  261. $prepared = array_slice($prepared, 0, $count);
  262. // Create the domains.
  263. foreach ($prepared as $key => $item) {
  264. $hostname = strtolower($item);
  265. $values = array(
  266. 'name' => ($item != $primary) ? ucwords(str_replace(".$primary", '', $item)) : \Drupal::config('system.site')->get('name'),
  267. 'hostname' => $hostname,
  268. 'scheme' => 'http',
  269. 'status' => 1,
  270. 'weight' => ($item != $primary) ? $key + $start_weight + 1 : -1,
  271. 'is_default' => 0,
  272. 'id' => \Drupal::service('entity_type.manager')->getStorage('domain')->createMachineName($hostname),
  273. );
  274. $domain = \Drupal::service('entity_type.manager')->getStorage('domain')->create($values);
  275. domain_drush_create($domain);
  276. }
  277. // If nothing created, say so.
  278. if (empty($new)) {
  279. drush_print(dt('No new domains were created.'));
  280. }
  281. }
  282. /**
  283. * Validates the domain generation script.
  284. *
  285. * @param $primary
  286. * The root domain to use for domain creation.
  287. */
  288. function drush_domain_generate_domains_validate($primary = 'example.com') {
  289. if ($empty = drush_get_option('empty')) {
  290. db_query("TRUNCATE TABLE {domain}");
  291. }
  292. return;
  293. // TODO: Update this validation.
  294. $error = domain_valid_domain($primary);
  295. if (!empty($error)) {
  296. return drush_set_error('domain', $error);
  297. }
  298. }
  299. /**
  300. * Adds a new domain.
  301. *
  302. * @param string $hostname
  303. * The domain name to register.
  304. * @param string $name
  305. * The name to use for this domain.
  306. */
  307. function drush_domain_add($hostname, $name) {
  308. $records_count = \Drupal::entityTypeManager()->getStorage('domain')->getQuery()->count()->execute();
  309. $start_weight = $records_count + 1;
  310. $hostname = strtolower($hostname);
  311. /** @var \Drupal\domain\DomainStorageInterface $domain_storage */
  312. $domain_storage = \Drupal::service('entity_type.manager')->getStorage('domain');
  313. $values = array(
  314. 'hostname' => $hostname,
  315. 'name' => $name,
  316. 'status' => (!drush_get_option('invalid')) ? 1 : 0,
  317. 'scheme' => (!drush_get_option('https')) ? 'http' : 'https',
  318. 'weight' => ($weight = drush_get_option('weight')) ? $weight : $start_weight + 1,
  319. 'is_default' => ($is_default = drush_get_option('is_default')) ? $is_default : 0,
  320. 'id' => $domain_storage->createMachineName($hostname),
  321. 'validate_url' => (drush_get_option('validate')) ? 1 : 0,
  322. );
  323. $domain = $domain_storage->create($values);
  324. domain_drush_create($domain);
  325. }
  326. /**
  327. * Validates the domain add command arguments.
  328. *
  329. * @param string $hostname
  330. * The domain name to register.
  331. * @param string $name
  332. * The name to use for this domain.
  333. *
  334. * @return bool
  335. * TRUE when validation passed, FALSE otherwise.
  336. */
  337. function drush_domain_add_validate($hostname, $name) {
  338. $errors = domain_drush_validate_domain($hostname);
  339. if (!empty($errors)) {
  340. return drush_set_error('domain', $errors);
  341. }
  342. elseif (\Drupal::service('entity_type.manager')->getStorage('domain')->loadByHostname($hostname)) {
  343. return drush_set_error('domain', dt('The hostname is already registered.'));
  344. }
  345. return TRUE;
  346. }
  347. /**
  348. * Creates a domain record.
  349. *
  350. * @param Drupal\domain\DomainInterface $domain
  351. * A domain entity.
  352. */
  353. function domain_drush_create(DomainInterface $domain) {
  354. if ($error = domain_drush_check_response($domain)) {
  355. drush_set_error('hostname', $error);
  356. }
  357. else {
  358. $domain->save();
  359. if ($domain->getDomainId()) {
  360. drush_print(dt('Created @name at @domain.', array('@name' => $domain->label(), '@domain' => $domain->getHostname())));
  361. }
  362. else {
  363. drush_print(dt('The request could not be completed.'));
  364. }
  365. }
  366. }
  367. /**
  368. * Runs a check to ensure that the domain is responsive.
  369. *
  370. * @param Drupal\domain\DomainInterface $domain
  371. * A domain entity.
  372. *
  373. * @return string
  374. * An error message if the domain url does not validate. Else empty.
  375. */
  376. function domain_drush_check_response(DomainInterface $domain) {
  377. // Check the domain response. First, clear the path value.
  378. if ($domain->validate_url) {
  379. $domain->setPath();
  380. try {
  381. $response = $domain->getResponse();
  382. }
  383. // We cannot know which Guzzle Exception class will be returned; be generic.
  384. catch (RequestException $e) {
  385. watchdog_exception('domain', $e);
  386. // File a general server failure.
  387. $domain->setResponse(500);
  388. }
  389. // If validate_url is set, then we must receive a 200 response.
  390. if ($domain->getResponse() != 200) {
  391. if (empty($response)) {
  392. $response = 500;
  393. }
  394. return dt('The server request to @url returned a @response response. To proceed, disable the test of the server response by leaving off the --validate flag.', ['@url' => $domain->getPath(), '@response' => $response]);
  395. }
  396. }
  397. }
  398. /**
  399. * Validates a domain.
  400. *
  401. * @param $hostname
  402. * The domain name to validate for syntax and uniqueness.
  403. *
  404. * @return array
  405. * An array of errors encountered.
  406. *
  407. * @see domain_validate()
  408. */
  409. function domain_drush_validate_domain($hostname) {
  410. /** @var \Drupal\domain\DomainValidatorInterface $validator */
  411. $validator = \Drupal::service('domain.validator');
  412. return $validator->validate($hostname);
  413. }
  414. /**
  415. * Deletes a domain record.
  416. *
  417. * @param $argument
  418. * The domain_id to delete. Pass 'all' to delete all records.
  419. */
  420. function drush_domain_delete($argument = NULL) {
  421. if (is_null($argument)) {
  422. drush_set_error('domain', dt('You must specify a domain to delete.'));
  423. }
  424. if ($argument == 'all') {
  425. $domains = \Drupal::service('entity_type.manager')->getStorage('domain')->loadMultiple(NULL, TRUE);
  426. if (empty($domains)) {
  427. drush_print(dt('There are no domains to delete.'));
  428. return;
  429. }
  430. $content = drush_choice(array(1 => dt('Delete all domains')), dt('This action may not be undone. Continue?'), '!value');
  431. if (empty($content)) {
  432. return;
  433. }
  434. }
  435. // Resolve the domain.
  436. elseif ($domain = drush_domain_get_from_argument($argument)) {
  437. if ($domain->isDefault()) {
  438. return drush_set_error('domain', dt('The primary domain may not be deleted. Use drush domain-default to set a new default domain.'));
  439. }
  440. $domains = [$domain];
  441. }
  442. else {
  443. return;
  444. }
  445. foreach ($domains as $domain) {
  446. $domain->delete();
  447. drush_print(dt('Domain record @domain deleted.', array('@domain' => $domain->getHostname())));
  448. }
  449. return;
  450. // TODO: Set options for re-assigning content.
  451. $list = \Drupal::service('entity_type.manager')->getStorage('domain')->loadMultiple(NULL, TRUE);
  452. $options = array('0' => dt('Do not reassign'));
  453. foreach ($list as $data) {
  454. if ($data->id() != $domain->id()) {
  455. $options[$data->getDomainId()] = $data->getHostname();
  456. }
  457. }
  458. $content = drush_choice($options, dt('Reassign content to:'), '!value');
  459. if (empty($content)) {
  460. return;
  461. }
  462. $users = drush_choice($options, dt('Reassign users to:'), '!value');
  463. if (empty($users)) {
  464. return;
  465. }
  466. $values['domain_access'] = (!empty($content)) ? $content : 'none';
  467. $values['domain_editor'] = (!empty($content)) ? $users : 'none';
  468. domain_delete($domain, $values);
  469. drush_print(dt('Domain record deleted.'));
  470. }
  471. /**
  472. * Tests a domain record for the proper HTTP response.
  473. *
  474. * @param $argument
  475. * The domain_id to test. Passing no value tests all records.
  476. */
  477. function drush_domain_test($argument = NULL) {
  478. // TODO: This won't work in a subdirectory without a parameter.
  479. if ($base_path = drush_get_option('base_path')) {
  480. $GLOBALS['base_path'] = '/' . $base_path . '/';
  481. }
  482. if (is_null($argument)) {
  483. $domains = \Drupal::service('entity_type.manager')->getStorage('domain')->loadMultiple(NULL, TRUE);
  484. }
  485. else {
  486. if ($domain = drush_domain_get_from_argument($argument)) {
  487. $domains = array($domain);
  488. }
  489. else {
  490. return;
  491. }
  492. }
  493. foreach ($domains as $domain) {
  494. if ($domain->getResponse() != 200) {
  495. drush_print(dt('Fail: !error. Please pass a --uri parameter or a --base_path to retest.' , array('!error' => $domain->getResponse())));
  496. }
  497. else {
  498. drush_print(dt('Success: !url tested successfully.', array('!url' => $domain->getPath())));
  499. }
  500. }
  501. }
  502. /**
  503. * Sets the default domain id.
  504. */
  505. function drush_domain_default($argument) {
  506. // Resolve the domain.
  507. if ($domain = drush_domain_get_from_argument($argument)) {
  508. $validate = (drush_get_option('validate')) ? 1 : 0;
  509. $domain->addProperty('validate_url', $validate);
  510. if ($error = domain_drush_check_response($domain)) {
  511. drush_set_error('domain', $error);
  512. }
  513. else {
  514. $domain->saveDefault();
  515. drush_print(dt('!domain set to primary domain.', array('!domain' => $domain->getHostname())));
  516. }
  517. }
  518. }
  519. /**
  520. * Disables a domain.
  521. */
  522. function drush_domain_disable($argument) {
  523. // Resolve the domain.
  524. if ($domain = drush_domain_get_from_argument($argument)) {
  525. if ($domain->status()) {
  526. $domain->disable();
  527. drush_print(dt('!domain has been disabled.', array('!domain' => $domain->getHostname())));
  528. }
  529. else {
  530. drush_print(dt('!domain is already disabled.', array('!domain' => $domain->getHostname())));
  531. }
  532. }
  533. }
  534. /**
  535. * Enables a domain.
  536. */
  537. function drush_domain_enable($argument) {
  538. // Resolve the domain.
  539. if ($domain = drush_domain_get_from_argument($argument)) {
  540. if (!$domain->status()) {
  541. $domain->enable();
  542. drush_print(dt('!domain has been enabled.', array('!domain' => $domain->getHostname())));
  543. }
  544. else {
  545. drush_print(dt('!domain is already enabled.', array('!domain' => $domain->getHostname())));
  546. }
  547. }
  548. }
  549. /**
  550. * Changes a domain name.
  551. */
  552. function drush_domain_name($argument, $name) {
  553. // Resolve the domain.
  554. if ($domain = drush_domain_get_from_argument($argument)) {
  555. $domain->saveProperty('name', $name);
  556. }
  557. }
  558. /**
  559. * Changes a domain machine_name.
  560. */
  561. function drush_domain_machine_name($argument, $machine_name) {
  562. $machine_name = \Drupal::service('entity_type.manager')->getStorage('domain')->createMachineName($machine_name);
  563. // Resolve the domain.
  564. if ($domain = drush_domain_get_from_argument($argument)) {
  565. $results = \Drupal::entityTypeManager()
  566. ->getStorage('domain')
  567. ->loadByProperties(array('machine_name' => $machine_name));
  568. foreach ($results as $result) {
  569. if ($result->id() == $machine_name) {
  570. drush_print(dt('The machine_name @machine_name is being used by domain @hostname.', array('@machine_name' => $machine_name, '@hostname' => $result->getHostname())));
  571. return;
  572. }
  573. }
  574. $domain->saveProperty('id', $machine_name);
  575. }
  576. }
  577. /**
  578. * Changes a domain scheme.
  579. */
  580. function drush_domain_scheme($argument) {
  581. // Resolve the domain.
  582. if ($domain = drush_domain_get_from_argument($argument)) {
  583. $content = drush_choice(array(1 => dt('http'), 2 => dt('https')), dt('Select the default http scheme:'), '!value');
  584. if (empty($content)) {
  585. return;
  586. }
  587. $scheme = 'http';
  588. if ($content == 2) {
  589. $scheme = 'https';
  590. }
  591. $domain->saveProperty('scheme', $scheme);
  592. }
  593. }
  594. /**
  595. * Converts a domain string or domain_id to a $domain array.
  596. *
  597. * On failure, throws a drush error.
  598. */
  599. function drush_domain_get_from_argument($argument) {
  600. $domain = \Drupal::service('entity_type.manager')->getStorage('domain')->load($argument);
  601. if (!$domain) {
  602. $domain = \Drupal::service('entity_type.manager')->getStorage('domain')->loadByHostname($argument);
  603. }
  604. if (!$domain) {
  605. drush_set_error('domain', dt('Domain record not found.'));
  606. return NULL;
  607. }
  608. return $domain;
  609. }