Commit d4722347 authored by Michael Pretty's avatar Michael Pretty
Browse files

switching to generic cdn rewrite plugin

parent f0ab5904
<?php
/*
Plugin Name: Cloudfiles CDN
Plugin Name: CDN Rewrite
Plugin URI: http://voceconnect.com/
Description: Adds/Deletes uploaded images on CDN and rewrites asset URLs to CDN
Description: Rewrites asset URLs to CDN
Version: 0.1
Author: Chris Scott, Michael Pretty
Author URI: http://voceconnect.com/
......@@ -10,54 +10,55 @@ Author URI: http://voceconnect.com/
require_once('voce-settings.php');
class CloudfilesCdn {
class CDN_Rewrite {
var $submenu_general;
const OPTION_GENERAL = 'cdn_general';
//private static $option_group = 'cloudfiles_cdn';
const OPTION_GENERAL = 'cloudfiles_cdn_general';
private $submenu_general;
private $file_extensions;
private $blog_details;
private $cdn_root_url;
/**
* get general setting
*
* @param string $setting setting name
* @return mixed setting value or false if not set
*/
public static function get_setting($setting) {
$settings = get_option(self::OPTION_GENERAL);
if(!$settings || !is_array($settings)) {
$settings = array(
'file_extensions' => 'bmp|bz2|css|gif|ico|gz|jpg|jpeg|js|mp3|pdf|png|rar|rtf|swf|tar|tgz|txt|wav|zip'
);
}
return (isset($settings[$setting])) ? $settings[$setting] : false;
public function __construct() {
$this->file_extensions = $this->get_setting('file_extensions');
$this->cdn_root_url = untrailingslashit($this->get_setting('root_url'));
}
public function __construct() {}
public function initialize() {
// relies on Voce_Settings
if (!class_exists('Voce_Settings')) {
return;
}
add_action('admin_menu', array($this, 'add_options_page'));
if (!self::get_setting('username') || !self::get_setting('api_key') || !self::get_setting('container') || !self::get_setting('root_url') || !self::get_setting('file_extensions')) {
if ('' == $this->file_extensions || '' == $this->cdn_root_url) {
add_action('admin_notices', array($this, 'settings_warning'));
return;
}
add_filter('wp_handle_upload', array($this, 'catch_wp_handle_upload'));
add_filter('wp_delete_file', array($this, 'catch_wp_delete_file'));
add_filter('wp_generate_attachment_metadata', array($this, 'catch_wp_generate_attachment_metadata'));
add_filter('bp_core_avatar_cropstore', array($this, 'catch_bp_core_avatar_cropstore'));
add_action('bp_core_avatar_save', array($this, 'catch_bp_core_avatar_save'), 10, 2);
if('/' != $this->cdn_root_url) {
add_action('template_redirect', array($this, 'start_buffer'), 1);
}
}
/**
* get general setting
*
* @param string $setting setting name
* @return mixed setting value or false if not set
*/
private function get_setting($setting) {
$settings = get_option(self::OPTION_GENERAL);
if(!$settings || !is_array($settings)) {
$settings = array(
'file_extensions' => 'bmp|bz2|css|gif|ico|gz|jpg|jpeg|js|mp3|pdf|png|rar|rtf|swf|tar|tgz|txt|wav|zip'
);
update_option(self::OPTION_GENERAL, $settings);
}
return (isset($settings[$setting])) ? $settings[$setting] : false;
}
public function settings_warning() {
echo "<div class='update-nag'>The Cloudfiles CDN plugin is missing some required settings.</div>";
echo "<div class='update-nag'>The CDN Rewrite plugin is missing some required settings.</div>";
}
/**
......@@ -66,17 +67,12 @@ class CloudfilesCdn {
* @return void
*/
public function add_options_page() {
$this->submenu_general = add_options_page('Cloudfiles CDN', 'Cloudfiles CDN', 'manage_options', self::OPTION_GENERAL, array($this, 'submenu_general'));
$this->submenu_general = add_options_page('CDN Rewrite', 'CDN Rewrite', 'manage_options', self::OPTION_GENERAL, array($this, 'submenu_general'));
$settings = new Voce_Settings(self::OPTION_GENERAL, self::OPTION_GENERAL);
$section = $settings->add_section('api', 'Cloudfiles API Settings', $this->submenu_general);
$section->add_field('username', 'Username (required)', 'field_input');
$section->add_field('api_key', 'API Key (required)', 'field_input');
$section->add_field('container', 'Container Name (required)', 'field_input', array('description' => 'The container to store files in.'));
$section->add_field('root_url', 'Root URL (required)', 'field_input', array('description' => 'The root URL to the container without a trailing slash.'));
$section = $settings->add_section('api', 'CDN Rewrite Settings', $this->submenu_general);
$section->add_field('root_url', 'CDN Root URL (required)', 'field_input', array('description' => 'The base URL of the CDN.'));
$section->add_field('file_extensions', 'File Extensions (required)', 'field_input');
$section->add_field('enable_debug', 'Enable Debugging?', 'field_checkbox', array('description' => 'Enable error_log() to log upload/delete actions.'));
}
/**
......@@ -87,7 +83,7 @@ class CloudfilesCdn {
function submenu_general() {
?>
<div class="wrap">
<h2>Cloudfiles CDN Settings</h2>
<h2>CDN Rewrite Settings</h2>
<form method="post" action="options.php">
<?php settings_fields(self::OPTION_GENERAL); ?>
<?php do_settings_sections($this->submenu_general); ?>
......@@ -99,307 +95,6 @@ class CloudfilesCdn {
<?php
}
/**
* delete old BP avatars on save
*
* @param string $user_id not used
* @param string $old the path to the file being deleted
* @return void
*/
public function catch_bp_core_avatar_save($user_id, $old) {
$files = array();
// this will be -avatar2
$files[] = str_replace(ABSPATH, '', $old);
// get -avatar1 also
$files[] = str_replace('-avatar2', '-avatar1', $files[0]);
foreach ($files as $file) {
if (self::get_setting('enable_debug'))
error_log("DELETING OLD BP AVATAR: $file");
$this->delete_file($file);
}
}
/**
* when BP avatars are cropped, catch the cropped sizes and upload
*
* @param string $files
* @return array the original file array
*/
public function catch_bp_core_avatar_cropstore($files) {
foreach ((array) $files as $file) {
$relative_file_path = str_replace(ABSPATH, '', $file);
$file_type = wp_check_filetype($file);
if (self::get_setting('enable_debug'))
error_log("UPLOADING BP AVATAR: $relative_file_path");
$this->upload_file($file, $file_type['type'], $relative_file_path);
}
return $files;
}
/**
* go grab the already generated intermediate sizes and upload
*
* @param string $metadata
* @return array updated metadata
*/
public function catch_wp_generate_attachment_metadata($metadata) {
//error_log("WP_GENERATE_ATTACHMENT_METADATA: " . var_export($metadata, true));
$upload_dir = wp_upload_dir();
$upload_path = trailingslashit($upload_dir['path']);
$sizes = $metadata['sizes'];
foreach ((array) $sizes as $size => $size_data ) {
$file = $size_data['file'];
if (is_multisite()) {
$relative_file_path = self::get_blog_path() . 'files' . trailingslashit($upload_dir['subdir']) . $file;
} else {
$upload_dir = wp_upload_dir();
$relative_file_path = str_replace(ABSPATH, '', $upload_dir['basedir']) . trailingslashit($upload_dir['subdir']) . $file;
}
$file_type = wp_check_filetype($file);
if (self::get_setting('enable_debug'))
error_log("UPLOADING INTERMEDIATE SIZE: $relative_file_path");
$this->upload_file($upload_path . $file, $file_type['type'], $relative_file_path);
}
return $metadata;
}
/**
* get a local filename from a CDN URL
*
* @param string $url
* @return string filename
*/
private function get_local_filename($url) {
return ABSPATH . str_replace(trailingslashit(self::get_setting('root_url')), '', $url);
}
/**
* Filter to handle wp_handle_upload for uploaded files. Logs any errors.
*
* @param string $upload
* @return void
*/
public function catch_wp_handle_upload($upload) {
// check for buddypress avatar upload and don't upload since it resizes and deletes this one
if (function_exists('bp_core_setup_globals') && strpos($upload['file'], '/avatars/') !== false) {
return $upload;
}
$blog_path = $this->get_blog_path();
$relative_url = $blog_path . $this->remove_site_url($upload['url']);
if (self::get_setting('enable_debug'))
error_log("UPLOADING: $relative_url");
// upload file
if (!$this->upload_file($upload['file'], $upload['type'], $relative_url)) {
error_log("[CloudfilesCdn] Error uploading file: $relative_url");
return $upload;
}
return $upload;
}
/**
* Filter to handle wp_delete_file for deleted files. Deletes the file
* from the CDN.
*
* @param string $file
* @return string the original file path
*/
public function catch_wp_delete_file($file) {
//error_log("WP_DELETE_FILE: " . var_export($file, true));
if (is_multisite()) {
// not sure if this is needed or not...
if (strpos($file, 'blogs.dir')) {
$parts = explode('files/', $file);
$file = $parts[1];
}
$file = $this->get_blog_path() . 'files/' . $file;
}
// delete the file from the CDN
if (self::get_setting('enable_debug'))
error_log("DELETING FILE: $file");
$this->delete_file(str_replace(ABSPATH, '', $file));
return $file;
}
/**
* get the blog's path without the leading slash
*
* @return string
*/
private function get_blog_path() {
global $blog_id;
$blog_path = '';
if ((int) $blog_id !== 1) {
$blog_details = get_blog_details($blog_id);
$blog_path = $blog_details->path;
}
return ltrim($blog_path, '/');
}
/**
* Get the relative filename. The filename from the wp_delete_file filter
* will either have the file path to the WP_UPLOAD_DIR prepended to the cdn'd file
* url or it will be the path relative to the WP_UPLOAD_DIR
*
* @param string $file
* @return void
*/
private function get_relative_file($file) {
$cont_url = self::get_setting('root_url');
if (strpos($file, $cont_url) !== false) {
if ($file_parts = explode(trailingslashit(self::get_setting('root_url')), $file)) {
// prepended w/bad upload path (e.g. http://c0002127.cdn1.cloudfiles.rackspacecloud.com/wp-content/uploads/2010/07/http://c0002127.cdn1.cloudfiles.rackspacecloud.com/wp-content/uploads/2010/07/653106995_338e53fb1416-150x150.jpg)
if (isset($file_parts[2])) {
return $file_parts[2];
} elseif (isset($file_parts[1])) {
// just regular CDN url
return $file_parts[1];
}
}
}
return false;
}
/**
* delete a file from the CDN
*
* @param string $file relative filename
* @return bool true on success, false on failure
*/
private function delete_file($file) {
require_once(trailingslashit(dirname(__FILE__)) . 'cloudfiles/cloudfiles.php');
$auth = new CF_Authentication(self::get_setting('username'), self::get_setting('api_key'));
try {
$auth->authenticate();
} catch (Exception $e) {
error_log(sprintf("[CloudfilesCdn] Error authenticating to Cloudfiles: %s", $e->getMessage()));
return false;
}
$conn = new CF_Connection($auth);
$container = $conn->get_container(self::get_setting('container'));
try {
$obj = $container->get_object($file);
$container->delete_object($obj);
} catch (Exception $e) {
error_log(sprintf("[CloudfilesCdn] Error deleting file '%s' from Cloudfiles: %s", $file, $e->getMessage()));
}
if (self::get_setting('enable_debug'))
error_log("DELETED FILE: $file");
return true;
}
/**
* Update the attachement URL if the attached file is on the CDN
*
* @param string $url
* @param string $post_id
* @return string original or updated URL
*/
public function cdn_attachment_url($url, $post_id) {
if ($file = get_post_meta($post_id, '_wp_attached_file', true)) {
if (strpos($file, self::get_setting('root_url')) !== false) {
return $file;
}
}
return $url;
}
/**
* Upload a file to cloudfiles
*
* @param string $file the file's path
* @param string $file_type the file's mime type
* @param string $file_url the file's site-relative URL
* @return bool true on succes, false on fail
*/
private function upload_file($file, $file_type, $file_url) {
require_once(trailingslashit(dirname(__FILE__)) . 'cloudfiles/cloudfiles.php');
$auth = new CF_Authentication(self::get_setting('username'), self::get_setting('api_key'));
try {
$auth->authenticate();
} catch (Exception $e) {
error_log(sprintf("[CloudfilesCdn] Error authenticating to Cloudfiles: %s", $e->getMessage()));
return false;
}
$conn = new CF_Connection($auth);
$container = $conn->get_container(self::get_setting('container'));
$obj = $container->create_object($file_url);
$obj->content_type = $file_type;
try {
$obj->load_from_filename($file);
} catch (Exception $e) {
error_log(sprintf("[CloudfilesCdn] Error uploading '%s' to Cloudfiles: %s", $file, $e->getMessage()));
return false;
}
if (self::get_setting('enable_debug'))
error_log("UPLOADED: $file, $file_type, $file_url");
return true;
}
/**
* Get a site-relative url without a leading / from an absolute URL containing the siteurl
*
* @param string $absolute_url
* @return string relative url
*/
private function remove_site_url($absolute_url) {
return str_replace(trailingslashit(get_option('siteurl')), '', $absolute_url);
}
private function remove_cdn_url($url) {
return str_replace(trailingslashit(self::get_setting('root_url')), '', $url);
}
}
add_action('init', array(new CloudfilesCdn(), 'initialize'));
class CDN_Rewrite {
private $file_extensions;
private $blog_details;
private $cdn_root_url;
public function __construct() {
$this->file_extensions = CloudfilesCdn::get_setting('file_extensions');
$this->cdn_root_url = untrailingslashit(CloudfilesCdn::get_setting('root_url'));
}
/**
* Initializes class and registers hooks
*
*/
public function initialize() {
if('/' != $this->cdn_root_url) {
add_action('template_redirect', array($this, 'start_buffer'), 1);
}
}
/**
* start output buffering.
*
......@@ -469,6 +164,7 @@ class CDN_Rewrite {
}
return $this->cdn_root_url . $path;
}
}
add_action('init', array(new CDN_Rewrite(), 'initialize'));
......@@ -484,7 +180,7 @@ class CDN_VersionAssets {
public function initialize() {
add_filter('style_loader_src', array($this, 'replace_version'), 10);
add_filter('script_loader_src', array($this, 'replace_version'), 10);
add_filter('style_loader_src', array($this, 'replace_version'), 10);
add_filter('stylesheet_uri', array($this, 'replace_version'), 10);
}
public function on_template_redirect() {
......
This diff is collapsed.
<?php
/**
* Custom Exceptions for the CloudFiles API
*
* Requres PHP 5.x (for Exceptions and OO syntax)
*
* See COPYING for license information.
*
* @author Eric "EJ" Johnson <ej@racklabs.com>
* @copyright Copyright (c) 2008, Rackspace US, Inc.
* @package php-cloudfiles-exceptions
*/
/**
* Custom Exceptions for the CloudFiles API
* @package php-cloudfiles-exceptions
*/
class SyntaxException extends Exception { }
class AuthenticationException extends Exception { }
class InvalidResponseException extends Exception { }
class NonEmptyContainerException extends Exception { }
class NoSuchObjectException extends Exception { }
class NoSuchContainerException extends Exception { }
class NoSuchAccountException extends Exception { }
class MisMatchedChecksumException extends Exception { }
class IOException extends Exception { }
class CDNNotEnabledException extends Exception { }
class BadContentTypeException extends Exception { }
class InvalidUTF8Exception extends Exception { }
class ConnectionNotOpenException extends Exception { }
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* c-hanging-comment-ender-p: nil
* End:
*/
?>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -61,7 +61,7 @@ if (!class_exists('Voce_Settings')) {
* @param string $extra_args extra args to pass to the callback
* @return void
*/
function add_settings_field($id, $label, $display_callback, $page, $section, $extra_args = null) {
public function add_settings_field($id, $label, $display_callback, $page, $section, $extra_args = null) {
$args = array(
'id' => $id,
......@@ -89,7 +89,7 @@ if (!class_exists('Voce_Settings')) {
* @param string $args
* @return string checkbox
*/
function field_checkbox($args) {
public function field_checkbox($args) {
$options = get_option($this->option_name);
$defaults = array('prepend_field' => '', 'append_field' => '', 'class' => '');
......@@ -112,107 +112,107 @@ if (!class_exists('Voce_Settings')) {
);
}
/**
* input type text callback for settings API
*
* @param string $args
* @return string input
*/
function field_input($args) {
$options = get_option($this->option_name);
$defaults = array('prepend_field' => '', 'append_field' => '', 'class' => 'regular-text', 'type' => 'text');
$args = wp_parse_args($args, $defaults);
extract($args);
$description = (isset($description) && $description) ? sprintf('<p><span class="description">%s</span></p>', $description) : '';
if (!isset($value)) {
if (isset($options[$id])) {
$value = $options[$id];
} else {
$value = '';
}
}
/**
* input type text callback for settings API
*
* @param string $args
* @return string input
*/
public function field_input($args) {
$options = get_option($this->option_name);
echo sprintf(
"%s<input id='%s' name='{$this->option_name}[%s]' type='%s' class='%s' value='%s' />%s%s",
$prepend_field,
$id,
$id,
$type,
$class,
esc_attr($value),
$append_field,
$description
);
$defaults = array('prepend_field' => '', 'append_field' => '', 'class' => 'regular-text', 'type' => 'text');
$args = wp_parse_args($args, $defaults);
extract($args);
$description = (isset($description) && $description) ? sprintf('<p><span class="description">%s</span></p>', $description) : '';
if (!isset($value)) {
if (isset($options[$id])) {
$value = $options[$id];
} else {
$value = '';
}
}
/**
* textarea callback for settings API
*
* @param string $args
* @return string textarea
*/
function field_textarea($args) {
$options = get_option($this->option_name);
$id = $args['id'];
echo sprintf(
"%s<input id='%s' name='{$this->option_name}[%s]' type='%s' class='%s' value='%s' />%s%s",
$prepend_field,
$id,
$id,
$type,
$class,
esc_attr($value),
$append_field,
$description
);
}
$defaults = array('prepend_field' => '', 'append_field' => '', 'columns' => 40, 'rows' => 8, 'class' => 'large-text');
$args = wp_parse_args($args, $defaults);
extract($args);
/**
* textarea callback for settings API
*
* @param string $args
* @return string textarea
*/
public function field_textarea($args) {
$options = get_option($this->option_name);
$id = $args['id'];
$description = (isset($description) && $description) ? sprintf('<p><span class="description">%s</span></p>', $description) : '';
$defaults = array('prepend_field' => '', 'append_field' => '', 'columns' => 40, 'rows' => 8, 'class' => 'large-text');
$args = wp_parse_args($args, $defaults);
extract($args);
echo sprintf(
"%s<textarea id='%s' name='{$this->option_name}[%s]' columns='%s' rows='%s' class='%s' />%s</textarea>%s%s",
$prepend_field,
$id,