*/
class Abovethefold_Admin_BuildTool
{
/**
* Above the fold controller
*/
public $CTRL;
/**
* Options
*/
public $options;
/**
* Initialize the class and set its properties.
*/
public function __construct(&$CTRL)
{
$this->CTRL = & $CTRL;
$this->options = & $CTRL->options;
/**
* Admin panel specific
*/
if (is_admin()) {
/**
* Handle form submissions
*/
$this->CTRL->loader->add_action('admin_post_abtf_create_critical_package', $this, 'create_critical_package');
}
}
/**
* Create critical package
*/
public function create_critical_package()
{
check_admin_referer('abovethefold');
// @link https://codex.wordpress.org/Function_Reference/stripslashes_deep
$_POST = array_map('stripslashes_deep', $_POST);
$options = get_option('abovethefold');
if (!is_array($options)) {
$options = array();
}
// download package
if (isset($_POST['download_package'])) {
$this->download_package();
exit;
}
// install package
if (isset($_POST['install_package'])) {
$this->install_package();
exit;
}
$url = (isset($_POST['url'])) ? trim($_POST['url']) : '';
$taskname = (isset($_POST['taskname'])) ? trim($_POST['taskname']) : '';
$dimensions = (isset($_POST['dimensions'])) ? $this->CTRL->admin->newline_array(trim($_POST['dimensions'])) : '';
$extra = (isset($_POST['extra']) && intval($_POST['extra']) === 1) ? true : false;
$update = (isset($_POST['update']) && trim($_POST['update']) !== '') ? trim($_POST['update']) : false;
if ($url === '') {
$this->CTRL->admin->set_notice('You did not select a page.', 'ERROR');
wp_redirect(add_query_arg(array( 'page' => 'pagespeed-build-tool','taskname' => $taskname, 'dimensions' => $dimensions, 'extra' => $extra, 'update' => $update ), admin_url('admin.php')));
exit;
}
if ($taskname === '' || !preg_match('|^critical-[a-z0-9\-]+$|Ui', $taskname)) {
$this->CTRL->admin->set_notice('You did not enter a valid task name.', 'ERROR');
wp_redirect(add_query_arg(array( 'page' => 'pagespeed-build-tool','taskname' => $taskname, 'dimensions' => $dimensions, 'extra' => $extra, 'update' => $update ), admin_url('admin.php')));
exit;
}
$originalDimensions = $dimensions;
if (!empty($dimensions)) {
foreach ($dimensions as $n => $dim) {
$dimparts = explode('x', $dim);
if (count($dimparts) !== 2 || !is_numeric($dimparts[0]) || !is_numeric($dimparts[1]) || intval($dimparts[0]) <= 0 || intval($dimparts[1]) <= 0) {
$this->CTRL->admin->set_notice('Dimension '.htmlentities($dim, ENT_COMPAT, 'utf-8').' is not valid.', 'ERROR');
wp_redirect(add_query_arg(array( 'page' => 'pagespeed-build-tool','taskname' => $taskname, 'dimensions' => $dimensions, 'extra' => $extra, 'update' => $update ), admin_url('admin.php')));
exit;
}
$dimensions[$n] = $dimparts;
}
}
// Update
if ($update) {
$criticalcss_files = $this->CTRL->criticalcss->get_theme_criticalcss();
if (!isset($criticalcss_files[$update])) {
$this->CTRL->admin->set_notice('You did not select a valid conditional CSS file to update.', 'ERROR');
wp_redirect(add_query_arg(array( 'page' => 'pagespeed-build-tool','taskname' => $taskname, 'dimensions' => $dimensions, 'extra' => $extra, 'update' => $update ), admin_url('admin.php')));
}
} else {
$update = false;
}
// get page JSON
$pagejson = $this->get_page_json($url);
if (!$pagejson) {
wp_die('Failed to retrieve page JSON for critical CSS generator.');
}
$settings = array(
'dimensions' => $dimensions,
'extra' => $extra,
'update' => $update
);
// Update default build tool settings
$default = get_option('abtf-build-tool-default');
if (!is_array($default)) {
$default = array();
}
$default['taskname'] = $taskname;
$default['url'] = $url;
$default['dimensions'] = $originalDimensions;
$default['extra'] = $extra;
$default['update'] = $update;
// update settings
update_option('abtf-build-tool-default', $default, false);
// download
if (isset($_POST['download'])) {
$this->download_critical_task_package($pagejson, $url, $taskname, $settings);
exit;
}
/**
* Initialize
*/
$gulpdir = get_stylesheet_directory() . '/abovethefold/';
if (!is_dir($gulpdir)) {
if (!@$this->CTRL->mkdir($gulpdir)) {
wp_die('Failed to create ' . $gulpdir);
}
}
$gulptaskdir = $gulpdir . $taskname . '/';
if (is_dir($gulptaskdir)) {
// remove existing
function abtf_rmdir_recursive($dir, $delete = true)
{
$files = array_diff(scandir($dir), array('.','..'));
foreach ($files as $file) {
(is_dir("$dir/$file")) ? abtf_rmdir_recursive("$dir/$file") : @unlink("$dir/$file");
}
return ($delete) ? @rmdir($dir) : false;
}
abtf_rmdir_recursive($gulptaskdir, false);
}
if (!is_dir($gulptaskdir)) {
if (!$this->CTRL->mkdir($gulptaskdir)) {
wp_die('Failed to create ' . $gulptaskdir);
}
}
// css dir
if (!is_dir($gulptaskdir . 'css/')) {
if (!$this->CTRL->mkdir($gulptaskdir . 'css/')) {
wp_die('Failed to create ' . $gulptaskdir . 'css/');
}
}
$gulp_installed = is_dir($gulpdir . 'node_modules/');
// gulp-header added in version 2.7
// @since 2.7
if (!is_dir($gulpdir . 'node_modules/gulp-header/')) {
// remove package / gulpfile to re-create in next steps
if (file_exists($gulpdir . 'package.json')) {
@unlink($gulpdir . 'package.json');
}
if (file_exists($gulpdir . 'gulpfile.js')) {
@unlink($gulpdir . 'gulpfile.js');
}
$gulp_installed = false;
}
// copy package.json if it does not exist
if (!file_exists($gulpdir . 'package.json')) {
copy(WPABTF_PATH . 'modules/critical-css-build-tool/package.json', $gulpdir . 'package.json');
chmod($gulpdir . 'package.json', 0666);
}
// copy gulpfile.js if it does not exist
if (!file_exists($gulpdir . 'gulpfile.js')) {
copy(WPABTF_PATH . 'modules/critical-css-build-tool/gulpfile.js', $gulpdir . 'gulpfile.js');
chmod($gulpdir . 'gulpfile.js', 0666);
}
// add html file
$this->CTRL->file_put_contents($gulptaskdir . '/page.html', $pagejson['html']);
$fullcss = '';
$taskjs_cssfiles = array();
# add css files
foreach ($pagejson['css'] as $file) {
#add it to the zip
$this->CTRL->file_put_contents($gulptaskdir . '/css/' . $file['file'], $file['code']);
$fullcss .= $file['code'];
$taskjs_cssfiles[] = 'TASKPATH/css/' . $file['file'];
}
// add full css file
$this->CTRL->file_put_contents($gulptaskdir . '/full.css', $fullcss);
// add extra css file
if ($extra) {
$this->CTRL->file_put_contents($gulptaskdir . '/extra.css', '/**
* Use this file to append extra CSS to critical.css.
* This CSS code is not processed by the Critical CSS generator to enable correction of the output of the Critical CSS generator.
*/');
}
/**
* Create gulp-critical-task.js
*/
$taskjs = false;
include(WPABTF_PATH . 'modules/critical-css-build-tool/gulp-critical-task.php');
if (empty($taskjs)) {
wp_die('Failed to load gulp-critical.task.js');
}
// add full css file
$this->CTRL->file_put_contents($gulptaskdir . '/gulp-critical-task.js', $taskjs);
// add notice
$this->CTRL->admin->set_notice('
The package has been installed in '.trailingslashit(str_replace(home_url(), '', get_stylesheet_directory_uri())).'abovethefold/
Run gulp '.$taskname.' to generate critical CSS.
', 'NOTICE');
wp_redirect(add_query_arg(array( 'page' => 'pagespeed-build-tool' ), admin_url('admin.php')));
exit;
}
/**
* Download critical package zip
*/
public function download_critical_task_package($pagejson, $url, $taskname, $settings)
{
// ZipArchive requires PHP v5.2+
if (!version_compare(PHP_VERSION, '5.2.0', '>=')) {
wp_die('Creating zipfiles requires PHP v5.2+.');
}
/**
* Create zip object
*/
$zip = new ZipArchive();
# create a temp file & open it
$tmp_file = tempnam('.', '');
if ($zip->open($tmp_file, ZipArchive::CREATE) !== true) {
wp_die('Failed to create zip archive. Please check PHP ZipArchive permissions.');
}
// add html file
$zip->addFromString($taskname . '/page.html', $pagejson['html']);
$fullcss = '';
$taskjs_cssfiles = array();
# add css files
foreach ($pagejson['css'] as $file) {
#add it to the zip
$zip->addFromString($taskname . '/css/' . $file['file'], $file['code']);
$fullcss .= $file['code'];
$taskjs_cssfiles[] = 'TASKPATH/css/' . $file['file'];
}
// add full css file
$zip->addFromString($taskname . '/full.css', $fullcss);
if ($settings['extra']) {
// add extra css file
$zip->addFromString($taskname . '/extra.css', '/**
* Use this file to append extra CSS to critical.css.
* This CSS code is not processed by the Critical CSS generator to enable correction of the output of the Critical CSS generator.
*/');
}
/**
* Create gulp-critical-task.js
*/
$taskjs = false;
include(WPABTF_PATH . 'modules/critical-css-build-tool/gulp-critical-task.php');
if (empty($taskjs)) {
wp_die('Failed to load gulp-critical.task.js');
}
// add full css file
$zip->addFromString($taskname . '/gulp-critical-task.js', $taskjs);
// add package.json
$data = file_get_contents(WPABTF_PATH . 'modules/critical-css-build-tool/package.json');
// add package.json
$zip->addFromString('package.json', $data);
// add package.json
$data = file_get_contents(WPABTF_PATH . 'modules/critical-css-build-tool/gulpfile.js');
// add package.json
$zip->addFromString('gulpfile.js', $data);
$zip->close();
/**
* Download zipfile
*/
header("Content-type: application/zip");
header("Content-Disposition: attachment; filename=".$taskname.".zip");
header("Content-length: " . filesize($tmp_file));
header("Pragma: no-cache");
header("Expires: 0");
readfile($tmp_file);
exit;
}
/**
* Download package.json and gulpfile.js
*/
public function download_package()
{
// ZipArchive requires PHP v5.2+
if (!version_compare(PHP_VERSION, '5.2.0', '>=')) {
wp_die('Creating zipfiles requires PHP v5.2+.');
}
/**
* Create zip object
*/
$zip = new ZipArchive();
# create a temp file & open it
$tmp_file = tempnam('.', '');
if ($zip->open($tmp_file, ZipArchive::CREATE) !== true) {
wp_die('Failed to create zip archive. Please check PHP ZipArchive permissions.');
}
// add package.json
$data = file_get_contents(WPABTF_PATH . 'modules/critical-css-build-tool/package.json');
// add package.json
$zip->addFromString('package.json', $data);
// add package.json
$data = file_get_contents(WPABTF_PATH . 'modules/critical-css-build-tool/gulpfile.js');
// add package.json
$zip->addFromString('gulpfile.js', $data);
$zip->close();
/**
* Download zipfile
*/
header("Content-type: application/zip");
header("Content-Disposition: attachment; filename=wp-abtf-gulp-critical-css.zip");
header("Content-length: " . filesize($tmp_file));
header("Pragma: no-cache");
header("Expires: 0");
readfile($tmp_file);
exit;
}
/**
* Install package.json and gulpfile.js
*/
public function install_package()
{
$gulpdir = get_stylesheet_directory() . '/abovethefold/';
if (!is_dir($gulpdir)) {
if (!$this->CTRL->mkdir($gulpdir)) {
wp_die('Failed to create ' . $gulpdir);
}
}
// copy package.json if it does not exist
if (!file_exists($gulpdir . 'package.json')) {
copy(WPABTF_PATH . 'modules/critical-css-build-tool/package.json', $gulpdir . 'package.json');
}
// copy gulpfile.js if it does not exist
if (!file_exists($gulpdir . 'gulpfile.js')) {
copy(WPABTF_PATH . 'modules/critical-css-build-tool/gulpfile.js', $gulpdir . 'gulpfile.js');
}
// add notice
$this->CTRL->admin->set_notice('The Gulp.js Critical CSS Generator files have been installed in '.trailingslashit(str_replace(home_url(), '', get_stylesheet_directory_uri())).'abovethefold/
Run npm install to install the dependencies.
', 'NOTICE');
wp_redirect(add_query_arg(array( 'page' => 'pagespeed-build-tool' ), admin_url('admin.php')));
exit;
}
/**
* Retrieve HTML and CSS JSON
*/
public function get_page_json($url)
{
// get HTML without CSS
$html = trim($this->CTRL->remote_get($this->CTRL->view_url('abtf-buildtool-html', false, $url)));
if ($html === '') {
// no HTML
return false;
}
// extract full CSS
$cssdata = $this->CTRL->remote_get($this->CTRL->view_url('abtf-buildtool-css', false, $url));
if ($cssdata === '') {
// no CSS data
return false;
}
// extract JSON from result
$cssdata_parsed = explode('--FULL-CSS-JSON--', $cssdata);
if (trim($cssdata_parsed[1]) === '') {
return false;
}
$css = @json_decode(trim($cssdata_parsed[1]), true);
if (!is_array($css)) {
// no CSS
return false;
}
$json_result = array(
'html' => $html,
'css' => array()
);
if (is_array($css) && !empty($css)) {
$file_number = 0;
foreach ($css as $file) {
$file_number++;
if (!isset($file['media'])) {
$file['media'] = array('all');
}
if (!isset($file['inline']) || intval($file['inline']) !== 1) {
$cssfilehost = parse_url($file['src'], PHP_URL_HOST);
$filename = preg_replace(array('|[^a-z0-9\-]+|is','|-+|is'), array('-','-'), $cssfilehost) . '-' . preg_replace('|\?.*$|Ui', '', basename($file['src']));
}
if (in_array('all', $file['media'])) {
if (isset($file['inline']) && intval($file['inline']) === 1) {
$header = "/**\n * Inline CSS exported from ".$url."\n *\n * @inline " . $file['src'] . "\n * @size " . $this->CTRL->admin->human_filesize(strlen($file['inlinecode'])) . "\n * @media ".implode(', ', $file['media']) . "\n * @position ".$file_number."\n */\n\n";
$json_result['css'][] = array(
'file' => $file_number . '-' . $file['src'] . '.css',
'code' => $header . $file['inlinecode']
);
} else {
$header = "/**\n * CSS file exported from ".$url."\n *\n * @file " . $file['src'] . "\n * @size " . $this->CTRL->admin->human_filesize(strlen($file['code'])) . "\n * @media ".implode(', ', $file['media']) . "\n * @position ".$file_number."\n */\n\n";
$json_result['css'][] = array(
'file' => $file_number . '-' . $filename,
'code' => $header . $file['code']
);
}
} else {
if (isset($file['inline']) && intval($file['inline']) === 1) {
$header = "/**\n * Inline CSS exported from ".$url."\n *\n * @inline " . $file['src'] . "\n * @size " . $this->CTRL->admin->human_filesize(strlen($file['inlinecode'])) . "\n * @media ".implode(', ', $file['media']) . "\n * @position ".$file_number."\n */\n\n";
$json_result['css'][] = array(
'file' => $file_number . '-' . $file['src'] . '.css',
'code' => $header . '@media '.implode(', ', $file['media']).' { ' . $file['inlinecode'] . ' }'
);
} else {
$header = "/**\n * CSS file exported from ".$url."\n *\n * @file " . $file['src'] . "\n * @size " . $this->CTRL->admin->human_filesize(strlen($file['code'])) . "\n * @media ".implode(', ', $file['media']) . "\n * @position ".$file_number."\n */\n\n";
// media query
$json_result['css'][] = array(
'file' => $file_number . '-' . $filename,
'code' => $header . '@media '.implode(', ', $file['media']).' { ' . $file['code'] . ' }'
);
}
}
}
}
return $json_result;
}
/**
* Installed
*/
public function is_installed()
{
$gulpdir = get_stylesheet_directory() . '/abovethefold/';
if (!is_dir($gulpdir)) {
return false;
}
if (!file_exists($gulpdir . 'package.json')) {
return false;
}
// copy gulpfile.js if it does not exist
if (!file_exists($gulpdir . 'gulpfile.js')) {
return false;
}
return true;
}
}