File: //usr/local/wp/vendor/wp-cli/entity-command/src/Site_Command.php
<?php
use WP_CLI\CommandWithDBObject;
use WP_CLI\ExitException;
use WP_CLI\Fetchers\Site as SiteFetcher;
use WP_CLI\Iterators\Query as QueryIterator;
use WP_CLI\Iterators\Table as TableIterator;
use WP_CLI\Utils;
use WP_CLI\Formatter;
use WP_CLI\Fetchers\User as UserFetcher;
/**
* Creates, deletes, empties, moderates, and lists one or more sites on a multisite installation.
*
* ## EXAMPLES
*
* # Create site
* $ wp site create --slug=example
* Success: Site 3 created: www.example.com/example/
*
* # Output a simple list of site URLs
* $ wp site list --field=url
* http://www.example.com/
* http://www.example.com/subdir/
*
* # Delete site
* $ wp site delete 123
* Are you sure you want to delete the 'http://www.example.com/example' site? [y/n] y
* Success: The site at 'http://www.example.com/example' was deleted.
*
* @package wp-cli
*/
class Site_Command extends CommandWithDBObject {
protected $obj_type = 'site';
protected $obj_id_key = 'blog_id';
private $fetcher;
public function __construct() {
$this->fetcher = new SiteFetcher();
}
/**
* Delete comments.
*/
private function empty_comments() {
global $wpdb;
// Empty comments and comment cache
$comment_ids = $wpdb->get_col( "SELECT comment_ID FROM $wpdb->comments" );
foreach ( $comment_ids as $comment_id ) {
wp_cache_delete( $comment_id, 'comment' );
wp_cache_delete( $comment_id, 'comment_meta' );
}
$wpdb->query( "TRUNCATE TABLE $wpdb->comments" );
$wpdb->query( "TRUNCATE TABLE $wpdb->commentmeta" );
}
/**
* Delete all posts.
*/
private function empty_posts() {
global $wpdb;
// Empty posts and post cache
$posts_query = "SELECT ID FROM $wpdb->posts";
$posts = new QueryIterator( $posts_query, 10000 );
$taxonomies = get_taxonomies();
while ( $posts->valid() ) {
$post_id = $posts->current()->ID;
wp_cache_delete( $post_id, 'posts' );
wp_cache_delete( $post_id, 'post_meta' );
foreach ( $taxonomies as $taxonomy ) {
wp_cache_delete( $post_id, "{$taxonomy}_relationships" );
}
wp_cache_delete( $wpdb->blogid . '-' . $post_id, 'global-posts' );
$posts->next();
}
$wpdb->query( "TRUNCATE TABLE $wpdb->posts" );
$wpdb->query( "TRUNCATE TABLE $wpdb->postmeta" );
}
/**
* Delete terms, taxonomies, and tax relationships.
*/
private function empty_taxonomies() {
global $wpdb;
// Empty taxonomies and terms
$terms = $wpdb->get_results( "SELECT term_id, taxonomy FROM $wpdb->term_taxonomy" );
$taxonomies = [];
foreach ( (array) $terms as $term ) {
$taxonomies[] = $term->taxonomy;
wp_cache_delete( $term->term_id, $term->taxonomy );
}
$taxonomies = array_unique( $taxonomies );
$cleaned = [];
foreach ( $taxonomies as $taxonomy ) {
if ( isset( $cleaned[ $taxonomy ] ) ) {
continue;
}
$cleaned[ $taxonomy ] = true;
wp_cache_delete( 'all_ids', $taxonomy );
wp_cache_delete( 'get', $taxonomy );
delete_option( "{$taxonomy}_children" );
}
$wpdb->query( "TRUNCATE TABLE $wpdb->terms" );
$wpdb->query( "TRUNCATE TABLE $wpdb->term_taxonomy" );
$wpdb->query( "TRUNCATE TABLE $wpdb->term_relationships" );
if ( ! empty( $wpdb->termmeta ) ) {
$wpdb->query( "TRUNCATE TABLE $wpdb->termmeta" );
}
}
/**
* Delete all links, link_category terms, and related cache.
*/
private function empty_links() {
global $wpdb;
// Remove links and related cached data.
$links_query = "SELECT link_id FROM {$wpdb->links}";
$links = new QueryIterator( $links_query, 10000 );
// Remove bookmarks cache group.
wp_cache_delete( 'get_bookmarks', 'bookmark' );
while ( $links->valid() ) {
$link_id = $links->current()->link_id;
// Remove cache for the link.
wp_delete_object_term_relationships( $link_id, 'link_category' );
wp_cache_delete( $link_id, 'bookmark' );
clean_object_term_cache( $link_id, 'link' );
$links->next();
}
// Empty the table once link related cache and term is removed.
$wpdb->query( "TRUNCATE TABLE {$wpdb->links}" );
}
/**
* Insert default terms.
*/
private function insert_default_terms() {
global $wpdb;
// Default category
$cat_name = __( 'Uncategorized' );
/* translators: Default category slug */
$cat_slug = sanitize_title( _x( 'Uncategorized', 'Default category slug' ) );
// phpcs:ignore WordPress.WP.DeprecatedFunctions.global_terms_enabledFound -- Required for backwards compatibility.
if ( global_terms_enabled() ) {
$cat_id = $wpdb->get_var( $wpdb->prepare( "SELECT cat_ID FROM {$wpdb->sitecategories} WHERE category_nicename = %s", $cat_slug ) );
if ( null === $cat_id ) {
$wpdb->insert(
$wpdb->sitecategories,
[
'cat_ID' => 0,
'cat_name' => $cat_name,
'category_nicename' => $cat_slug,
'last_updated' => current_time(
'mysql',
true
),
]
);
$cat_id = $wpdb->insert_id;
}
update_option( 'default_category', $cat_id );
} else {
$cat_id = 1;
}
$wpdb->insert(
$wpdb->terms,
[
'term_id' => $cat_id,
'name' => $cat_name,
'slug' => $cat_slug,
'term_group' => 0,
]
);
$wpdb->insert(
$wpdb->term_taxonomy,
[
'term_id' => $cat_id,
'taxonomy' => 'category',
'description' => '',
'parent' => 0,
'count' => 0,
]
);
}
/**
* Reset option values to default.
*/
private function reset_options() {
// Reset Privacy Policy value to prevent error.
update_option( 'wp_page_for_privacy_policy', 0 );
// Reset sticky posts option.
update_option( 'sticky_posts', [] );
}
/**
* Empties a site of its content (posts, comments, terms, and meta).
*
* Truncates posts, comments, and terms tables to empty a site of its
* content. Doesn't affect site configuration (options) or users.
*
* If running a persistent object cache, make sure to flush the cache
* after emptying the site, as the cache values will be invalid otherwise.
*
* To also empty custom database tables, you'll need to hook into command
* execution:
*
* ```
* WP_CLI::add_hook( 'after_invoke:site empty', function(){
* global $wpdb;
* foreach( array( 'p2p', 'p2pmeta' ) as $table ) {
* $table = $wpdb->$table;
* $wpdb->query( "TRUNCATE $table" );
* }
* });
* ```
*
* ## OPTIONS
*
* [--uploads]
* : Also delete *all* files in the site's uploads directory.
*
* [--yes]
* : Proceed to empty the site without a confirmation prompt.
*
* ## EXAMPLES
*
* $ wp site empty
* Are you sure you want to empty the site at http://www.example.com of all posts, links, comments, and terms? [y/n] y
* Success: The site at 'http://www.example.com' was emptied.
*
* @subcommand empty
*/
public function empty_( $args, $assoc_args ) {
$upload_message = '';
if ( Utils\get_flag_value( $assoc_args, 'uploads' ) ) {
$upload_message = ', and delete its uploads directory';
}
WP_CLI::confirm( "Are you sure you want to empty the site at '" . site_url() . "' of all posts, links, comments, and terms" . $upload_message . '?', $assoc_args );
$this->empty_posts();
$this->empty_links();
$this->empty_comments();
$this->empty_taxonomies();
$this->insert_default_terms();
$this->reset_options();
if ( ! empty( $upload_message ) ) {
$upload_dir = wp_upload_dir();
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator( $upload_dir['basedir'], RecursiveDirectoryIterator::SKIP_DOTS ),
RecursiveIteratorIterator::CHILD_FIRST
);
$files_to_unlink = [];
$directories_to_delete = [];
$is_main_site = is_main_site();
foreach ( $files as $fileinfo ) {
$realpath = $fileinfo->getRealPath();
// Don't clobber subsites when operating on the main site
if ( $is_main_site && false !== stripos( $realpath, '/sites/' ) ) {
continue;
}
if ( $fileinfo->isDir() ) {
$directories_to_delete[] = $realpath;
} else {
$files_to_unlink[] = $realpath;
}
}
foreach ( $files_to_unlink as $file ) {
unlink( $file );
}
foreach ( $directories_to_delete as $directory ) {
// Directory could be main sites directory '/sites' which may be non-empty.
@rmdir( $directory );
}
// May be non-empty if '/sites' still around.
@rmdir( $upload_dir['basedir'] );
}
WP_CLI::success( "The site at '" . site_url() . "' was emptied." );
}
/**
* Deletes a site in a multisite installation.
*
* ## OPTIONS
*
* [<site-id>]
* : The id of the site to delete. If not provided, you must set the --slug parameter.
*
* [--slug=<slug>]
* : Path of the site to be deleted. Subdomain on subdomain installs, directory on subdirectory installs.
*
* [--yes]
* : Answer yes to the confirmation message.
*
* [--keep-tables]
* : Delete the blog from the list, but don't drop its tables.
*
* ## EXAMPLES
*
* $ wp site delete 123
* Are you sure you want to delete the http://www.example.com/example site? [y/n] y
* Success: The site at 'http://www.example.com/example' was deleted.
*/
public function delete( $args, $assoc_args ) {
if ( ! is_multisite() ) {
WP_CLI::error( 'This is not a multisite installation.' );
}
if ( isset( $assoc_args['slug'] ) ) {
$blog_id = get_id_from_blogname( $assoc_args['slug'] );
if ( null === $blog_id ) {
WP_CLI::error( sprintf( 'Could not find site with slug \'%s\'.', $assoc_args['slug'] ) );
}
$blog = get_blog_details( $blog_id );
} else {
if ( empty( $args ) ) {
WP_CLI::error( 'Need to specify a blog id.' );
}
$blog_id = $args[0];
if ( is_main_site( $blog_id ) ) {
WP_CLI::error( 'You cannot delete the root site.' );
}
$blog = get_blog_details( $blog_id );
}
if ( ! $blog ) {
WP_CLI::error( 'Site not found.' );
}
$site_url = trailingslashit( $blog->siteurl );
WP_CLI::confirm( "Are you sure you want to delete the '{$site_url}' site?", $assoc_args );
wpmu_delete_blog( $blog->blog_id, ! Utils\get_flag_value( $assoc_args, 'keep-tables' ) );
WP_CLI::success( "The site at '{$site_url}' was deleted." );
}
/**
* Creates a site in a multisite installation.
*
* ## OPTIONS
*
* --slug=<slug>
* : Path for the new site. Subdomain on subdomain installs, directory on subdirectory installs.
*
* [--title=<title>]
* : Title of the new site. Default: prettified slug.
*
* [--email=<email>]
* : Email for admin user. User will be created if none exists. Assignment to super admin if not included.
*
* [--network_id=<network-id>]
* : Network to associate new site with. Defaults to current network (typically 1).
*
* [--private]
* : If set, the new site will be non-public (not indexed)
*
* [--porcelain]
* : If set, only the site id will be output on success.
*
* ## EXAMPLES
*
* $ wp site create --slug=example
* Success: Site 3 created: http://www.example.com/example/
*/
public function create( $args, $assoc_args ) {
if ( ! is_multisite() ) {
WP_CLI::error( 'This is not a multisite installation.' );
}
global $wpdb, $current_site;
$base = $assoc_args['slug'];
$title = Utils\get_flag_value( $assoc_args, 'title', ucfirst( $base ) );
$email = empty( $assoc_args['email'] ) ? '' : $assoc_args['email'];
// Network
if ( ! empty( $assoc_args['network_id'] ) ) {
$network = $this->get_network( $assoc_args['network_id'] );
if ( false === $network ) {
WP_CLI::error( "Network with id {$assoc_args['network_id']} does not exist." );
}
} else {
$network = $current_site;
}
$public = ! Utils\get_flag_value( $assoc_args, 'private' );
// Sanitize
if ( preg_match( '|^([a-zA-Z0-9-])+$|', $base ) ) {
$base = strtolower( $base );
}
// If not a subdomain install, make sure the domain isn't a reserved word
if ( ! is_subdomain_install() ) {
$subdirectory_reserved_names = $this->get_subdirectory_reserved_names();
if ( in_array( $base, $subdirectory_reserved_names, true ) ) {
WP_CLI::error( 'The following words are reserved and cannot be used as blog names: ' . implode( ', ', $subdirectory_reserved_names ) );
}
}
// Check for valid email, if not, use the first super admin found
// Probably a more efficient way to do this so we dont query for the
// User twice if super admin
$email = sanitize_email( $email );
if ( empty( $email ) || ! is_email( $email ) ) {
$super_admins = get_super_admins();
$email = '';
if ( ! empty( $super_admins ) && is_array( $super_admins ) ) {
// Just get the first one
$super_login = reset( $super_admins );
$super_user = get_user_by( 'login', $super_login );
if ( $super_user ) {
$email = $super_user->user_email;
}
}
}
if ( is_subdomain_install() ) {
$newdomain = $base . '.' . preg_replace( '|^www\.|', '', $current_site->domain );
$path = $current_site->path;
} else {
$newdomain = $current_site->domain;
$path = $current_site->path . $base . '/';
}
$user_id = email_exists( $email );
if ( ! $user_id ) { // Create a new user with a random password
$password = wp_generate_password( 24, false );
$user_id = wpmu_create_user( $base, $password, $email );
if ( false === $user_id ) {
WP_CLI::error( "Can't create user." );
} else {
User_Command::wp_new_user_notification( $user_id, $password );
}
}
$wpdb->hide_errors();
$title = wp_slash( $title );
$id = wpmu_create_blog( $newdomain, $path, $title, $user_id, [ 'public' => $public ], $network->id );
$wpdb->show_errors();
if ( ! is_wp_error( $id ) ) {
if ( ! is_super_admin( $user_id ) && ! get_user_option( 'primary_blog', $user_id ) ) {
update_user_option( $user_id, 'primary_blog', $id, true );
}
} else {
WP_CLI::error( $id->get_error_message() );
}
if ( Utils\get_flag_value( $assoc_args, 'porcelain' ) ) {
WP_CLI::line( $id );
} else {
$site_url = trailingslashit( get_site_url( $id ) );
WP_CLI::success( "Site {$id} created: {$site_url}" );
}
}
/**
* Generate some sites.
*
* Creates a specified number of new sites.
*
* ## OPTIONS
*
* [--count=<number>]
* : How many sites to generates?
* ---
* default: 100
* ---
*
* [--slug=<slug>]
* : Path for the new site. Subdomain on subdomain installs, directory on subdirectory installs.
*
* [--email=<email>]
* : Email for admin user. User will be created if none exists. Assignment to super admin if not included.
*
* [--network_id=<network-id>]
* : Network to associate new site with. Defaults to current network (typically 1).
*
* [--private]
* : If set, the new site will be non-public (not indexed)
*
* [--format=<format>]
* : Render output in a particular format.
* ---
* default: progress
* options:
* - progress
* - ids
* ---
*
* ## EXAMPLES
*
* # Generate 10 sites.
* $ wp site generate --count=10
* Generating sites 100% [================================================] 0:01 / 0:04
*/
public function generate( $args, $assoc_args ) {
if ( ! is_multisite() ) {
WP_CLI::error( 'This is not a multisite installation.' );
}
global $wpdb, $current_site;
$defaults = [
'count' => 100,
'email' => '',
'network_id' => 1,
'slug' => 'site',
];
$assoc_args = array_merge( $defaults, $assoc_args );
// Base.
$base = $assoc_args['slug'];
if ( preg_match( '|^([a-zA-Z0-9-])+$|', $base ) ) {
$base = strtolower( $base );
}
$is_subdomain_install = is_subdomain_install();
// If not a subdomain install, make sure the domain isn't a reserved word
if ( ! $is_subdomain_install ) {
$subdirectory_reserved_names = $this->get_subdirectory_reserved_names();
if ( in_array( $base, $subdirectory_reserved_names, true ) ) {
WP_CLI::error( 'The following words are reserved and cannot be used as blog names: ' . implode( ', ', $subdirectory_reserved_names ) );
}
}
// Network.
if ( ! empty( $assoc_args['network_id'] ) ) {
$network = $this->get_network( $assoc_args['network_id'] );
if ( false === $network ) {
WP_CLI::error( "Network with id {$assoc_args['network_id']} does not exist." );
}
} else {
$network = $current_site;
}
// Public.
$public = ! Utils\get_flag_value( $assoc_args, 'private' );
// Limit.
$limit = $assoc_args['count'];
// Email.
$email = sanitize_email( $assoc_args['email'] );
if ( empty( $email ) || ! is_email( $email ) ) {
$super_admins = get_super_admins();
$email = '';
if ( ! empty( $super_admins ) && is_array( $super_admins ) ) {
$super_login = reset( $super_admins );
$super_user = get_user_by( 'login', $super_login );
if ( $super_user ) {
$email = $super_user->user_email;
}
}
}
$user_id = email_exists( $email );
if ( ! $user_id ) {
$password = wp_generate_password( 24, false );
$user_id = wpmu_create_user( $base . '-admin', $password, $email );
if ( false === $user_id ) {
WP_CLI::error( "Can't create user." );
} else {
User_Command::wp_new_user_notification( $user_id, $password );
}
}
$format = Utils\get_flag_value( $assoc_args, 'format', 'progress' );
$notify = false;
if ( 'progress' === $format ) {
$notify = Utils\make_progress_bar( 'Generating sites', $limit );
}
for ( $index = 1; $index <= $limit; $index++ ) {
$current_base = $base . $index;
$title = ucfirst( $base ) . ' ' . $index;
if ( $is_subdomain_install ) {
$new_domain = $current_base . '.' . preg_replace( '|^www\.|', '', $network->domain );
$path = $network->path;
} else {
$new_domain = $network->domain;
$path = $network->path . $current_base . '/';
}
$wpdb->hide_errors();
$title = wp_slash( $title );
$id = wpmu_create_blog( $new_domain, $path, $title, $user_id, [ 'public' => $public ], $network->id );
$wpdb->show_errors();
if ( ! is_wp_error( $id ) ) {
if ( ! is_super_admin( $user_id ) && ! get_user_option( 'primary_blog', $user_id ) ) {
update_user_option( $user_id, 'primary_blog', $id, true );
}
} else {
WP_CLI::error( $id->get_error_message() );
}
if ( 'progress' === $format ) {
$notify->tick();
} else {
echo $id;
if ( $index < $limit - 1 ) {
echo ' ';
}
}
}
if ( 'progress' === $format ) {
$notify->finish();
}
}
/**
* Retrieves a list of reserved site on a sub-directory Multisite installation.
*
* Works on older WordPress versions where get_subdirectory_reserved_names() does not exist.
*
* @return string[] Array of reserved names.
*/
private function get_subdirectory_reserved_names() {
if ( function_exists( 'get_subdirectory_reserved_names' ) ) {
return get_subdirectory_reserved_names();
}
$names = array(
'page',
'comments',
'blog',
'files',
'feed',
'wp-admin',
'wp-content',
'wp-includes',
'wp-json',
'embed',
);
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Calling WordPress native hook.
return apply_filters( 'subdirectory_reserved_names', $names );
}
/**
* Gets network data for a given id.
*
* @param int $network_id
* @return bool|array False if no network found with given id, array otherwise
*/
private function get_network( $network_id ) {
global $wpdb;
// Load network data
$networks = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM $wpdb->site WHERE id = %d",
$network_id
)
);
if ( ! empty( $networks ) ) {
// Only care about domain and path which are set here
return $networks[0];
}
return false;
}
/**
* Lists all sites in a multisite installation.
*
* ## OPTIONS
*
* [--network=<id>]
* : The network to which the sites belong.
*
* [--<field>=<value>]
* : Filter by one or more fields (see "Available Fields" section). However,
* 'url' isn't an available filter, as it comes from 'home' in wp_options.
*
* [--site__in=<value>]
* : Only list the sites with these blog_id values (comma-separated).
*
* [--site_user=<value>]
* : Only list the sites with this user.
*
* [--field=<field>]
* : Prints the value of a single field for each site.
*
* [--fields=<fields>]
* : Comma-separated list of fields to show.
*
* [--format=<format>]
* : Render output in a particular format.
* ---
* default: table
* options:
* - table
* - csv
* - count
* - ids
* - json
* - yaml
* ---
*
* ## AVAILABLE FIELDS
*
* These fields will be displayed by default for each site:
*
* * blog_id
* * url
* * last_updated
* * registered
*
* These fields are optionally available:
*
* * site_id
* * domain
* * path
* * public
* * archived
* * mature
* * spam
* * deleted
* * lang_id
*
* ## EXAMPLES
*
* # Output a simple list of site URLs
* $ wp site list --field=url
* http://www.example.com/
* http://www.example.com/subdir/
*
* @subcommand list
*/
public function list_( $args, $assoc_args ) {
if ( ! is_multisite() ) {
WP_CLI::error( 'This is not a multisite installation.' );
}
global $wpdb;
if ( isset( $assoc_args['fields'] ) ) {
$assoc_args['fields'] = preg_split( '/,[ \t]*/', $assoc_args['fields'] );
}
$defaults = [
'format' => 'table',
'fields' => [ 'blog_id', 'url', 'last_updated', 'registered' ],
];
$assoc_args = array_merge( $defaults, $assoc_args );
$where = [];
$append = '';
$site_cols = [ 'blog_id', 'last_updated', 'registered', 'site_id', 'domain', 'path', 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id' ];
foreach ( $site_cols as $col ) {
if ( isset( $assoc_args[ $col ] ) ) {
$where[ $col ] = $assoc_args[ $col ];
}
}
if ( isset( $assoc_args['site__in'] ) ) {
$where['blog_id'] = explode( ',', $assoc_args['site__in'] );
$append = 'ORDER BY FIELD( blog_id, ' . implode( ',', array_map( 'intval', $where['blog_id'] ) ) . ' )';
}
if ( isset( $assoc_args['network'] ) ) {
$where['site_id'] = $assoc_args['network'];
}
if ( isset( $assoc_args['site_user'] ) ) {
$user = ( new UserFetcher() )->get_check( $assoc_args['site_user'] );
if ( $user ) {
$blogs = get_blogs_of_user( $user->ID );
foreach ( $blogs as $blog ) {
$where['blog_id'][] = $blog->userblog_id;
}
}
if ( ! isset( $where['blog_id'] ) || empty( $where['blog_id'] ) ) {
$formatter = new Formatter( $assoc_args, [], 'site' );
$formatter->display_items( [] );
return;
}
$append = 'ORDER BY FIELD( blog_id, ' . implode( ',', array_map( 'intval', $where['blog_id'] ) ) . ' )';
}
$iterator_args = [
'table' => $wpdb->blogs,
'where' => $where,
'append' => $append,
];
$iterator = new TableIterator( $iterator_args );
$iterator = Utils\iterator_map(
$iterator,
function ( $blog ) {
$blog->url = trailingslashit( get_home_url( $blog->blog_id ) );
return $blog;
}
);
if ( ! empty( $assoc_args['format'] ) && 'ids' === $assoc_args['format'] ) {
$sites = iterator_to_array( $iterator );
$ids = wp_list_pluck( $sites, 'blog_id' );
$formatter = new Formatter( $assoc_args, null, 'site' );
$formatter->display_items( $ids );
} else {
$formatter = new Formatter( $assoc_args, null, 'site' );
$formatter->display_items( $iterator );
}
}
/**
* Archives one or more sites.
*
* ## OPTIONS
*
* [<id>...]
* : One or more IDs of sites to archive. If not provided, you must set the --slug parameter.
*
* [--slug=<slug>]
* : Path of the site to archive. Subdomain on subdomain installs, directory on subdirectory installs.
*
* ## EXAMPLES
*
* $ wp site archive 123
* Success: Site 123 archived.
*
* $ wp site archive --slug=demo
* Success: Site 123 archived.
*/
public function archive( $args, $assoc_args ) {
if ( ! $this->check_site_ids_and_slug( $args, $assoc_args ) ) {
return;
}
$ids = $this->get_sites_ids( $args, $assoc_args );
$this->update_site_status( $ids, 'archived', 1 );
}
/**
* Unarchives one or more sites.
*
* ## OPTIONS
*
* [<id>...]
* : One or more IDs of sites to unarchive. If not provided, you must set the --slug parameter.
*
* [--slug=<slug>]
* : Path of the site to unarchive. Subdomain on subdomain installs, directory on subdirectory installs.
*
* ## EXAMPLES
*
* $ wp site unarchive 123
* Success: Site 123 unarchived.
*
* $ wp site unarchive --slug=demo
* Success: Site 123 unarchived.
*/
public function unarchive( $args, $assoc_args ) {
if ( ! $this->check_site_ids_and_slug( $args, $assoc_args ) ) {
return;
}
$ids = $this->get_sites_ids( $args, $assoc_args );
$this->update_site_status( $ids, 'archived', 0 );
}
/**
* Activates one or more sites.
*
* ## OPTIONS
*
* [<id>...]
* : One or more IDs of sites to activate. If not provided, you must set the --slug parameter.
*
* [--slug=<slug>]
* : Path of the site to be activated. Subdomain on subdomain installs, directory on subdirectory installs.
*
* ## EXAMPLES
*
* $ wp site activate 123
* Success: Site 123 activated.
*
* $ wp site activate --slug=demo
* Success: Site 123 marked as activated.
*/
public function activate( $args, $assoc_args ) {
if ( ! $this->check_site_ids_and_slug( $args, $assoc_args ) ) {
return;
}
$ids = $this->get_sites_ids( $args, $assoc_args );
$this->update_site_status( $ids, 'deleted', 0 );
}
/**
* Deactivates one or more sites.
*
* ## OPTIONS
*
* [<id>...]
* : One or more IDs of sites to deactivate. If not provided, you must set the --slug parameter.
*
* [--slug=<slug>]
* : Path of the site to be deactivated. Subdomain on subdomain installs, directory on subdirectory installs.
*
* ## EXAMPLES
*
* $ wp site deactivate 123
* Success: Site 123 deactivated.
*
* $ wp site deactivate --slug=demo
* Success: Site 123 deactivated.
*/
public function deactivate( $args, $assoc_args ) {
if ( ! $this->check_site_ids_and_slug( $args, $assoc_args ) ) {
return;
}
$ids = $this->get_sites_ids( $args, $assoc_args );
$this->update_site_status( $ids, 'deleted', 1 );
}
/**
* Marks one or more sites as spam.
*
* ## OPTIONS
*
* [<id>...]
* : One or more IDs of sites to be marked as spam. If not provided, you must set the --slug parameter.
*
* [--slug=<slug>]
* : Path of the site to be marked as spam. Subdomain on subdomain installs, directory on subdirectory installs.
*
* ## EXAMPLES
*
* $ wp site spam 123
* Success: Site 123 marked as spam.
*/
public function spam( $args, $assoc_args ) {
if ( ! $this->check_site_ids_and_slug( $args, $assoc_args ) ) {
return;
}
$ids = $this->get_sites_ids( $args, $assoc_args );
$this->update_site_status( $ids, 'spam', 1 );
}
/**
* Removes one or more sites from spam.
*
* ## OPTIONS
*
* [<id>...]
* : One or more IDs of sites to remove from spam. If not provided, you must set the --slug parameter.
*
* [--slug=<slug>]
* : Path of the site to be removed from spam. Subdomain on subdomain installs, directory on subdirectory installs.
*
* ## EXAMPLES
*
* $ wp site unspam 123
* Success: Site 123 removed from spam.
*
* @subcommand unspam
*/
public function unspam( $args, $assoc_args ) {
if ( ! $this->check_site_ids_and_slug( $args, $assoc_args ) ) {
return;
}
$ids = $this->get_sites_ids( $args, $assoc_args );
$this->update_site_status( $ids, 'spam', 0 );
}
/**
* Sets one or more sites as mature.
*
* ## OPTIONS
*
* [<id>...]
* : One or more IDs of sites to set as mature. If not provided, you must set the --slug parameter.
*
* [--slug=<slug>]
* : Path of the site to be set as mature. Subdomain on subdomain installs, directory on subdirectory installs.
*
* ## EXAMPLES
*
* $ wp site mature 123
* Success: Site 123 marked as mature.
*
* $ wp site mature --slug=demo
* Success: Site 123 marked as mature.
*/
public function mature( $args, $assoc_args ) {
if ( ! $this->check_site_ids_and_slug( $args, $assoc_args ) ) {
return;
}
$ids = $this->get_sites_ids( $args, $assoc_args );
$this->update_site_status( $ids, 'mature', 1 );
}
/**
* Sets one or more sites as immature.
*
* ## OPTIONS
*
* [<id>...]
* : One or more IDs of sites to set as unmature. If not provided, you must set the --slug parameter.
*
* [--slug=<slug>]
* : Path of the site to be set as unmature. Subdomain on subdomain installs, directory on subdirectory installs.
*
* ## EXAMPLES
*
* $ wp site unmature 123
* Success: Site 123 marked as unmature.
*
* $ wp site unmature --slug=demo
* Success: Site 123 marked as unmature.
*/
public function unmature( $args, $assoc_args ) {
if ( ! $this->check_site_ids_and_slug( $args, $assoc_args ) ) {
return;
}
$ids = $this->get_sites_ids( $args, $assoc_args );
$this->update_site_status( $ids, 'mature', 0 );
}
/**
* Sets one or more sites as public.
*
* ## OPTIONS
*
* [<id>...]
* : One or more IDs of sites to set as public. If not provided, you must set the --slug parameter.
*
* [--slug=<slug>]
* : Path of the site to be set as public. Subdomain on subdomain installs, directory on subdirectory installs.
*
* ## EXAMPLES
*
* $ wp site public 123
* Success: Site 123 marked as public.
*
* $ wp site public --slug=demo
* Success: Site 123 marked as public.
*
* @subcommand public
*/
public function set_public( $args, $assoc_args ) {
if ( ! $this->check_site_ids_and_slug( $args, $assoc_args ) ) {
return;
}
$ids = $this->get_sites_ids( $args, $assoc_args );
$this->update_site_status( $ids, 'public', 1 );
}
/**
* Sets one or more sites as private.
*
* ## OPTIONS
*
* [<id>...]
* : One or more IDs of sites to set as private. If not provided, you must set the --slug parameter.
*
* [--slug=<slug>]
* : Path of the site to be set as private. Subdomain on subdomain installs, directory on subdirectory installs.
*
* ## EXAMPLES
*
* $ wp site private 123
* Success: Site 123 marked as private.
*
* $ wp site private --slug=demo
* Success: Site 123 marked as private.
*
* @subcommand private
*/
public function set_private( $args, $assoc_args ) {
if ( ! $this->check_site_ids_and_slug( $args, $assoc_args ) ) {
return;
}
$ids = $this->get_sites_ids( $args, $assoc_args );
$this->update_site_status( $ids, 'public', 0 );
}
private function update_site_status( $ids, $pref, $value ) {
$value = (int) $value;
switch ( $pref ) {
case 'archived':
$action = $value ? 'archived' : 'unarchived';
break;
case 'deleted':
$action = $value ? 'deactivated' : 'activated';
break;
case 'mature':
$action = $value ? 'marked as mature' : 'marked as unmature';
break;
case 'public':
$action = $value ? 'marked as public' : 'marked as private';
break;
case 'spam':
$action = $value ? 'marked as spam' : 'removed from spam';
break;
}
foreach ( $ids as $site_id ) {
$site = $this->fetcher->get_check( $site_id );
if ( is_main_site( $site->blog_id ) ) {
WP_CLI::warning( 'You are not allowed to change the main site.' );
continue;
}
$old_value = (int) get_blog_status( $site->blog_id, $pref );
if ( $value === $old_value ) {
WP_CLI::warning( "Site {$site->blog_id} already {$action}." );
continue;
}
update_blog_status( $site->blog_id, $pref, $value );
WP_CLI::success( "Site {$site->blog_id} {$action}." );
}
}
/**
* Get an array of site IDs from the passed-in arguments or slug parameter.
*
* @param array $args Passed-in arguments.
* @param array $assoc_args Passed-in parameters.
*
* @return array Site IDs.
* @throws ExitException
*/
private function get_sites_ids( $args, $assoc_args ) {
$slug = Utils\get_flag_value( $assoc_args, 'slug', false );
if ( $slug ) {
$blog_id = get_id_from_blogname( trim( $slug, '/' ) );
if ( null === $blog_id ) {
WP_CLI::error( sprintf( 'Could not find site with slug \'%s\'.', $slug ) );
}
return [ $blog_id ];
}
return $args;
}
/**
* Check that the site IDs or slug are provided.
*
* @param array $args Passed-in arguments.
* @param array $assoc_args Passed-in parameters.
*
* @return bool
* @throws ExitException If neither site ids nor site slug using --slug were provided.
*/
private function check_site_ids_and_slug( $args, $assoc_args ) {
if ( ( empty( $args ) && empty( $assoc_args['slug'] ) )
|| ( ! empty( $args ) && ! empty( $assoc_args['slug'] ) ) ) {
WP_CLI::error( 'Please specify one or more IDs of sites, or pass the slug for a single site using --slug.' );
}
return true;
}
}