Initial commit

This commit is contained in:
Alex Rennie-Lis committed 2026-11-02 11:04:39 +00:00
commit f9f65dc84d
23 files changed
+5074

No files matched your search

@@ -0,0 +1,355 @@
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
/**
* Simply Static URL fetcher class
*
* @package Simply_Static
*/
class Simply_Static_Archive_Creator {
/**
* The path to the archive directory
*
* @var string
*/
protected $archive_dir;
/**
* Export log (list of processed urls)
*
* @var array
*/
protected $export_log = array();
/**
* The slug (id) used for the plugin
*
* @var string
*/
protected $slug;
/**
* The protocol used for the destination URL
*
* @var string
*/
protected $destination_scheme;
/**
* The host for the destination URL
*
* @var string
*/
protected $destination_host;
/**
* The directory where static files should be saved
*
* @var string
*/
protected $temp_files_dir;
/**
* Additional urls to add to the archive
*
* @var string
*/
protected $additional_urls;
/**
* Constructor
*
* @param string $url URI resource
*/
public function __construct( $slug, $destination_scheme, $destination_host, $temp_files_dir, $additional_urls ) {
$this->slug = $slug;
$this->destination_scheme = $destination_scheme;
$this->destination_host = $destination_host;
$this->temp_files_dir = $temp_files_dir;
$this->additional_urls = $additional_urls;
}
/**
* Get the list of URLs in the archive
*
* @param array $export_log URLs that were successfully added to the archive
*/
public function get_export_log() {
return $this->export_log;
}
/**
* Create a static version of the site
*
* @return void
*/
public function create_archive() {
global $blog_id;
// TODO: Do ajax calls instead of just running forever and ever
set_time_limit(0);
// Create archive directory
$current_user = wp_get_current_user();
$archive_name = join( '-', array( $this->slug, $blog_id, time(), $current_user->user_login ) );
$this->archive_dir = trailingslashit( $this->temp_files_dir . $archive_name );
if ( ! file_exists( $this->archive_dir ) ) {
wp_mkdir_p( $this->archive_dir );
}
// Add URLs to queue
$origin_url = home_url();
$destination_url = $this->destination_scheme . '://' . $this->destination_host;
$urls_queue = array_unique( array_merge(
array( trailingslashit( $origin_url ) ),
// using preg_split to intelligently break at newlines
// see: http://stackoverflow.com/questions/1483497/how-to-put-string-in-array-split-by-new-line
preg_split( "/\r\n|\n|\r/", $this->additional_urls )
) );
while ( count( $urls_queue ) ) {
$current_url = array_shift( $urls_queue );
$response = Simply_Static_Url_Fetcher::fetch( $current_url );
// If we get a WP_Error then somehow our request failed (e.g. space in URL)
// TODO: Keep a queue of failed urls too
if ( is_wp_error( $response ) ) {
continue;
}
$url_parts = parse_url( $response->url );
$path = $url_parts['path'];
$is_html = $response->is_html();
// If we get a 30x redirect...
if ( in_array( $response->code, array( 301, 302, 303, 307 ) ) ) {
$redirect_url = $response->get_redirect_url();
// WP likes to 301 redirect `/path` to `/path/` -- we want to
// check for this and just add the trailing slashed version
if ( $redirect_url === trailingslashit( $current_url ) ) {
$urls_queue = $this->add_url_to_queue( $urls_queue, $redirect_url );
} else {
/// convert our potentially relative URL to an absolute URL
$redirect_url = sist_relative_to_absolute_url( $redirect_url, $current_url );
if ( $redirect_url ) {
// check if this is a local URL
if ( sist_is_local_url( $redirect_url ) ) {
// add the redirected page to the queue
$urls_queue = $this->add_url_to_queue( $urls_queue, $redirect_url );
// and update the URL
$redirect_url = str_replace( $origin_url, $destination_url, $redirect_url );
}
$view = new Simply_Static_View();
$content = $view->set_template( 'redirect' )
->assign( 'redirect_url', $redirect_url )
->render_to_string();
$this->save_url_to_file( $path, $content, $is_html );
$this->export_log[] = $current_url;
}
}
continue;
}
// Not a 200 for the response code? Move on.
// TODO: Keep a queue of failed urls too
if ( $response->code != 200 ) {
continue;
}
$this->export_log[] = $current_url;
// Fetch all URLs from the page and add them to the queue...
$urls = $response->extract_urls();
foreach ( $urls as $url ) {
$urls_queue = $this->add_url_to_queue( $urls_queue, $url );
}
// Replace the origin URL with the destination URL
$response->replace_url( $origin_url, $destination_url );
// Save the page to our archive
$content = $response->body;
$this->save_url_to_file( $path, $content, $is_html );
}
}
/**
* Add a URL to the processing queue if we haven't already processed it and
* if it's not in the queue to be processed
*
* @param array $urls_queue Queue of URLs to be processed
* @param string $url URL to add to the processing queue
* @return array Queue of URLs to be processed
*/
private function add_url_to_queue( $urls_queue, $url ) {
if ( ! in_array( $url, $this->export_log ) && ! in_array( $url, $urls_queue ) ) {
$urls_queue[] = $url;
}
return $urls_queue;
}
/**
* Create a ZIP file using the archive directory
*
* @return string|WP_Error $temporary_zip The path to the archive zip file
*/
public function create_zip() {
$temporary_zip = untrailingslashit( $this->archive_dir ) . '.tmp';
$zip_archive = new ZipArchive();
if ( $zip_archive->open( $temporary_zip, ZIPARCHIVE::CREATE ) !== true ) {
return new WP_Error( 'create_zip_failed', __( 'Unable to create ZIP archive', $this->slug ) );
}
$iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $this->archive_dir ) );
foreach ( $iterator as $file_name => $file_object ) {
$base_name = basename( $file_name );
if ( $base_name != '.' && $base_name != '..' ) {
if ( ! $zip_archive->addFile( realpath( $file_name ), str_replace( $this->archive_dir, '', $file_name ) ) ) {
return new WP_Error( 'cannot_add_file_to_zip', sprintf( __( "Could not add file: %s", $this->slug ), $file_name ) );
}
}
}
$zip_archive->close();
$zip_file = untrailingslashit( $this->archive_dir ) . '.zip';
rename( $temporary_zip, $zip_file );
$archive_url = str_replace( plugin_dir_path( dirname( __FILE__ ) ), plugin_dir_url( dirname( __FILE__ ) ), $zip_file );
return $archive_url;
}
/**
* Publish to AWS S3 bucket
*
* @return boolean|WP_Error
*/
public function publish_to_s3( $bucket, $aws_access_key_id, $aws_secret_access_key, $aws_s3_region = 'us-east-1', $aws_s3_endpoint = 's3.amazonaws.com' ) {
$directory_iterator = new RecursiveDirectoryIterator( $this->archive_dir, RecursiveDirectoryIterator::SKIP_DOTS );
$recursive_iterator = new RecursiveIteratorIterator( $directory_iterator, RecursiveIteratorIterator::SELF_FIRST );
S3::$useExceptions = true;
$useSSL = true;
$endpoint = $aws_s3_endpoint;
if ( preg_match( '#^https://#i', $endpoint ) ) {
$useSSL = true;
$endpoint = preg_replace( '#^https://#i', '', $endpoint );
} elseif ( preg_match( '#^http://#i', $endpoint ) ) {
$useSSL = false;
$endpoint = preg_replace( '#^http://#i', '', $endpoint );
}
$endpoint = untrailingslashit( $endpoint );
if ( empty( $endpoint ) ) {
$endpoint = 's3.amazonaws.com';
}
$s3 = new S3( $aws_access_key_id, $aws_secret_access_key, $useSSL, $endpoint, $aws_s3_region );
S3::setRegion( $aws_s3_region );
foreach ( $recursive_iterator as $item ) {
if (!$item->isDir()) {
$path = $recursive_iterator->getSubPathName();
try {
$s3->putObject( S3::inputFile( $item->getRealPath() ), $bucket, $path, S3::ACL_PUBLIC_READ );
} catch (any $err) {
return new WP_Error( 'cannot_publish_to_s3', sprintf( __( "Could not publish file to S3: %s: %s", $this->slug, $err ), $path ) );
}
}
}
return true;
}
/**
* Copy static files to a local directory
*
* @return boolean|WP_Error
*/
public function copy_static_files( $local_dir ) {
$directory_iterator = new RecursiveDirectoryIterator( $this->archive_dir, RecursiveDirectoryIterator::SKIP_DOTS );
$recursive_iterator = new RecursiveIteratorIterator( $directory_iterator, RecursiveIteratorIterator::SELF_FIRST );
foreach ( $recursive_iterator as $item ) {
$path = $local_dir . $recursive_iterator->getSubPathName();
$success = $item->isDir() ? wp_mkdir_p( $path ) : copy( $item, $path );
if ( ! $success ) {
return new WP_Error( 'cannot_create_file_or_dir', sprintf( __( "Could not create file or directory: %s", $this->slug ), $path ) );
}
}
return true;
}
/**
* Delete generated static files
*
* @param $archive_dir The archive directory path
* @return boolean|WP_Error
*/
public function delete_static_files() {
$directory_iterator = new RecursiveDirectoryIterator( $this->archive_dir, FilesystemIterator::SKIP_DOTS );
$recursive_iterator = new RecursiveIteratorIterator( $directory_iterator, RecursiveIteratorIterator::CHILD_FIRST );
// recurse through the entire directory and delete all files / subdirectories
foreach ( $recursive_iterator as $item ) {
$success = $item->isDir() ? rmdir( $item ) : unlink( $item );
if ( ! $success ) {
return new WP_Error( 'cannot_delete_file_or_dir', sprintf( __( "Could not delete temporary file or directory: %s", $this->slug ), $item ) );
}
}
// must make sure to delete the original directory at the end
$success = rmdir( $this->archive_dir );
if ( ! $success ) {
return new WP_Error( 'cannot_delete_file_or_dir', sprintf( __( "Could not delete temporary file or directory: %s", $this->slug ), $item ) );
}
return true;
}
/**
* Save the contents of a page to a file in our archive directory
*
* @param string $path The relative path for the URL to save
* @param string $content The contents of the page we want to save
* @param boolean $is_html Is this an html page?
* @return boolean $success Did we successfully save the file?
*/
protected function save_url_to_file( $path, $content, $is_html ) {
$path_info = pathinfo( $path && $path != '/' ? $path : 'index.html' );
// Create file directory if it doesn't exist
$file_dir = $this->archive_dir . ( $path_info['dirname'] ? $path_info['dirname'] : '' );
if ( empty( $path_info['extension'] ) && $path_info['basename'] == $path_info['filename'] ) {
$file_dir .= '/' . $path_info['basename'];
$path_info['filename'] = 'index';
}
if ( ! file_exists( $file_dir ) ) {
wp_mkdir_p( $file_dir );
}
// Save file contents
$file_extension = ( $is_html || ! isset( $path_info['extension'] ) ) ? 'html' : $path_info['extension'];
$file_name = $file_dir . '/' . $path_info['filename'] . '.' . $file_extension;
$success = file_put_contents( $file_name, $content );
return $success;
}
}
+69
View File
@@ -0,0 +1,69 @@
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
/**
* Simply Static options class
*
* @package Simply_Static
*/
class Simply_Static_Options {
/**
* Options array
*
* @var array
*/
protected $options = array();
/**
* Defines options record in the wp_options table
*
* @var string
*/
protected $option_key = null;
/**
* Performs initializion of the options structure
*
* @param string $option_key The options key name
*/
public function __construct( $option_key ) {
$options = get_option( $option_key );
if ( false === $options ) {
$options = array();
}
$this->options = $options;
$this->option_key = $option_key;
}
/**
* Updates the option identified by $name with the value provided in $value
*
* @param string $name The option name
* @param mixed $value The option value
* @return Simply_Static_Options
*/
public function set( $name, $value ) {
$this->options[$name] = $value;
return $this;
}
/**
* Returns a value of the option identified by $name
*
* @param string $name The option name
* @return mixed|null
*/
public function get( $name ) {
return array_key_exists( $name, $this->options ) ? $this->options[$name] : null;
}
/**
* Saves the internal options data to the wp_options table using the stored $option_key value as the key
*
* @return boolean
*/
public function save() {
return update_option( $this->option_key, $this->options );
}
}
@@ -0,0 +1,211 @@
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
/**
* Simply Static URL extractor class
*
* @package Simply_Static
*/
class Simply_Static_Url_Extractor {
/**
* The following pages were incredibly helpful:
* - http://stackoverflow.com/questions/2725156/complete-list-of-html-tag-attributes-which-have-a-url-value
* - http://nadeausoftware.com/articles/2008/01/php_tip_how_extract_urls_web_page
* - http://php.net/manual/en/book.dom.php
*/
/** @const */
protected static $match_elements = array(
// HTML
'a' => array( 'href', 'urn' ),
'base' => array( 'href' ),
'form' => array( 'action', 'data' ),
'img' => array( 'src', 'usemap', 'longdesc', 'dynsrc', 'lowsrc' ),
'link' => array( 'href' ),
'applet' => array( 'code', 'codebase', 'archive', 'object' ),
'area' => array( 'href' ),
'body' => array( 'background', 'credits', 'instructions', 'logo' ),
'input' => array( 'src', 'usemap', 'dynsrc', 'lowsrc', 'action', 'formaction' ),
'blockquote' => array( 'cite' ),
'del' => array( 'cite' ),
'frame' => array( 'longdesc', 'src' ),
'head' => array( 'profile' ),
'iframe' => array( 'longdesc', 'src' ),
'ins' => array( 'cite' ),
'object' => array( 'archive', 'classid', 'codebase', 'data', 'usemap' ),
'q' => array( 'cite' ),
'script' => array( 'src' ),
'audio' => array( 'src' ),
'command' => array( 'icon' ),
'embed' => array( 'src', 'code', 'pluginspage' ),
'event-source' => array( 'src' ),
'html' => array( 'manifest', 'background', 'xmlns' ),
'source' => array( 'src' ),
'video' => array( 'src', 'poster' ),
'bgsound' => array( 'src' ),
'div' => array( 'href', 'src' ),
'ilayer' => array( 'src' ),
'table' => array( 'background' ),
'td' => array( 'background' ),
'th' => array( 'background' ),
'layer' => array( 'src' ),
'xml' => array( 'src' ),
'button' => array( 'action', 'formaction' ),
'datalist' => array( 'data' ),
'select' => array( 'data' ),
'access' => array( 'path' ),
'card' => array( 'onenterforward', 'onenterbackward', 'ontimer' ),
'go' => array( 'href' ),
'option' => array( 'onpick' ),
'template' => array( 'onenterforward', 'onenterbackward', 'ontimer' ),
'wml' => array( 'xmlns' )
);
// /** @const */
// protected static $match_metas = array(
// 'content-base',
// 'content-location',
// 'referer',
// 'location',
// 'refresh',
// );
/**
* The URL request response
*
* @var Simply_Static_Url_Response
*/
protected $response;
/**
* The url of the site
*
* @var array
*/
protected $extracted_urls = array();
/**
* Constructor
*
* @param string $page_contents The contents of the html/css/etc. page/file
* @param string $url The url of the site
*/
public function __construct( $response ) {
$this->response = $response;
}
/**
* Extracts URLs from the response
*
* Returns a list of unique URLs from the body of the response. It only
* extracts URLs from the same domain, either absolute urls or relative urls
* that are then converted to absolute urls.
*
* Note that no validation is performed on whether the URLs would actually
* return a 200/OK response.
*
* @return array $urls
*/
public function extract_urls() {
if ( $this->response->is_html() ) {
$this->extract_urls_from_html();
}
if ( $this->response->is_css() ) {
$this->extract_urls_from_css( $this->response->body );
}
return array_unique( $this->extracted_urls );
}
/**
* Loops through all elements in the DOM to pull out URLs
*
* @return void
*/
private function extract_urls_from_html() {
$doc = new DOMDocument();
libxml_use_internal_errors( true );
$doc->loadHTML( $this->response->body );
libxml_use_internal_errors( false );
// get all elements on the page
$elements = $doc->getElementsByTagName( '*' );
foreach ( $elements as $element ) {
$tag_name = $element->tagName;
if( $tag_name === 'style' ) {
$this->extract_urls_from_css( $element->nodeValue );
} else {
$style_attr_value = $element->getAttribute( 'style' );
if ( $style_attr_value !== '' ) {
$this->extract_urls_from_css( $style_attr_value );
}
if ( array_key_exists( $tag_name, self::$match_elements ) ) {
$match_attributes = self::$match_elements[ $tag_name ];
foreach ( $match_attributes as $attribute_name ) {
$extracted_url = $element->getAttribute( $attribute_name );
$this->add_to_extracted_urls( $extracted_url );
}
}
}
}
}
/**
* Use regex to extract URLs on CSS pages
*
* URLs in CSS follow three basic patterns:
* - @import "common.css" screen, projection;
* - @import url("fineprint.css") print;
* - background-image: url(image.png);
*
* URLs are either contained within url(), part of an @import statement,
* or both.
*
* @return void
*/
private function extract_urls_from_css( $text ) {
$patterns = array( "/url\(\s*[\"']?([^)\"']+)/", // url()
"/@import\s+[\"']([^\"']+)/" ); // @import w/o url()
foreach ( $patterns as $pattern ) {
if ( preg_match_all( $pattern, $text, $matches, PREG_PATTERN_ORDER ) === false ) {
return;
}
foreach ( $matches[1] as $match ) {
if ( !empty($match) ) {
$this->add_to_extracted_urls( $match );
}
}
}
}
/**
* Add an extracted URL (relative or absolute) to the extracted URLs array
*
* Absolute URLs are only added if the scheme/host matches the site it was
* extracted from. Relative URLs are converted to absolute URLs before being
* added to the array.
*
* @return void
*/
private function add_to_extracted_urls( $extracted_url ) {
$absolute_url = sist_relative_to_absolute_url( $extracted_url, $this->response->url );
if ( $absolute_url && sist_is_local_url( $absolute_url ) ) {
$this->extracted_urls[] = sist_remove_params_and_fragment( $absolute_url );
}
}
}
@@ -0,0 +1,41 @@
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
/**
* Simply Static URL fetcher class
*
* @package Simply_Static
*/
class Simply_Static_Url_Fetcher {
/**
* Timeout for fetching URLs
*
* @var string
*/
const TIMEOUT = 300;
/**
* Fetch the URL and return a WP_Error if we get one, otherwise a Response class.
*
* @return WP_Error|Simply_Static_Url_Response
*/
public static function fetch( $url ) {
// Don't process URLs that don't match the URL of this WordPress installation
if ( ! sist_is_local_url( $url ) ) {
return new WP_Error( 'attempting_to_fetch_remote_url', sprintf( __( "Attempting to fetch remote URL: %s", Simply_Static::SLUG ), $url ) );
}
$response = wp_remote_get( $url, array(
'timeout' => self::TIMEOUT,
'sslverify' => false, // not verifying SSL because all calls are local
'redirection' => 0 // disable redirection
) );
if ( is_wp_error( $response ) ) {
return $response;
} else {
return new Simply_Static_Url_Response( $url, $response );
}
}
}
@@ -0,0 +1,132 @@
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
/**
* Simply Static URL response class
*
* @package Simply_Static
*/
class Simply_Static_Url_Response {
/**
* The URI resource
*
* @var string
*/
public $url;
/**
* The headers from the response
*
* @var string
*/
public $headers = array();
/**
* The body content of the response
*
* @var string
*/
public $body = '';
/**
* The code from the response, e.g. 200
*
* @var integer
*/
public $code = null;
/**
* The message from the response, e.g. 'OK'
*
* @var string
*/
public $message = '';
/**
* Constructor
*
* @param string $url URI resource
*/
public function __construct( $url, $response ) {
$this->url = $url;
$this->headers = $response['headers'];
$this->body = $response['body'];
$this->code = $response['response']['code'];
$this->message = $response['response']['message'];
}
/**
* Returns the content type
*
* @return string
*/
public function get_content_type() {
return isset( $this->headers['content-type'] ) ? $this->headers['content-type'] : null;
}
/**
* Checks if content type is html
*
* @return bool
*/
public function is_html() {
return stripos( $this->get_content_type(), 'html' ) !== false;
}
/**
* Checks if content type is html
*
* @return bool
*/
public function is_css() {
return stripos( $this->get_content_type(), 'css' ) !== false;
}
/**
* Checks for a location header (redirection URL). Returns an empty string if
* it doesn't find one.
*
* @return string
*/
public function get_redirect_url() {
return isset( $this->headers['location'] ) ? $this->headers['location'] : '';
}
// /**
// * Removes WordPress-specific meta tags
// *
// * @return void
// */
// protected function cleanup() {
// if ( $this->is_html() ) {
// $response_body = preg_replace( '/<link rel=["\' ](pingback|alternate|EditURI|wlwmanifest|index|profile|prev)["\' ](.*?)>/si', '', $this->get_response_body() );
// $response_body = preg_replace( '/<meta name=["\' ]generator["\' ](.*?)>/si', '', $response_body );
// $this->set_response_body( $response_body );
// }
// }
/**
* Extracts the list of unique URLs
*
* @return array
*/
public function extract_urls() {
$extractor = new Simply_Static_Url_Extractor( $this );
return $extractor->extract_urls();
}
/**
* Replaces base URL
*
* @param string $origin_url
* @param string $destination_url
* @return void
*/
public function replace_url( $origin_url, $destination_url ) {
if ( $this->is_html() ) {
$response_body = str_replace( $origin_url, $destination_url, $this->body );
$this->body = $response_body;
}
}
}
+216
View File
@@ -0,0 +1,216 @@
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
/**
* Simply Static view class
*
* @package Simply_Static
*/
class Simply_Static_View {
/**
* Base directory for views
*
* @var string
*/
const DIRECTORY = 'views';
/**
* View script extension
*
* @var string
*/
const EXTENSION = '.phtml';
/**
* View variables array
*
* @var array
*/
protected $variables = array();
/**
* Absolute path for view
*
* @var string
*/
protected $path = null;
/**
* Template file name to render
*
* @var string
*/
protected $template = null;
/**
* Flashes are quick status messages displayed at the top of the page
*
* @var array
*/
protected $flashes = array();
/**
* Performs initialization of the absolute path for views
*/
public function __construct() {
// Looking for a basic directory where plugin resides
list($plugin_dir) = explode( '/', plugin_basename( __FILE__ ) );
// making up an absolute path to views directory
$path_array = array( WP_PLUGIN_DIR, $plugin_dir, self::DIRECTORY );
$this->path = implode( '/', $path_array );
}
/**
* Sets a layout that will be used later in render() method.
*
* @param string $template The template filename, without extension
* @return Simply_Static_View
*/
public function set_layout( $layout ) {
$this->layout = trailingslashit( $this->path ) . 'layouts/' . $layout . self::EXTENSION;
return $this;
}
/**
* Sets a template that will be used later in render() method.
*
* @param string $template The template filename, without extension
* @return Simply_Static_View
*/
public function set_template( $template ) {
$this->template = trailingslashit( $this->path ) . $template . self::EXTENSION;
return $this;
}
/**
* Returns a value of the option identified by $name
*
* @param string $name The option name
* @return mixed|null
*/
public function __get( $name ) {
$value = array_key_exists( $name, $this->variables ) ? $this->variables[$name] : null;
return $value;
}
/**
* Updates the view variable identified by $name with the value provided in $value
*
* @param string $name The variable name
* @param mixed $value The variable value
* @return Simply_Static_View
*/
public function __set( $name, $value ) {
$this->variables[$name] = $value;
return $this;
}
/**
* Updates the view variable identified by $name with the value provided in $value
*
* @param string $name The variable name
* @param mixed $value The variable value
* @return Simply_Static_View
*/
public function assign( $name, $value ) {
return $this->__set( $name, $value );
}
/**
* Add a flash message to be displayed at the top of the page
*
* @param string $type The type of message to be displayed
* @param string $message The message to be displayed
* @return void
*/
public function add_flash( $type, $message ) {
array_push( $this->flashes, array( 'type' => $type, 'message' => $message ) );
}
/**
* Returns the layout (if available) or template. Checks to make sure that
* the file exists and is readable.
*
* @return string|WP_Error
*/
private function get_renderable_file() {
// must include a template
if ( ! is_readable( $this->template ) ) {
return new WP_Error( 'invalid_template', sprintf( __( "Can't find view template: %s", $this->slug ), $this->template ) );
}
// layouts are optional. if no layout provided, use the template by itself.
if ( $this->layout ) {
if ( ! is_readable( $this->layout ) ) {
return new WP_Error( 'invalid_layout', sprintf( __( "Can't find view layout: %s", $this->slug ), $this->layout ) );
} else {
// the layout should include the template
return $this->layout;
}
} else {
return $this->template;
}
}
/**
* Check Simply_Static's system requirements and add errors as necessary.
*
* @return void
*/
private function check_for_errors() {
$errors = Simply_Static::instance()->check_system_requirements();
foreach ( $errors as $field ) {
foreach ( $field as $error ) {
$this->add_flash( 'error', $error );
}
}
}
/**
* Renders the view script.
*
* @return Simply_Static_View|WP_Error
*/
public function render() {
$this->check_for_errors();
$file = $this->get_renderable_file();
if ( is_wp_error( $file ) ) {
return $file;
} else {
include $file;
return $this;
}
}
/**
* Returns the view as a string.
*
* @return string|WP_Error
*/
public function render_to_string() {
$file = $this->get_renderable_file();
if ( is_wp_error( $file ) ) {
return $file;
} else {
ob_start();
include $file;
return ob_get_clean();
}
}
}
+420
View File
@@ -0,0 +1,420 @@
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
/**
* The core plugin class
*
* @package Simply_Static
*/
class Simply_Static {
/**
* Plugin version
*
* @var string
*/
const VERSION = '2.0.0';
/**
* The slug of the plugin; used in actions, filters, i18n, etc.
*
* @var string
*/
const SLUG = 'simply-static-s3';
/**
* Singleton instance
*
* @var Simply_Static
*/
protected static $instance = null;
/**
* An instance of the options structure containing all options for this plugin
*
* @var Simply_Static_Options
*/
protected $options = null;
/**
* View object
*
* @var Simply_Static_View
*/
protected $view = null;
/**
* Disable usage of "new"
*
* @return void
*/
protected function __construct() {}
/**
* Disable cloning of the class
*
* @return void
*/
protected function __clone() {}
/**
* Disable unserializing of the class
*
* @return void
*/
public function __wakeup() {}
/**
* Return an instance of the Simply Static plugin
*
* @return Simply_Static
*/
public static function instance()
{
if ( null === self::$instance )
{
self::$instance = new self();
self::$instance->includes();
self::$instance->options = new Simply_Static_Options( self::SLUG );
self::$instance->view = new Simply_Static_View();
// Load the text domain for i18n
add_action( 'plugins_loaded', array( self::$instance, 'load_textdomain' ) );
// Enqueue admin styles
add_action( 'admin_enqueue_scripts', array( self::$instance, 'enqueue_admin_styles' ) );
// Enqueue admin scripts
add_action( 'admin_enqueue_scripts', array( self::$instance, 'enqueue_admin_scripts' ) );
// Add the options page and menu item.
add_action( 'admin_menu', array( self::$instance, 'add_plugin_admin_menu' ), 2 );
}
return self::$instance;
}
/**
* Initialize singleton instance
*
* @param string $bootstrap_file
* @return Simply_Static
*/
public static function init( $bootstrap_file )
{
$instance = self::instance();
// Activation
register_activation_hook( $bootstrap_file, array( $instance, 'activate' ) );
return $instance;
}
/**
* Performs activation
*
* @return void
*/
public function activate()
{
// Not installed?
if ( null === $this->options->get( 'version' ) ) {
$this->options
->set( 'version', self::VERSION )
->set( 'destination_scheme', sist_get_origin_scheme() )
->set( 'destination_host', sist_get_origin_host() )
->set( 'temp_files_dir', trailingslashit( plugin_dir_path( dirname( __FILE__ ) ) . 'static-files' ) )
->set( 'additional_urls', '' )
->set( 'delivery_method', 's3' )
->set( 'local_dir', '' )
->set( 'delete_temp_files', '1' )
->set( 'aws_s3_bucket', '' )
->set( 'aws_access_key_id', '' )
->set( 'aws_secret_access_key', '' )
->set( 'aws_s3_region', 'us-east-1' )
->set( 'aws_s3_endpoint', 's3.amazonaws.com' )
->save();
}
}
/**
* Include required files
*
* @return void
*/
private function includes() {
require plugin_dir_path( dirname( __FILE__ ) ) . 'includes/libraries/phpuri.php';
require plugin_dir_path( dirname( __FILE__ ) ) . 'includes/libraries/S3.php';
require plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-simply-static-options.php';
require plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-simply-static-view.php';
require plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-simply-static-url-extractor.php';
require plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-simply-static-url-fetcher.php';
require plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-simply-static-url-response.php';
require plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-simply-static-archive-creator.php';
require plugin_dir_path( dirname( __FILE__ ) ) . 'includes/misc-functions.php';
}
/**
* Enqueue admin-specific style sheets for this plugin's admin pages only
*
* @return null Return early if no settings page is registered.
*/
public function enqueue_admin_styles() {
// Plugin admin CSS. Tack on plugin version.
wp_enqueue_style( self::SLUG . '-admin-styles', plugin_dir_url( dirname( __FILE__ ) ) . 'css/admin.css', array(), self::VERSION );
}
/**
* Enqueue admin-specific javascript files for this plugin's admin pages only
*
* @return null Return early if no settings page is registered.
*/
public function enqueue_admin_scripts() {
// Plugin admin CSS. Tack on plugin version.
wp_enqueue_script( self::SLUG . '-admin-styles', plugin_dir_url( dirname( __FILE__ ) ) . 'js/admin.js', array(), self::VERSION );
}
/**
* Register the administration menu for this plugin into the WordPress Dashboard menu.
*
* @return void
*/
public function add_plugin_admin_menu() {
// Base 64 encoded SVG image.
$icon_svg = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiICAgaWQ9InN2ZzM0MzQiICAgdmVyc2lvbj0iMS4xIiAgIGlua3NjYXBlOnZlcnNpb249IjAuOTEgcjEzNzI1IiAgIHdpZHRoPSIxODAiICAgaGVpZ2h0PSIzMDAiICAgdmlld0JveD0iMCAwIDE4MCAzMDAiICAgc29kaXBvZGk6ZG9jbmFtZT0iYm9sdC12ZWN0b3ItZ3JheS5zdmciPiAgPG1ldGFkYXRhICAgICBpZD0ibWV0YWRhdGEzNDQwIj4gICAgPHJkZjpSREY+ICAgICAgPGNjOldvcmsgICAgICAgICByZGY6YWJvdXQ9IiI+ICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4gICAgICAgIDxkYzp0eXBlICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPiAgICAgICAgPGRjOnRpdGxlIC8+ICAgICAgPC9jYzpXb3JrPiAgICA8L3JkZjpSREY+ICA8L21ldGFkYXRhPiAgPGRlZnMgICAgIGlkPSJkZWZzMzQzOCIgLz4gIDxzb2RpcG9kaTpuYW1lZHZpZXcgICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIgICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IiAgICAgYm9yZGVyb3BhY2l0eT0iMSIgICAgIG9iamVjdHRvbGVyYW5jZT0iMTAiICAgICBncmlkdG9sZXJhbmNlPSIxMCIgICAgIGd1aWRldG9sZXJhbmNlPSIxMCIgICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwIiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIgICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTUzNiIgICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjgwMSIgICAgIGlkPSJuYW1lZHZpZXczNDM2IiAgICAgc2hvd2dyaWQ9ImZhbHNlIiAgICAgZml0LW1hcmdpbi10b3A9IjAiICAgICBmaXQtbWFyZ2luLWxlZnQ9IjAiICAgICBmaXQtbWFyZ2luLXJpZ2h0PSIwIiAgICAgZml0LW1hcmdpbi1ib3R0b209IjAiICAgICBpbmtzY2FwZTp6b29tPSIyLjE0MjM3MjkiICAgICBpbmtzY2FwZTpjeD0iOC44Njg2Njc5IiAgICAgaW5rc2NhcGU6Y3k9IjE0Ny41MDAwMSIgICAgIGlua3NjYXBlOndpbmRvdy14PSItOCIgICAgIGlua3NjYXBlOndpbmRvdy15PSItOCIgICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJzdmczNDM0IiAvPiAgPHBhdGggICAgIHN0eWxlPSJmaWxsOiM5Y2ExYTY7ZmlsbC1vcGFjaXR5OjEiICAgICBkPSJNIDM5LjksMjMzLjUgODQuNDMzMzMzLDE2MS4xMzMzMyAzOS45LDE2MS4xMzMzMyAxNDAuMSw2Ni41IGwgLTQ0LjUzMzMzMyw3Mi4zNjY2NyA0NC41MzMzMzMsMCB6IiAgICAgaWQ9InBhdGgzNDQ2IiAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjYyIgLz48L3N2Zz4=';
// Add main menu item
add_menu_page(
__( 'Simply Static S3 Settings', self::SLUG ),
__( 'Simply Static S3', self::SLUG ),
'manage_options',
self::SLUG,
array( self::$instance, 'display_generate_page' ),
$icon_svg
);
add_submenu_page(
self::SLUG,
__( 'Generate Static Site', self::SLUG ),
__( 'Generate', self::SLUG ),
'manage_options',
self::SLUG,
array( self::$instance, 'display_generate_page' )
);
add_submenu_page(
self::SLUG,
__( 'Simply Static S3 Settings', self::SLUG ),
__( 'Settings', self::SLUG ),
'manage_options',
self::SLUG . '-options',
array( self::$instance, 'display_options_page' )
);
}
/**
* Render the page for generating a static site.
*
* @return void
*/
public function display_generate_page() {
if ( $this->check_system_requirements() ) {
$this->view->assign( 'system_requirements_check_failed', true );
}
if ( isset( $_POST['generate'] ) ) {
$archive_creator = new Simply_Static_Archive_Creator(
self::SLUG,
$this->options->get( 'destination_scheme' ),
$this->options->get( 'destination_host' ),
$this->options->get( 'temp_files_dir' ),
$this->options->get( 'additional_urls' )
);
$archive_creator->create_archive();
// TODO: archive_url could be a WP_Error
if ( $this->options->get( 'delivery_method' ) == 'zip' ) {
$archive_url = $archive_creator->create_zip();
if ( is_wp_error( $archive_url ) ) {
$error = $archive_url->get_error_message();
$this->view->add_flash( 'error', $error );
} else {
$message = __( 'ZIP archive created: ', self::SLUG );
$message .= ' <a href="' . $archive_url . '">' . __( 'Click here to download', self::SLUG ) . '</a>';
$this->view->add_flash( 'updated', $message );
}
} elseif ( $this->options->get( 'delivery_method' ) == 'local' ) {
$local_dir = $this->options->get( 'local_dir' );
$result = $archive_creator->copy_static_files( $local_dir );
if ( is_wp_error( $result ) ) {
$error = $result->get_error_message();
$this->view->add_flash( 'error', $error );
} else {
$message = __( 'Static files copied to: ' . $local_dir, self::SLUG );
$this->view->add_flash( 'updated', $message );
}
} elseif ( $this->options->get( 'delivery_method' ) == 's3' ) {
$bucket = $this->options->get( 'aws_s3_bucket' );
$result = $archive_creator->publish_to_s3(
$bucket,
$this->options->get( 'aws_access_key_id' ),
$this->options->get( 'aws_secret_access_key' ),
$this->options->get( 'aws_s3_region' ),
$this->options->get( 'aws_s3_endpoint' )
);
if ( is_wp_error( $result ) ) {
$error = $result->get_error_message();
$this->view->add_flash( 'error', $error );
} else {
$message = __( 'Site published to S3 bucket: ' . $bucket, self::SLUG );
$this->view->add_flash( 'updated', $message );
}
}
if ( $this->options->get( 'delete_temp_files' ) == '1' ) {
$deleted_successfully = $archive_creator->delete_static_files();
}
$export_log = $archive_creator->get_export_log();
$this->view->assign( 'export_log', $export_log );
}
$this->view
->set_layout( 'admin' )
->set_template( 'generate' )
->render();
}
/**
* Render the options page.
*
* @return void
*/
public function display_options_page() {
if ( isset( $_POST['save'] ) ) {
$this->save_options();
$message = __( 'Settings saved.', self::SLUG );
$this->view->add_flash( 'updated', $message );
}
$this->view
->set_layout( 'admin' )
->set_template( 'options' )
->assign( 'slug', self::SLUG )
->assign( 'origin_scheme', sist_get_origin_scheme() )
->assign( 'origin_host', sist_get_origin_host() )
->assign( 'destination_scheme', $this->options->get( 'destination_scheme' ) )
->assign( 'destination_host', $this->options->get( 'destination_host' ) )
->assign( 'temp_files_dir', $this->options->get( 'temp_files_dir' ) )
->assign( 'additional_urls', $this->options->get( 'additional_urls' ) )
->assign( 'delivery_method', $this->options->get( 'delivery_method' ) )
->assign( 'local_dir', $this->options->get( 'local_dir' ) )
->assign( 'delete_temp_files', $this->options->get( 'delete_temp_files' ) )
->assign( 'aws_s3_bucket', $this->options->get( 'aws_s3_bucket' ) )
->assign( 'aws_access_key_id', $this->options->get( 'aws_access_key_id' ) )
->assign( 'aws_secret_access_key', $this->options->get( 'aws_secret_access_key' ) )
->assign( 'aws_s3_region', $this->options->get( 'aws_s3_region' ) )
->assign( 'aws_s3_endpoint', $this->options->get( 'aws_s3_endpoint' ) )
->render();
}
/**
* Save the options from the options page.
*
* @return void
*/
public function save_options() {
$this->options
->set( 'destination_scheme', filter_input( INPUT_POST, 'destination_scheme' ) )
->set( 'destination_host', untrailingslashit( filter_input( INPUT_POST, 'destination_host', FILTER_SANITIZE_URL ) ) )
->set( 'temp_files_dir', sist_trailingslashit_unless_blank( filter_input( INPUT_POST, 'temp_files_dir' ) ) )
->set( 'additional_urls', filter_input( INPUT_POST, 'additional_urls' ) )
->set( 'delivery_method', filter_input( INPUT_POST, 'delivery_method' ) )
->set( 'local_dir', sist_trailingslashit_unless_blank( filter_input( INPUT_POST, 'local_dir' ) ) )
->set( 'delete_temp_files', filter_input( INPUT_POST, 'delete_temp_files' ) )
->set( 'aws_s3_bucket', filter_input( INPUT_POST, 'aws_s3_bucket' ) )
->set( 'aws_access_key_id', filter_input( INPUT_POST, 'aws_access_key_id' ) )
->set( 'aws_secret_access_key', filter_input( INPUT_POST, 'aws_secret_access_key' ) )
->set( 'aws_s3_region', filter_input( INPUT_POST, 'aws_s3_region' ) )
->set( 'aws_s3_endpoint', filter_input( INPUT_POST, 'aws_s3_endpoint' ) )
->save();
}
/**
* Loads the plugin language files
*
* @return void
*/
public function load_textdomain() {
load_plugin_textdomain(
self::SLUG,
false,
dirname( dirname( plugin_basename( __FILE__ ) ) ) . '/languages/'
);
}
/**
* Loads the plugin language files
*
* @return array $errors associative array containing errored field names and an array of error messages
*/
public function check_system_requirements() {
$errors = array();
$destination_host = $this->options->get( 'destination_host' );
if ( strlen( $destination_host ) === 0 ) {
$errors['destination_host'][] = __( 'Destination URL cannot be blank', self::SLUG );
}
$temp_files_dir = $this->options->get( 'temp_files_dir' );
if ( strlen( $temp_files_dir ) === 0 ) {
$errors['temp_files_dir'][] = __( 'Temporary Files Directory cannot be blank', self::SLUG );
} else {
if ( file_exists( $temp_files_dir ) ) {
if ( ! is_writeable( $temp_files_dir ) ) {
$errors['delivery_method'][] = sprintf( __( 'Temporary Files Directory is not writeable: %s', self::SLUG ), $temp_files_dir );
}
} else {
$errors['delivery_method'][] = sprintf( __( 'Temporary Files Directory does not exist: %s', self::SLUG ), $temp_files_dir );
}
}
if ( strlen( get_option( 'permalink_structure' ) ) === 0 ) {
$errors['permalink_structure'][] = sprintf( __( "Your site does not have a permalink structure set. You can select one on <a href='%s'>the Permalink Settings page</a>.", self::SLUG ), admin_url( '/options-permalink.php' ) );
}
if ( $this->options->get( 'delivery_method' ) == 's3' ) {
if ( strlen( $this->options->get( 'aws_s3_bucket' ) ) == 0 ) {
$errors['delivery_method'][] = __( "You haven't configured an Amazon AWS bucket for S3 publishing.", self::SLUG );
}
if ( strlen( $this->options->get( 'aws_access_key_id' ) ) == 0 || strlen( $this->options->get( 'aws_secret_access_key' ) ) == 0 ) {
$errors['delivery_method'][] = __( "You haven't configured Amazon AWS credentials for S3 publishing.", self::SLUG );
}
}
if ( $this->options->get( 'delivery_method' ) == 'zip' ) {
if ( ! extension_loaded('zip') ) {
$errors['delivery_method'][] = __( "Your server does not have the PHP zip extension enabled. Please visit <a href='http://www.php.net/manual/en/book.zip.php'>the PHP zip extension page</a> for more information on how to enable it.", self::SLUG );
}
}
if ( $this->options->get( 'delivery_method' ) == 'local' ) {
$local_dir = $this->options->get( 'local_dir' );
if ( strlen( $local_dir ) === 0 ) {
$errors['delivery_method'][] = __( 'Local Directory cannot be blank', self::SLUG );
} else {
if ( file_exists( $local_dir ) ) {
if ( ! is_writeable( $local_dir ) ) {
$errors['delivery_method'][] = sprintf( __( 'Local Directory is not writeable: %s', self::SLUG ), $local_dir );
}
} else {
$errors['delivery_method'][] = sprintf( __( 'Local Directory does not exist: %s', self::SLUG ), $local_dir );
}
}
}
return $errors;
}
}
+1
View File
@@ -0,0 +1 @@
<?php // Silence is golden
File diff suppressed because it is too large. Load diff
+209
View File
@@ -0,0 +1,209 @@
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
/**
* A php library for converting relative urls to absolute.
* Website: https://github.com/monkeysuffrage/phpuri
*
* <pre>
* echo phpUri::parse('https://www.google.com/')->join('foo');
* //==> https://www.google.com/foo
* </pre>
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @author P Guardiario <pguardiario@gmail.com>
* @version 1.0
*/
/**
* phpUri
*/
class phpUri
{
/**
* http(s)://
* @var string
*/
public $scheme;
/**
* www.example.com
* @var string
*/
public $authority;
/**
* /search
* @var string
*/
public $path;
/**
* ?q=foo
* @var string
*/
public $query;
/**
* #bar
* @var string
*/
public $fragment;
private function __construct( $string )
{
preg_match_all( '/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/', $string, $m );
$this->scheme = $m[ 2 ][ 0 ];
$this->authority = $m[ 4 ][ 0 ];
/**
* CHANGE:
* @author Dominik Habichtsberg <Dominik.Habichtsberg@Hbg-IT.de>
* @since 24 Mai 2015 10:02 Uhr
*
* Former code: $this->path = ( empty( $m[ 5 ][ 0 ] ) ) ? '/' : $m[ 5 ][ 0 ];
* No tests failed, when the path is empty.
* With the former code, the relative urls //g and #s failed
*/
$this->path = $m[ 5 ][ 0 ];
$this->query = $m[ 7 ][ 0 ];
$this->fragment = $m[ 9 ][ 0 ];
}
private function to_str()
{
$ret = '';
if ( !empty( $this->scheme ) )
{
$ret .= "{$this->scheme}:";
}
if ( !empty( $this->authority ) )
{
$ret .= "//{$this->authority}";
}
$ret .= $this->normalize_path( $this->path );
if ( !empty( $this->query ) )
{
$ret .= "?{$this->query}";
}
if ( !empty( $this->fragment ) )
{
$ret .= "#{$this->fragment}";
}
return $ret;
}
private function normalize_path( $path )
{
if ( empty( $path ) )
{
return '';
}
$normalized_path = $path;
$normalized_path = preg_replace( '`//+`', '/', $normalized_path, -1, $c0 );
$normalized_path = preg_replace( '`^/\\.\\.?/`', '/', $normalized_path, -1, $c1 );
$normalized_path = preg_replace( '`/\\.(/|$)`', '/', $normalized_path, -1, $c2 );
/**
* CHANGE:
* @author Dominik Habichtsberg <Dominik.Habichtsberg@Hbg-IT.de>
* @since 24 Mai 2015 10:05 Uhr
* changed limit form -1 to 1, because climbing up the directory-tree failed
*/
$normalized_path = preg_replace( '`/[^/]*?/\\.\\.(/|$)`', '/', $normalized_path, 1, $c3 );
$num_matches = $c0 + $c1 + $c2 + $c3;
return ( $num_matches > 0 ) ? $this->normalize_path( $normalized_path ) : $normalized_path;
}
/**
* Parse an url string
*
* @param string $url the url to parse
*
* @return phpUri
*/
public static function parse( $url )
{
$uri = new phpUri( $url );
/**
* CHANGE:
* @author Dominik Habichtsberg <Dominik.Habichtsberg@Hbg-IT.de>
* @since 24 Mai 2015 10:25 Uhr
* The base-url should always have a path
*/
if ( empty( $uri->path ) )
{
$uri->path = '/';
}
return $uri;
}
/**
* Join with a relative url
*
* @param string $relative the relative url to join
*
* @return string
*/
public function join( $relative )
{
$uri = new phpUri( $relative );
switch ( TRUE )
{
case !empty( $uri->scheme ):
break;
case !empty( $uri->authority ):
break;
case empty( $uri->path ):
$uri->path = $this->path;
if ( empty( $uri->query ) )
{
$uri->query = $this->query;
}
break;
case strpos( $uri->path, '/' ) === 0:
break;
default:
$base_path = $this->path;
if ( strpos( $base_path, '/' ) === FALSE )
{
$base_path = '';
}
else
{
$base_path = preg_replace( '/\/[^\/]+$/', '/', $base_path );
}
if ( empty( $base_path ) && empty( $this->authority ) )
{
$base_path = '/';
}
$uri->path = $base_path . $uri->path;
}
if ( empty( $uri->scheme ) )
{
$uri->scheme = $this->scheme;
if ( empty( $uri->authority ) )
{
$uri->authority = $this->authority;
}
}
return $uri->to_str();
}
}
+156
View File
@@ -0,0 +1,156 @@
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
/**
* Miscellaneous functions for use across the plugin
*
* @package Simply_Static
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Get the protocol used for the origin URL
*
* @return string http or https
*/
function sist_get_origin_scheme() {
return is_ssl() ? 'https' : 'http';
}
/**
* Get the host for the origin URL
*
* @return string host (URL minus the protocol)
*/
function sist_get_origin_host() {
return untrailingslashit( preg_replace( "(^https?://)", "", home_url() ) );
}
/**
* Echo the selected value for an option tag if the statement is true.
*
* @return null
*/
function sist_selected_if( $statement ) {
echo ( $statement == true ? 'selected="selected"' : '' );
}
/**
* Truncate if a string exceeds a certain length (30 chars by default)
*
* @return string
*/
function sist_truncate( $string, $length = 30, $omission = '...' ) {
return ( strlen( $string ) > $length + 3 ) ? ( substr( $string, 0, $length ) . $omission ) : $string;
}
/**
* Use trailingslashit unless the string is empty
*
* @return string
*/
function sist_trailingslashit_unless_blank( $string ) {
return $string === '' ? $string : trailingslashit( $string );
}
/**
* Dump an object to error_log
*
* @return string
*/
function sist_error_log( $object=null ){
ob_start();
var_dump( $object );
$contents = ob_get_contents();
ob_end_clean();
error_log( $contents );
}
/**
* Given a URL extracted from a page, return an absolute URL
*
* Takes a URL (e.g. /test) extracted from a page (e.g. http://foo.com/bar/) and
* returns an absolute URL (e.g. http://foo.com/bar/test). Absolute URLs are
* returned as-is.
*
* A null value is returned in the event that the extracted_url is blank or it's
* unable to be parsed.
*
* @param string $extracted_url Relative or absolute URL extracted from page
* @param string $page_url URL of page
* @return string|null Absolute URL, or null
*/
function sist_relative_to_absolute_url( $extracted_url, $page_url ) {
$extracted_url = trim( $extracted_url );
if ( $extracted_url === '' ) {
return null;
}
$parsed_extracted_url = parse_url( $extracted_url );
// parse_url can sometimes return false; bail if it does
if ( $parsed_extracted_url === false ) {
return null;
}
if ( isset( $parsed_extracted_url['host'] ) ) {
return $extracted_url;
} elseif ( isset( $parsed_extracted_url['scheme'] ) ) {
// examples of schemes without hosts: java:, data:
return $extracted_url;
} else { // no host on extracted page (might be relative url)
$path = isset( $parsed_extracted_url['path'] ) ? $parsed_extracted_url['path'] : '';
// Check for a bug in PHP <= 5.4.7 where URLs starting
// with '//' are identified as a path
// http://php.net/manual/en/function.parse-url.php#example-4617
if ( substr( $path, 0, 2 ) === '//' ) {
return $extracted_url;
} else {
$query = isset( $parsed_extracted_url['query'] ) ? '?' . $parsed_extracted_url['query'] : '';
$fragment = isset( $parsed_extracted_url['fragment'] ) ? '#' . $parsed_extracted_url['fragment'] : '';
// turn our relative url into an absolute url
$extracted_url = phpUri::parse( $page_url )->join( $path . $query . $fragment );
return $extracted_url;
}
}
}
/**
* Check if URL starts with same URL as WordPress installation
*
* @param string $url URL to check
* @return boolean true if URL is local, false otherwise
*/
function sist_is_local_url( $url ) {
return ( stripos( $url, home_url() ) === 0 );
}
/**
* Returns a URL w/o the query string or fragment (i.e. nothing after the path)
*
* @param string $url URL to remove query string/fragment from
* @return string URL without query string/fragment
*/
function sist_remove_params_and_fragment( $url ) {
return preg_replace('/(\?|#).*/', '', $url);
}