2016-12-27 14:30:54 +13:00
< ? php
/**
* @ package WP Static HTML Output
*
* Copyright ( c ) 2011 Leon Stafford
*/
2017-03-11 14:50:49 +13:00
class StaticHtmlOutput {
2017-03-22 22:02:29 +13:00
const VERSION = '1.8' ;
2016-12-27 14:30:54 +13:00
const OPTIONS_KEY = 'wp-static-html-output-options' ;
const HOOK = 'wp-static-html-output' ;
protected static $_instance = null ;
protected $_options = null ;
protected $_view = null ;
protected $_exportLog = array ();
2017-03-11 14:50:49 +13:00
protected function __construct () {}
2016-12-27 14:30:54 +13:00
2017-03-11 14:50:49 +13:00
protected function __clone () {}
2016-12-27 14:30:54 +13:00
2017-03-11 14:50:49 +13:00
public static function getInstance () {
if ( null === self :: $_instance ) {
2016-12-27 14:30:54 +13:00
self :: $_instance = new self ();
self :: $_instance -> _options = new StaticHtmlOutput_Options ( self :: OPTIONS_KEY );
self :: $_instance -> _view = new StaticHtmlOutput_View ();
}
return self :: $_instance ;
}
2017-03-11 14:50:49 +13:00
public static function init ( $bootstrapFile ) {
2016-12-27 14:30:54 +13:00
$instance = self :: getInstance ();
register_activation_hook ( $bootstrapFile , array ( $instance , 'activate' ));
2017-03-11 14:50:49 +13:00
if ( is_admin ()) {
2016-12-27 14:30:54 +13:00
add_action ( 'admin_menu' , array ( $instance , 'registerOptionsPage' ));
add_action ( self :: HOOK . '-saveOptions' , array ( $instance , 'saveOptions' ));
}
return $instance ;
}
2017-03-11 14:50:49 +13:00
public function activate () {
if ( null === $this -> _options -> getOption ( 'version' )) {
2016-12-27 14:30:54 +13:00
$this -> _options
-> setOption ( 'version' , self :: VERSION )
2017-03-07 12:13:28 +13:00
-> setOption ( 'static_export_settings' , self :: VERSION )
2016-12-27 14:30:54 +13:00
-> save ();
}
}
2017-03-11 14:50:49 +13:00
public function registerOptionsPage () {
2016-12-27 14:30:54 +13:00
$page = add_submenu_page ( 'tools.php' , __ ( 'WP Static HTML Output' , 'static-html-output-plugin' ), __ ( 'WP Static HTML Output' , 'static-html-output-plugin' ), 'manage_options' , self :: HOOK . '-options' , array ( $this , 'renderOptionsPage' ));
add_action ( 'admin_print_styles-' . $page , array ( $this , 'enqueueAdminStyles' ));
}
2017-03-11 14:50:49 +13:00
public function enqueueAdminStyles () {
2016-12-27 14:30:54 +13:00
$pluginDirUrl = plugin_dir_url ( dirname ( __FILE__ ));
wp_enqueue_style ( self :: HOOK . '-admin' , $pluginDirUrl . '/css/wp-static-html-output.css' );
}
2017-03-11 14:50:49 +13:00
public function renderOptionsPage () {
2016-12-27 14:30:54 +13:00
// Check system requirements
$uploadDir = wp_upload_dir ();
$uploadsFolderWritable = $uploadDir && is_writable ( $uploadDir [ 'path' ]);
$supportsZipArchives = extension_loaded ( 'zip' );
$permalinksStructureDefined = strlen ( get_option ( 'permalink_structure' ));
2017-03-22 20:36:37 +13:00
2017-03-11 14:50:49 +13:00
if ( ! $uploadsFolderWritable || ! $supportsZipArchives || ! $permalinksStructureDefined ) {
2016-12-27 14:30:54 +13:00
$this -> _view
-> setTemplate ( 'system-requirements' )
-> assign ( 'uploadsFolderWritable' , $uploadsFolderWritable )
-> assign ( 'supportsZipArchives' , $supportsZipArchives )
-> assign ( 'permalinksStructureDefined' , $permalinksStructureDefined )
2016-12-27 20:19:22 +13:00
-> assign ( 'uploadsFolder' , $uploadDir )
2016-12-27 14:30:54 +13:00
-> render ();
2017-03-11 14:50:49 +13:00
} else {
2017-03-07 13:26:34 +13:00
2016-12-27 14:30:54 +13:00
do_action ( self :: HOOK . '-saveOptions' );
2016-12-29 16:20:49 +13:00
$this -> _view
-> setTemplate ( 'options-page' )
-> assign ( 'exportLog' , $this -> _exportLog )
2017-03-07 13:26:34 +13:00
-> assign ( 'staticExportSettings' , $this -> _options -> getOption ( 'static-export-settings' ))
2016-12-29 16:20:49 +13:00
-> assign ( 'onceAction' , self :: HOOK . '-options' )
-> render ();
2016-12-27 14:30:54 +13:00
}
}
2017-03-11 14:50:49 +13:00
public function saveOptions () {
if ( ! isset ( $_POST [ 'action' ]) || 'generate' != $_POST [ 'action' ]) {
2016-12-27 14:30:54 +13:00
return ;
}
2017-03-11 14:50:49 +13:00
if ( ! check_admin_referer ( self :: HOOK . '-options' ) || ! current_user_can ( 'manage_options' )) {
2017-03-07 13:26:34 +13:00
error_log ( 'user didnt have permissions to change options' );
2016-12-27 14:30:54 +13:00
exit ( 'You cannot change WP Static HTML Output Plugin options.' );
}
2017-03-07 13:26:34 +13:00
2016-12-27 14:30:54 +13:00
$this -> _options
2017-03-07 12:13:28 +13:00
-> setOption ( 'static-export-settings' , filter_input ( INPUT_POST , 'staticExportSettings' , FILTER_SANITIZE_URL ))
2016-12-27 14:30:54 +13:00
-> save ();
2016-12-29 14:45:16 +13:00
$message = 'Options have been updated successfully.' ;
$this -> _view -> setTemplate ( 'message' )
-> assign ( 'message' , $message )
-> render ();
2016-12-29 16:20:49 +13:00
}
2016-12-29 14:45:16 +13:00
2017-03-11 14:50:49 +13:00
public function genArch () {
2016-12-27 14:30:54 +13:00
$archiveUrl = $this -> _generateArchive ();
2017-03-11 14:50:49 +13:00
if ( is_wp_error ( $archiveUrl )) {
2016-12-27 14:30:54 +13:00
$message = 'Error: ' . $archiveUrl -> get_error_code ;
2017-03-11 14:50:49 +13:00
} else {
2016-12-27 14:30:54 +13:00
$message = sprintf ( 'Archive created successfully: <a href="%s">Download archive</a>' , $archiveUrl );
if ( $this -> _options -> getOption ( 'retainStaticFiles' ) == 1 )
{
$message .= sprintf ( '<br />Static files retained at: %s/' , str_replace ( home_url (), '' , substr ( $archiveUrl , 0 , - 4 )));
}
}
$this -> _view -> setTemplate ( 'message' )
-> assign ( 'message' , $message )
2016-12-29 17:22:51 +13:00
-> assign ( 'exportLog' , $this -> _exportLog )
2016-12-27 14:30:54 +13:00
-> render ();
2016-12-29 16:20:49 +13:00
}
2016-12-27 14:30:54 +13:00
protected function _generateArchive ()
{
global $blog_id ;
set_time_limit ( 0 );
2017-02-20 23:54:03 +13:00
2016-12-27 14:30:54 +13:00
$uploadDir = wp_upload_dir ();
$exporter = wp_get_current_user ();
$archiveName = $uploadDir [ 'path' ] . '/' . self :: HOOK . '-' . $blog_id . '-' . time () . '-' . $exporter -> user_login ;
$archiveDir = $archiveName . '/' ;
if ( ! file_exists ( $archiveDir ))
{
wp_mkdir_p ( $archiveDir );
}
2017-02-20 23:54:03 +13:00
2016-12-27 14:30:54 +13:00
$baseUrl = untrailingslashit ( home_url ());
2017-02-20 23:54:03 +13:00
$newBaseUrl = untrailingslashit ( filter_input ( INPUT_POST , 'baseUrl' , FILTER_SANITIZE_URL ));
2016-12-27 14:30:54 +13:00
$urlsQueue = array_unique ( array_merge (
array ( trailingslashit ( $baseUrl )),
$this -> _getListOfLocalFilesByUrl ( array ( get_template_directory_uri ())),
2017-03-16 21:41:25 +13:00
explode ( " \n " , filter_input ( INPUT_POST , 'additionalUrls' ))
2016-12-27 14:30:54 +13:00
));
2017-03-16 21:41:25 +13:00
2016-12-27 14:30:54 +13:00
$this -> _exportLog = array ();
2017-03-16 21:41:25 +13:00
2016-12-27 14:30:54 +13:00
while ( count ( $urlsQueue ))
{
$currentUrl = array_shift ( $urlsQueue );
2017-03-16 21:41:25 +13:00
2017-02-20 23:54:03 +13:00
$urlResponse = new StaticHtmlOutput_UrlRequest ( $currentUrl , filter_input ( INPUT_POST , 'cleanMeta' ));
2017-03-16 21:41:25 +13:00
if ( $urlResponse -> checkResponse () == 'FAIL' ) {
2017-03-21 17:33:54 +13:00
error_log ( 'Failed to get this file' );
error_log ( $currentUrl );
2017-03-16 21:41:25 +13:00
} else {
// Add current url to the list of processed urls
$this -> _exportLog [ $currentUrl ] = true ;
}
2017-03-21 17:47:30 +13:00
// TODO: this shouldnt be part of urlrequest, just general settings
// add conditional logic here whether to do cleanup, vs in each request?
$urlResponse -> cleanup ();
2017-03-16 21:41:25 +13:00
2017-03-21 17:33:54 +13:00
// get all other urls from within this one and add to queue if not there
2017-03-11 14:50:49 +13:00
foreach ( $urlResponse -> extractAllUrls ( $baseUrl ) as $newUrl ) {
if ( ! isset ( $this -> _exportLog [ $newUrl ]) && $newUrl != $currentUrl && ! in_array ( $newUrl , $urlsQueue )) {
2016-12-27 14:30:54 +13:00
//echo "Adding ".$newUrl." to the list<br />";
$urlsQueue [] = $newUrl ;
}
}
2017-03-21 17:47:30 +13:00
$urlResponse -> replaceBaseUlr ( $baseUrl , $newBaseUrl );
$this -> _saveUrlData ( $urlResponse , $archiveDir );
2016-12-27 14:30:54 +13:00
}
$tempZip = $archiveName . '.tmp' ;
$zipArchive = new ZipArchive ();
2017-03-11 14:50:49 +13:00
if ( $zipArchive -> open ( $tempZip , ZIPARCHIVE :: CREATE ) !== true ) {
2016-12-27 14:30:54 +13:00
return new WP_Error ( 'Could not create archive' );
}
$iterator = new RecursiveIteratorIterator ( new RecursiveDirectoryIterator ( $archiveDir ));
2017-03-11 14:50:49 +13:00
foreach ( $iterator as $fileName => $fileObject ) {
2016-12-27 14:30:54 +13:00
$baseName = basename ( $fileName );
2017-03-11 14:50:49 +13:00
if ( $baseName != '.' && $baseName != '..' ) {
if ( ! $zipArchive -> addFile ( realpath ( $fileName ), str_replace ( $archiveDir , '' , $fileName ))) {
2016-12-27 14:30:54 +13:00
return new WP_Error ( 'Could not add file: ' . $fileName );
}
}
}
$zipArchive -> close ();
rename ( $tempZip , $archiveName . '.zip' );
2017-03-10 15:25:19 +13:00
2017-03-11 14:50:49 +13:00
if ( filter_input ( INPUT_POST , 'sendViaFTP' ) == 1 ) {
2017-03-10 15:25:19 +13:00
require_once ( __DIR__ . '/FTP/FtpClient.php' );
require_once ( __DIR__ . '/FTP/FtpException.php' );
require_once ( __DIR__ . '/FTP/FtpWrapper.php' );
$ftp = new \FtpClient\FtpClient ();
$ftp -> connect ( filter_input ( INPUT_POST , 'ftpServer' ));
$ftp -> login ( filter_input ( INPUT_POST , 'ftpUsername' ), filter_input ( INPUT_POST , 'ftpPassword' ));
2017-03-10 22:37:53 +13:00
$ftp -> pasv ( true );
2017-03-11 16:20:16 +13:00
if ( ! $ftp -> isdir ( filter_input ( INPUT_POST , 'ftpRemotePath' ))) {
$ftp -> mkdir ( filter_input ( INPUT_POST , 'ftpRemotePath' ), true );
}
2017-03-10 15:25:19 +13:00
$ftp -> putAll ( $archiveName . '/' , filter_input ( INPUT_POST , 'ftpRemotePath' ));
2017-03-11 14:50:49 +13:00
// TODO: error handling when not connected/unable to put, etc
2016-12-27 14:30:54 +13:00
unset ( $ftp );
}
2016-12-29 13:25:40 +13:00
2017-03-11 21:25:38 +13:00
if ( filter_input ( INPUT_POST , 'sendViaS3' ) == 1 ) {
require_once ( __DIR__ . '/aws/aws-autoloader.php' );
2017-03-29 07:57:49 +13:00
$s3Client = Aws\S3\S3Client :: factory ( array (
'version' => 'latest' ,
'region' => filter_input ( INPUT_POST , 's3Region' ),
'credentials' => array (
'key' => filter_input ( INPUT_POST , 's3Key' ),
'secret' => filter_input ( INPUT_POST , 's3Secret' ),
)
2017-03-26 18:36:01 +13:00
));
2017-03-11 21:25:38 +13:00
2017-03-29 07:57:49 +13:00
/* TODO : look at this
2017-03-11 21:25:38 +13:00
2017-03-29 07:57:49 +13:00
use Aws\S3\S3Client ;
use Aws\S3\Enum\CannedAcl ;
function UploadObject ( $S3 , $Bucket , $Key , $Data ,
$ACL = CannedAcl :: PRIVATE_ACCESS , $ContentType = " text/plain " )
{
$Try = 1 ;
$Sleep = 1 ;
// Try to do the upload
do
{
try
{
$Model = $S3 -> PutObject ( array ( 'Bucket' => $Bucket ,
'Key' => $Key ,
'Body' => $Data ,
'ACL' => $ACL ,
'ContentType' => $ContentType ));
return true ;
}
catch ( Exception $e ) //FIX should be more fine-grained?
{
print ( " Retry, sleep ${ Sleep } - " . $e -> getMessage () . " \n " );
sleep ( $Sleep );
$Sleep *= 2 ;
}
}
while ( ++ $Try < 6 );
return false ;
}
// Heather added
function UploadDirectory ( $S3 , $Bucket , $Directory )
{
$dir = new DirectoryIterator ( $Directory );
foreach ( $dir as $fileinfo ) {
if ( ! $fileinfo -> isDot ()) {
var_dump ( $fileinfo -> getFilename ());
//$ContentType = mime_content_type($fileinfo->getFilename());
$ContentType = GuessType ( $fileinfo -> getFilename ());
var_dump ( $ContentType );
try {
if ( $ContentType === 'directory' ) {
UploadDirectory ( $S3 , $Bucket , $fileinfo -> getPath () . " \\ " . $fileinfo -> getFilename ());
} else {
$Data = file_get_contents ( $fileinfo -> getFilename ());
if ( $Data === FALSE ) {
print ( " Error uploading file/directory: " . $fileinfo -> getFilename ());
} else {
if ( UploadObject ( $S3 , $Bucket , $fileinfo -> getPath () . " \\ " . $fileinfo -> getFilename (),
$Data , CannedAcl :: PUBLIC_READ , $ContentType )) {
print ( " Uploaded file " . $fileinfo -> getFilename () .
" to Bucket ' { $Bucket } ' \n " );
} else {
exit ( " Could not " .
" upload file " . $fileinfo -> getFilename () .
" to Bucket ' { $Bucket } ' \n " );
}
}
}
}
catch ( UnexpectedValueException $e ) {
print ( " Error: Could not read directory or was not a directory: " . $fileinfo -> getFilename ());
}
}
}
}
/*
* GuessType -
*
* Make a simple guess as to the file ' s content type ,
* and return a MIME type .
*/
function GuessType ( $File )
{
$Info = pathinfo ( $File , PATHINFO_EXTENSION );
switch ( strtolower ( $Info ))
{
case " jpg " :
case " jpeg " :
return " image/jpeg " ;
case " png " :
return " image/png " ;
case " gif " :
return " image/gif " ;
case " htm " :
case " html " :
return " text/html " ;
case " txt " :
return " text/plain " ;
case " php " :
return " text/plain " ;
case " " :
return " directory " ;
default :
return " text/plain " ;
}
}
*/
2017-03-11 21:25:38 +13:00
2017-03-29 07:57:49 +13:00
// adjust for v2 of php sdk
$dir = new DirectoryIterator ( $archiveName . '/' );
foreach ( $dir as $fileinfo ) {
if ( ! $fileinfo -> isDot ()) {
error_log ( $fileinfo -> getFilename ());
$result = $s3Client -> putObject ( array (
'Bucket' => filter_input ( INPUT_POST , 's3Bucket' ),
'Key' => $fileinfo -> getFilename (),
'SourceFile' => $fileinfo -> getFilename (),
//'Metadata' => array(
// 'Foo' => 'abc',
// 'Baz' => '123'
//)
));
}
}
2017-03-11 21:25:38 +13:00
}
2017-03-12 17:54:22 +13:00
if ( filter_input ( INPUT_POST , 'sendViaDropbox' ) == 1 ) {
require_once ( __DIR__ . '/Dropbox/autoload.php' );
// will exclude the siteroot when copying
$siteroot = $archiveName . '/' ;
$dropboxAccessToken = filter_input ( INPUT_POST , 'dropboxAccessToken' );
$dropboxFolder = filter_input ( INPUT_POST , 'dropboxFolder' );
2017-03-12 18:03:16 +13:00
$dbxClient = new Dropbox\Client ( $dropboxAccessToken , " PHP-Example/1.0 " );
2017-03-12 17:54:22 +13:00
function FolderToDropbox ( $dir , $dbxClient , $siteroot , $dropboxFolder ){
$files = scandir ( $dir );
foreach ( $files as $item ){
if ( $item != '.' && $item != '..' ){
if ( is_dir ( $dir . '/' . $item )) {
2017-03-12 18:14:58 +13:00
FolderToDropbox ( $dir . '/' . $item , $dbxClient , $siteroot , $dropboxFolder );
2017-03-12 17:54:22 +13:00
} else if ( is_file ( $dir . '/' . $item )) {
$clean_dir = str_replace ( $siteroot , '' , $dir . '/' . $item );
$targetPath = '/' . $dropboxFolder . '/' . $clean_dir ;
$f = fopen ( $dir . '/' . $item , " rb " );
$result = $dbxClient -> uploadFile ( $targetPath , Dropbox\WriteMode :: add (), $f );
fclose ( $f );
}
}
}
}
FolderToDropbox ( $siteroot , $dbxClient , $siteroot , $dropboxFolder );
}
2017-03-11 21:25:38 +13:00
2016-12-29 13:25:40 +13:00
// TODO: keep copy of last export folder for incremental addition
2016-12-27 14:30:54 +13:00
// Remove temporary files unless user requested to keep or needed for FTP transfer
2017-03-11 14:50:49 +13:00
if ( $this -> _options -> getOption ( 'retainStaticFiles' ) != 1 ) {
2016-12-27 14:30:54 +13:00
$iterator = new RecursiveIteratorIterator ( new RecursiveDirectoryIterator ( $archiveDir ), RecursiveIteratorIterator :: CHILD_FIRST );
2017-03-11 14:50:49 +13:00
foreach ( $iterator as $fileName => $fileObject ) {
2016-12-27 14:30:54 +13:00
// Remove file
2017-03-11 14:50:49 +13:00
if ( $fileObject -> isDir ()) {
2016-12-27 14:30:54 +13:00
// Ignore special dirs
$dirName = basename ( $fileName );
if ( $dirName != '.' && $dirName != '..' ) {
rmdir ( $fileName );
}
2017-03-11 14:50:49 +13:00
} else {
2016-12-27 14:30:54 +13:00
unlink ( $fileName );
}
}
rmdir ( $archiveDir );
}
return str_replace ( ABSPATH , trailingslashit ( home_url ()), $archiveName . '.zip' );
}
protected function _getListOfLocalFilesByUrl ( array $urls )
{
$files = array ();
2017-03-16 21:41:25 +13:00
2017-03-11 14:50:49 +13:00
foreach ( $urls as $url ) {
2016-12-27 14:30:54 +13:00
$directory = str_replace ( home_url ( '/' ), ABSPATH , $url );
2017-03-16 21:41:25 +13:00
// checking if url contains the WP site url at first position and is a directory
2017-03-11 14:50:49 +13:00
if ( stripos ( $url , home_url ( '/' )) === 0 && is_dir ( $directory )) {
2016-12-27 14:30:54 +13:00
$iterator = new RecursiveIteratorIterator ( new RecursiveDirectoryIterator ( $directory ));
2017-03-11 14:50:49 +13:00
foreach ( $iterator as $fileName => $fileObject ) {
2017-03-16 21:41:25 +13:00
2017-03-11 14:50:49 +13:00
if ( is_file ( $fileName )) {
2016-12-27 14:30:54 +13:00
$pathinfo = pathinfo ( $fileName );
2017-03-11 14:50:49 +13:00
if ( isset ( $pathinfo [ 'extension' ]) && ! in_array ( $pathinfo [ 'extension' ], array ( 'php' , 'phtml' , 'tpl' ))) {
2016-12-27 14:30:54 +13:00
array_push ( $files , home_url ( str_replace ( ABSPATH , '' , $fileName )));
}
}
}
2017-03-16 21:41:25 +13:00
} else {
// if is not empty, add full path to $files list
if ( $url != '' ) {
array_push ( $files , $url );
}
}
2016-12-27 14:30:54 +13:00
}
2017-03-16 21:41:25 +13:00
2016-12-27 14:30:54 +13:00
return $files ;
}
2017-03-11 14:50:49 +13:00
protected function _saveUrlData ( StaticHtmlOutput_UrlRequest $url , $archiveDir ) {
2016-12-27 14:30:54 +13:00
$urlInfo = parse_url ( $url -> getUrl ());
$pathInfo = pathinfo ( isset ( $urlInfo [ 'path' ]) && $urlInfo [ 'path' ] != '/' ? $urlInfo [ 'path' ] : 'index.html' );
// Prepare file directory and create it if it doesn't exist
$fileDir = $archiveDir . ( isset ( $pathInfo [ 'dirname' ]) ? $pathInfo [ 'dirname' ] : '' );
2017-03-11 14:50:49 +13:00
if ( empty ( $pathInfo [ 'extension' ]) && $pathInfo [ 'basename' ] == $pathInfo [ 'filename' ]) {
2016-12-27 14:30:54 +13:00
$fileDir .= '/' . $pathInfo [ 'basename' ];
$pathInfo [ 'filename' ] = 'index' ;
}
2017-03-11 14:50:49 +13:00
if ( ! file_exists ( $fileDir )) {
2016-12-27 14:30:54 +13:00
wp_mkdir_p ( $fileDir );
}
$fileExtension = ( $url -> isHtml () || ! isset ( $pathInfo [ 'extension' ]) ? 'html' : $pathInfo [ 'extension' ]);
$fileName = $fileDir . '/' . $pathInfo [ 'filename' ] . '.' . $fileExtension ;
2017-03-21 17:47:30 +13:00
$fileContents = $url -> getResponseBody ();
if ( $fileContents != '' ) {
file_put_contents ( $fileName , $fileContents );
} else {
2017-03-21 17:56:29 +13:00
error_log ( $filename );
2017-03-21 17:47:30 +13:00
error_log ( 'response body was empty' );
}
2016-12-27 14:30:54 +13:00
}
}