diff --git a/bin/noblogs b/bin/noblogs new file mode 100644 index 0000000000000000000000000000000000000000..70ecac23c23448df3ab207c36e963166fa64bbde --- /dev/null +++ b/bin/noblogs @@ -0,0 +1,430 @@ +#!/suca/dai/php5 -f +<?php + +include('/usr/local/lib/noblogs-cli/noblogs.php'); + +function help() { +?> +Usage: noblogs <COMMAND> [<ARGS>...] + +Known commands: + + info BLOG [...] + Print some basic information about one or more blogs. + + connectdb BLOG + Connect to the MySQL instance that has the blog db. + + get-option OPTION_NAME BLOG [...] + Print the value of an option for the specified blogs. + + set-option OPTION_NAME OPTION_VALUE BLOG [...] + Set the value of an option for the specified blogs. + + print-all-blogs + Print a list of all existing blog IDs. + + print-local-blogs + Print a list of just those blog IDs that are hosted on this + server. + + dump-shards + Print a JSON dictionary representing the full backend -> blogs + map. + + check-upgrade BLOG [...] + Check whether the specified blogs need to be upgraded. + + upgrade BLOG [...] + Upgrade the specified blogs. NOTE: upgrading a blog that is + not local is dangerous! Use the 'on-local-blogs' wrapper. + + run-cron BLOG [...] + Run cron jobs for the specified blogs. NOTE: running cron jobs + for a blog that is not local is dangerous! Use the 'on-local-blogs' + wrapper. + + fix-rewrites BLOG [...] + Fix broken rewrite rules for the specified blogs. NOTE: dangerous!!!! + + close-comments-if-inactive BLOG [...] + Closes old posts for comments on non-active blogs. + + remove-network-upgrade-message + Remove the 'network upgrade' message when all the blogs have + been upgraded individually. + + update-friend-domains + Update the list of 'friend' email domains. + + check-updates + Check for updates of core, plugins and themes. + Options: + -force prints the known updates when not checking for the new ones + -mail sends email to ai-changes@investici.org + -json print JSON output + + set-readonly on|off + Set read-only mode for the local noblogs installation. The argument + must be either the literal 'on' or 'off'. + +<?php + exit(1); +} + +// 'info': Return information about a single blog. +function do_info($args) { + foreach ($args as $arg) { + $blog = noblogs_get_blog($arg); + if (!$blog) { + die("Blog not found.\n"); + } + $status = 'active'; + if ($blog->deleted) { + $status = 'deleted'; + } elseif ($blog->archived) { + $status = 'archived'; + } + $dbinfo = noblogs_get_backend_for_blog($blog->blog_id); + + echo "ID: {$blog->blog_id}\n"; + echo "Name: {$blog->domain}\n"; + echo "Host: {$dbinfo['host']}\n"; + echo "Status: {$status}\n"; + echo "Registered: {$blog->registered}\n"; + echo "Last Update: {$blog->last_updated}\n"; + echo "\n"; + } +} + + +// 'connectdb': Connect to the database hosting a specific blog. +function do_connectdb($args) { + $blog = noblogs_get_blog($args[0]); + if (!$blog) { + die("Blog not found.\n"); + } + $backend = noblogs_get_backend_for_blog($blog->blog_id); + + echo "ID: {$blog->blog_id}\n"; + $cmd = "mysql -A -h {$backend['host']} -P {$backend['port']} -u{$backend['user']} -p{$backend['password']} {$backend['db']}"; + echo "$cmd\n"; + //system($cmd); +} + + +// 'get-option': Print the value of a blog option. +function do_get_option($args) { + $option = $args[0]; + if (!$option) { + echo "Not enough arguments\n"; + help(); + } + + foreach (array_splice($args, 1) as $arg) { + $blog = noblogs_get_blog($arg); + if (!$blog) { + echo "Blog {$arg} not found.\n"; + continue; + } + + switch_to_blog($blog->blog_id); + $value = get_option($option); + if ($value) { + if (is_array($value)) { + // Use JSON as a string representation. + $value = json_encode($value); + } + echo "{$arg}: {$value}\n"; + } + restore_current_blog(); + } +} + +// 'set-option': Set the value of a blog option. +function do_set_option($args) { + $option = array_shift($args); + if (!$option) { + echo "Not enough arguments\n"; + help(); + } + $value = array_shift($args); + if ($value === null) { + echo "Not enough arguments\n"; + help(); + } + + + foreach ($args as $arg) { + $blog = noblogs_get_blog($arg); + if (!$blog) { + echo "Blog {$arg} not found.\n"; + continue; + } + + switch_to_blog($blog->blog_id); + update_option($option, $value); + $nv = get_option($option); + echo "{$arg}: {$nv}\n"; + restore_current_blog(); + } +} + + +// 'dump-shards': Print a JSON dictionary representing the full map +// of backend -> blogs database mappings. +function do_dump_shards($args) { + $backend_map = noblogs_get_backend_map(); + echo json_encode($backend_map); + echo "\n"; +} + + +// 'print-all-blogs': List all blog IDs. +function do_print_all_blogs($args) { + $blogs = noblogs_get_blogs(); + foreach ($blogs as $blog) { + echo "{$blog->blog_id}\n"; + } +} + + +// 'print-local-blogs': List the blog IDs that are local to this machine. +function do_print_local_blogs($args) { + $local_blogs = noblogs_get_local_blogs(); + foreach ($local_blogs as $b) { + echo $b . "\n"; + } +} + + +// 'remove-network-upgrade-message': Remove the annoying "network +// upgrade necessary" banner from the dashboard. +function do_remove_network_upgrade_message($args) { + global $wp_db_version; + update_site_option('wpmu_upgrade_site', $wp_db_version); +} + + +// 'check-upgrade': Check if a blog needs to be upgraded. +function do_check_upgrade($args) { + global $wp_db_version; + foreach ($args as $arg) { + $blog = noblogs_get_blog($arg); + if (!$blog) { + echo "Blog {$arg} not found.\n"; + continue; + } + switch_to_blog($blog->blog_id); + $db_version = get_option('db_version'); + if ($db_version != $wp_db_version) { + echo "{$arg}: UPGRADE\n"; + } else { + echo "{$arg}: ok\n"; + } + restore_current_blog(); + } +} + + +// 'upgrade': Upgrade a blog. +function do_upgrade($args) { + include(NOBLOGS_ROOT . "/wp-admin/includes/upgrade.php"); + + foreach ($args as $arg) { + $blog = noblogs_get_blog($arg); + if (!$blog) { + echo "Blog {$arg} not found.\n"; + continue; + } + switch_to_blog($blog->blog_id); + wp_upgrade(); + echo "{$arg}: ok\n"; + restore_current_blog(); + } +} + + +// 'run-cron': Run cron jobs. +function do_run_cron($args) { + foreach ($args as $arg) { + $blog = noblogs_get_blog($arg); + if (!$blog) { + echo "Blog {$arg} not found.\n"; + continue; + } + switch_to_blog($blog->blog_id); + noblogs_run_cron_for_current_blog(); + echo "{$arg}: ok\n"; + restore_current_blog(); + } +} + + +// 'fix-rewrites': Fix rewrite rules +function do_fix_rewrites($args) { + global $wp_rewrite; + foreach ($args as $arg) { + $blog = noblogs_get_blog($arg); + if (!$blog) { + echo "Blog {$arg} not found.\n"; + continue; + } + switch_to_blog($blog->blog_id); + $wp_rewrite->init(); + create_initial_taxonomies(); + $wp_rewrite->flush_rules(); + echo "{$arg}: ok\n"; + restore_current_blog(); + } +} + +function do_update_friend_domains($args) { + $domains = noblogs_list_friend_domains(); + update_site_option('limited_email_domains', $domains); + echo "Update done.\n"; +} + +function do_check_spam($args) { + global $wpdb; + $spamcount = 0; + foreach ($args as $arg) { + $blog = noblogs_get_blog($arg); + if (!$blog) { + echo "Blog {$arg} not found.\n"; + continue; + } + $spam = $wpdb->get_var("SELECT count(*) FROM wp_".$blog->blog_id ."_comments where comment_approved = 'spam';"); + $spamcount+=$spam; + printf("%s - %d : %d\n", $blog->domain, $blog->blog_id, $spam); + } + printf("Found %d spam comments\n", $spamcount); +} + +function do_nuke_spam($args) { + global $wpdb; + foreach ($args as $arg) { + $blog = noblogs_get_blog($arg); + if (!$blog) { + echo "Blog {$arg} not found.\n"; + continue; + } + $spam = $wpdb->get_var("DELETE FROM wp_".$blog->blog_id ."_comments where comment_approved = 'spam' and comment_date < '".date('Y-m-d',time() - (86400 * 60))."';"); + printf("%s - %d : %d\n", $blog->domain, $blog->blog_id, $spam); + } +} + +function do_close_comments_if_inactive($args) { + global $wpdb; + foreach ($args as $arg) { + $blog = noblogs_get_blog($arg); + if (!$blog) { + echo "Blog {$arg} not found.\n"; + continue; + } + switch_to_blog($blog->blog_id); + if (get_option('close_comments_for_old_posts') == '1') { + echo "Blog {$blog->domain} already closed to comments, skipping.\n"; + continue; + } else if (noblogs_is_stale()) { + echo "Closing comments on blog {$blog->domain}.\n"; + update_option('close_comments_for_old_posts', '1'); + update_option('close_comments_days_old', '90'); + } else { + echo "Leaving comments opened on blog {$blog->domain}.\n"; + } + } +} + +function check_updates_parse_flags($args) { + $flags = array(); + foreach (array('json', 'force', 'mail') as $f){ + $flags[$f] = in_array('-' . $f, $args); + } + return $flags; +} + +// 'check-updates': check for core, plugins and theme updates +function do_check_updates($args) { + $alertmail = "root@localhost"; + $flags = check_updates_parse_flags($args); + global $wp_version; + + $updates = array(); + $updates['core'] = array(); + $updates['plugins'] = array(); + $updates['themes'] = array(); + + $txt = ''; + + $check = $flags['force']; + $check = wp_version_check() || $check; + $check = wp_update_plugins() || $check; + $check = wp_update_themes() || $check; + if (!$check) { + $flags['json'] || print "Check is too soon, exiting\n" ; + exit(1); + } + + foreach(get_site_transient('update_core')->updates as $update) { + if($update->current != $wp_version) { + $txt .= sprintf("Core version: %s\n\n", $update->current); + $updates['core'][] = array('version' => $update->current, + 'url' => $update->package); + } + } + + foreach(get_site_transient('update_plugins')->response as $update) { + $txt .= sprintf("Plugin: %s\nVersion: %s\nURL: %s\n\n", + $update->slug, $update->new_version, $update->package); + $updates['plugins'][] = array('name' => $update->slug, + 'version' => $update->new_version, + 'url' => $update->package); + } + + foreach(get_site_transient('update_themes')->response as $name => $update) { + $txt .= sprintf("Theme: %s\nVersion: %s\nURL: %s\n\n", + $name, $update['new_version'], $update['package']); + $updates['themes'][] = array('name' => $name, + 'version' => $update['new_version'], + 'url' => $update['package']); + } + + if ($flags['mail']){ + wp_mail($alertmail, "[Noblogs-Alert] Updates are available", $txt); + } + + if ($flags['json']){ + print json_encode($updates); + } else { + print $txt; + } + +} + +// 'set-readonly': toggle readonly mode by modifying .htaccess +function do_set_readonly($args) { + $htaccess = NOBLOGS_ROOT . '/.htaccess'; + if ($args[0] == "on") { + comment_with_markers($htaccess, 'readonly', false); + } elseif ($args[0] == "off") { + comment_with_markers($htaccess, 'readonly', true); + } else { + print "Argument must be either 'on' or 'off'."; + } +} + + +// Command-line parsing. +$cmd = $argv[1]; +if (!$cmd) { + help(); +} + +$cmd_func = "do_" . str_replace('-', '_', $cmd); +if (!function_exists($cmd_func)) { + echo "Unknown command '".$cmd."'\n\n"; + help(); +} +call_user_func($cmd_func, array_slice($argv, 2)); + diff --git a/lib/editfiles.php b/lib/editfiles.php new file mode 100644 index 0000000000000000000000000000000000000000..fbe831c71728fa60123babbed9b7702b1958eedb --- /dev/null +++ b/lib/editfiles.php @@ -0,0 +1,52 @@ +<?php + +/* Comments/uncomments a section of a file, placed between BEGIN and + END markers. */ +function comment_with_markers($filename, $marker, $do_comment) { + if (!file_exists($filename)) { + return false; + } + $markerdata = explode("\n", implode('', file($filename))); + if (!$f = @fopen($filename, 'w')) { + return false; + } + $foundit = false; + + $begin_marker = '# BEGIN ' . $marker; + $end_marker = '# END ' . $marker; + + if ($markerdata) { + $state = false; + foreach ($markerdata as $n => $markerline) { + if (strpos($markerline, $end_marker) !== false) { + $state = false; + } + + if ($state) { + if ($do_comment) { + if (substr($markerline, 0, 1) != '#') { + $markerline = '#' . $markerline; + } + } else { + if (substr($markerline, 0, 1) == '#') { + $markerline = substr($markerline, 1); + } + } + } + + if ($n + 1 < count($markerdata)) { + fwrite($f, "{$markerline}\n"); + } else { + fwrite($f, "{$markerline}"); + } + + if (strpos($markerline, $begin_marker) !== false) { + $state = true; + $foundit = true; + } + } + } + + fclose($f); + return $foundit; +} diff --git a/lib/noblogs.php.in b/lib/noblogs.php.in index deb2a465d6f75d493e9bb12436401d1094058a2a..7bf44204cedf59f53c0cbfe16411b3c19901e07a 100644 --- a/lib/noblogs.php.in +++ b/lib/noblogs.php.in @@ -9,6 +9,7 @@ define('AI_CRON_SCRIPT', true); require_once(dirname(__FILE__) . '/blogs.php'); require_once(dirname(__FILE__) . '/cron.php'); require_once(dirname(__FILE__) . '/friends.php'); +require_once(dirname(__FILE__) . '/editfiles.php'); // Load the Wordpress api. define('WP_CACHE',false);