-
-
Save nacin/963d6e67a8b9736039f9 to your computer and use it in GitHub Desktop.
Subscribers Only
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
* Plugin Name: Subscribers Only | |
* Description: Forces users to log in to view the site. Offers a per-user random key for use in feed readers. Also locks down uploads (which works on single-site only, running on Apache, under certain upload configurations). | |
* Author: Andrew Nacin | |
* Author URI: http://andrewnacin.com/ | |
* Version: 0.2 | |
*/ | |
class Subscribers_Only { | |
static $instance; | |
const key_length = 32; | |
const meta_key = '_subscribers_only_feed_key'; | |
const uploads_query_var = 'uploads_subscribers_only'; | |
function __construct() { | |
self::$instance = $this; | |
add_action( 'init', array( $this, 'init' ) ); | |
add_action( 'template_redirect', array( $this, 'template_redirect' ) ); | |
add_filter( 'login_message', array( $this, 'login_message' ) ); | |
add_action( 'tool_box', array( $this, 'tool_box' ) ); | |
if ( is_multisite() ) | |
return; | |
add_action( 'init', array( $this, 'add_external_rule' ), 11 ); | |
register_activation_hook( __FILE__, array( $this, 'activate' ) ); | |
register_deactivation_hook( __FILE__, array( $this, 'deactivate' ) ); | |
} | |
function init() { | |
global $wp; | |
$wp->add_query_var( self::uploads_query_var ); | |
} | |
function activate() { | |
$this->add_external_rule(); | |
flush_rewrite_rules( true ); | |
} | |
function deactivate() { | |
global $wp_rewrite; | |
// Yuck. | |
if ( false !== $key = array_search( 'index.php?' . self::uploads_query_var . '=$1', $wp_rewrite->non_wp_rules ) ) | |
unset( $wp_rewrite->non_wp_rules[ $key ] ); | |
flush_rewrite_rules( true ); | |
} | |
function add_external_rule() { | |
global $wp_rewrite; | |
$upload_dir = wp_upload_dir(); | |
// This probably isn't compatible with various upload directory configurations. | |
$relative_dir = str_replace( site_url( '/' ), '', $upload_dir['baseurl'] ); | |
$wp_rewrite->add_external_rule( $relative_dir . '/(.*)', 'index.php?' . self::uploads_query_var . '=$1' ); | |
} | |
function template_redirect() { | |
if ( is_feed() && ! is_user_logged_in() ) { | |
if ( $this->validate_feed_key() ) | |
return; | |
else | |
wp_die( __( 'Sorry, this is a private site.', 'subscribers-only' ) ); | |
} | |
if ( ! is_user_logged_in() ) { | |
$redirect = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; | |
wp_redirect( wp_login_url( $redirect ) ); | |
exit; | |
} | |
if ( apply_filters( 'subscribers_only_require_read', true ) && ! current_user_can( 'read' ) ) { | |
wp_die( __( 'Sorry, this is a private site.', 'subscribers-only' ) ); | |
} | |
if ( ! is_feed() | |
&& ! is_multisite() | |
&& apply_filters( 'subscribers_only_maybe_serve_file', true ) | |
&& $file = get_query_var( self::uploads_query_var ) | |
) | |
$this->serve_file( $file ); | |
} | |
function validate_feed_key() { | |
global $wpdb; | |
if ( empty( $_GET['key'] ) ) | |
return false; | |
$key = preg_replace( '/[^a-z0-9]/i', '', $_GET['key'] ); | |
if ( self::key_length != strlen( $key ) ) | |
return false; | |
if ( $wpdb->get_var( $wpdb->prepare( "SELECT user_id FROM $wpdb->usermeta WHERE meta_key = %s AND meta_value = %s", $wpdb->prefix . self::meta_key, $key ) ) ) | |
return true; | |
return false; | |
} | |
function login_message( $message ) { | |
return '<p class="message">' . __( 'You must log in to access this site.', 'subscribers-only' ) . '</p>'; | |
} | |
function tool_box() { | |
$key = get_user_option( self::meta_key ); | |
if ( isset( $_POST['generate-new-feed-key'] ) && isset( $_POST['_subscribers_only_nonce'] ) && wp_verify_nonce( $_POST['_subscribers_only_nonce'], 'generate-new-feed-key' ) ) { | |
$key = wp_generate_password( self::key_length, false, false ); | |
update_user_option( get_current_user_id(), self::meta_key, $key ); | |
} | |
echo '<div class="tool-box">'; | |
echo '<h3 class="title">' . __( 'Secret Feed Key', 'subscribers-only' ) . '</h3>'; | |
echo '<p>' . __( 'As this is a private site, you need to append a key to feeds for use in feed readers.' ) . '</p>'; | |
if ( $key ) | |
echo '<p><span class="description">' . sprintf( __( 'Example: %s', 'subscribers-only' ), add_query_arg( 'key', $key, get_feed_link() ) ) . '</p>'; | |
echo '<p><input type="text" value="' . esc_attr( $key ) . '" class="regular-text" readonly="readonly" />'; | |
echo '<form method="post">'; | |
wp_nonce_field( 'generate-new-feed-key', '_subscribers_only_nonce' ); | |
submit_button( __( 'Generate New Key', 'subscribers-only' ), 'secondary', 'generate-new-feed-key', false ); | |
echo '</form></p></div>'; | |
} | |
// Derived from wp-includes/ms-files.php. | |
function serve_file( $requested_file ) { | |
$upload_dir = wp_upload_dir(); | |
$file = $upload_dir['basedir'] . '/' . $requested_file; | |
$file = apply_filters( 'subscribers_only_serve_file', $file ); | |
if ( 0 !== validate_file( $requested_file ) || ! is_file( $file ) ) { | |
status_header( 404 ); | |
die( '404 — File not found.' ); | |
} | |
// We may override this later. | |
status_header( 200 ); | |
// The rest comes from wp-includes/ms-files.php. | |
$mime = wp_check_filetype( $file ); | |
if ( false === $mime[ 'type' ] && function_exists( 'mime_content_type' ) ) | |
$mime[ 'type' ] = mime_content_type( $file ); | |
if ( $mime[ 'type' ] ) | |
$mimetype = $mime[ 'type' ]; | |
else | |
$mimetype = 'image/' . substr( $file, strrpos( $file, '.' ) + 1 ); | |
header( 'Content-Type: ' . $mimetype ); // always send this | |
header( 'Content-Length: ' . filesize( $file ) ); | |
$last_modified = gmdate( 'D, d M Y H:i:s', filemtime( $file ) ); | |
$etag = '"' . md5( $last_modified ) . '"'; | |
header( "Last-Modified: $last_modified GMT" ); | |
header( 'ETag: ' . $etag ); | |
header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', time() + 100000000 ) . ' GMT' ); | |
// Support for Conditional GET | |
$client_etag = isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ? stripslashes( $_SERVER['HTTP_IF_NONE_MATCH'] ) : false; | |
if( ! isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) | |
$_SERVER['HTTP_IF_MODIFIED_SINCE'] = false; | |
$client_last_modified = trim( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ); | |
// If string is empty, return 0. If not, attempt to parse into a timestamp | |
$client_modified_timestamp = $client_last_modified ? strtotime( $client_last_modified ) : 0; | |
// Make a timestamp for our most recent modification... | |
$modified_timestamp = strtotime($last_modified); | |
if ( ( $client_last_modified && $client_etag ) | |
? ( ( $client_modified_timestamp >= $modified_timestamp) && ( $client_etag == $etag ) ) | |
: ( ( $client_modified_timestamp >= $modified_timestamp) || ( $client_etag == $etag ) ) | |
) { | |
status_header( 304 ); | |
exit; | |
} | |
// If we made it this far, just serve the file | |
readfile( $file ); | |
} | |
} | |
new Subscribers_Only; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks, Alex!