Skip to content

Instantly share code, notes, and snippets.

@sunnysideup
Last active May 29, 2016 03:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sunnysideup/5e01107b919dc2a45ad78f35ddbd8c35 to your computer and use it in GitHub Desktop.
Save sunnysideup/5e01107b919dc2a45ad78f35ddbd8c35 to your computer and use it in GitHub Desktop.
<?php
/**
* replaces `Requirements_Backend`
* this class blocks all JS and CSS files and intead copies them to a folder
* for webpack inclusion.
*
* `Custom` (inline) CSS / JS still works as normal.
*
*/
class Requirements_Backend_For_Webpack extends Requirements_Backend {
/**
* e.g. /mysite/javascript/test.js
* @ var array
*/
private static $files_to_ignore = array();
/**
* we need this method because Requirements_Backend does not extend Object!
* @param array $array
*/
public static function set_files_to_ignore($array) {self::$files_to_ignore = $array;}
/**
* @ var string
*/
private static $copy_css_to_folder = "themes/base/source/css/requirements";
/**
* we need this method because Requirements_Backend does not extend Object!
* @param string $string
*/
public static function set_copy_css_to_folder($string) {self::$copy_css_to_folder = $string;}
/**
* @ var string
*/
private static $copy_js_to_folder = "themes/base/source/js/requirements";
/**
* we need this method because Requirements_Backend does not extend Object!
* @param string $string
*/
public static function set_copy_js_to_folder($string) {self::$copy_js_to_folder = $string;}
/**
* @ var bool
*/
private static $force_update = true;
public static function set_force_update($bool) {self::$force_update = $bool;}
/**
* Whether to add caching query params to the requests for file-based requirements.
* Eg: themes/myTheme/js/main.js?m=123456789. The parameter is a timestamp generated by
* filemtime. This has the benefit of allowing the browser to cache the URL infinitely,
* while automatically busting this cache every time the file is changed.
*
* @var bool
*/
protected $suffix_requirements = false;
/**
* Whether to combine CSS and JavaScript files
*
* @var bool
*/
protected $combined_files_enabled = false;
/**
* Force the JavaScript to the bottom of the page, even if there's a script tag in the body already
*
* @var boolean
*/
protected $force_js_to_bottom = true;
/**
* Update the given HTML content with the appropriate include tags for the registered
* requirements. Needs to receive a valid HTML/XHTML template in the $content parameter,
* including a head and body tag.
*
* @param string $templateFile No longer used, only retained for compatibility
* @param string $content HTML content that has already been parsed from the $templateFile
* through {@link SSViewer}
* @return string HTML content augmented with the requirements tags
*/
public function includeInHTML($templateFile, $content) {
if($this->themedRequest()) {
//=====================================================================
// start copy-ish from parent class
if(
(strpos($content, '</head>') !== false || strpos($content, '</head ') !== false)
&& ($this->css || $this->javascript || $this->customCSS || $this->customScript || $this->customHeadTags)
) {
$requirements = '';
$jsRequirements = '';
$requirementsCSSFiles = array();
$requirementsJSFiles = array();
// Combine files - updates $this->javascript and $this->css
$this->process_combined_files();
if(Director::isDev()) {
foreach(array_diff_key($this->javascript,$this->blocked) as $file => $dummy) {
$path = Convert::raw2xml($this->path_for_file($file));
if($path) {
$requirementsJSFiles[$path] = $path;
}
}
}
// Add all inline JavaScript *after* including external files they might rely on
if($this->customScript) {
foreach(array_diff_key($this->customScript,$this->blocked) as $script) {
$jsRequirements .= "<script type=\"text/javascript\">\n//<![CDATA[\n";
$jsRequirements .= "$script\n";
$jsRequirements .= "\n//]]>\n</script>\n";
}
}
if(Director::isDev()) {
foreach(array_diff_key($this->css,$this->blocked) as $file => $params) {
$path = Convert::raw2xml($this->path_for_file($file));
if($path) {
$media = (isset($params['media']) && !empty($params['media']))
? " media=\"{$params['media']}\"" : "";
$requirementsCSSFiles[$path."_".$media] = $path;
}
}
}
foreach(array_diff_key($this->customCSS, $this->blocked) as $css) {
$requirements .= "<style type=\"text/css\">\n$css\n</style>\n";
}
foreach(array_diff_key($this->customHeadTags,$this->blocked) as $customHeadTag) {
$requirements .= "$customHeadTag\n";
}
// Remove all newlines from code to preserve layout
$jsRequirements = preg_replace('/>\n*/', '>', $jsRequirements);
// Forcefully put the scripts at the bottom of the body instead of before the first
// script tag.
$content = preg_replace("/(<\/body[^>]*>)/i", $jsRequirements . "\\1", $content);
// Put CSS at the bottom of the head
$content = preg_replace("/(<\/head>)/i", $requirements . "\\1", $content);
//end copy-ish from parent class
//=====================================================================
//copy files ...
if($this->canSaveRequirements()) {
//css
$cssFolder = self::$copy_css_to_folder;
foreach($requirementsCSSFiles as $cssFile) {
$this->moveFileToRequirementsFolder($cssFile, $cssFolder);
}
//js
$jsFolder = self::$copy_js_to_folder;
foreach($requirementsJSFiles as $jsFile) {
$this->moveFileToRequirementsFolder($jsFile, $jsFolder);
}
}
}
return $content;
}
else {
return parent::includeInHTML($templateFile, $content);
}
}
/**
* Attach requirements inclusion to X-Include-JS and X-Include-CSS headers on the given
* HTTP Response
*
* @param SS_HTTPResponse $response
*/
public function include_in_response(SS_HTTPResponse $response)
{
if($this->themedRequest()) {
//do nothing
}
else {
return parent::include_in_response($response);
}
//$this->process_combined_files();
//do nothing ...
}
/**
*
*
*
* @return bool
*/
protected function canSaveRequirements()
{
if(Director::isDev()) {
if($this->themedRequest()) {
$socket = @fsockopen('localhost', 3000, $errno, $errstr, 1);
if($socket) {
return true;
}
}
}
}
/**
*
*
* @return bool
*/
protected function themedRequest()
{
return Config::inst()->get('SSViewer', 'theme') && Config::inst()->get('SSViewer', 'theme_enabled') ? true : false;
}
protected function moveFileToRequirementsFolder($fileLocation, $folderLocation)
{
$base = Director::baseFolder()."/";
$folderLocation = $base.$folderLocation;
Filesystem::makeFolder($folderLocation);
if(!file_exists($folderLocation)) {
user_error('Please update Requirements_Backend_For_Webpack for the right folder or create '.$folderLocation);
}
if(strpos($fileLocation, "//") !== false) {
$to = $folderLocation."/EXTERNALS.README";
$lines = array();
if(file_exists($to)) {
$lines = file($to);
}
if( ! in_array($fileLocation, $lines)) {
$handle = fopen($to, 'a');
$data = $_SERVER['REQUEST_URI']." | ".$fileLocation."\n";
fwrite($handle, $data);
}
}
else {
$from = $base.$fileLocation;
$to = $folderLocation."/".basename($fileLocation);
if(in_array($fileLocation, self::$files_to_ignore)) {
//to be completed ...
}
else {
if( ! file_exists($to) || self::$force_update) {
copy($from, $to);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment