@@ -43,9 +43,13 @@ class Template {
4343 // used by html_head smarty block to add content before </head>
4444 var $ html_head_elements = array ();
4545
46+ const COMBINED_SCRIPTS_TAG = '<!-- COMBINED_SCRIPTS --> ' ;
4647 var $ scriptLoader ;
4748 var $ html_footer_raw_script = array ();
4849
50+ const COMBINED_CSS_TAG = '<!-- COMBINED_CSS --> ' ;
51+ var $ css_by_priority = array ();
52+
4953 function Template ($ root = ". " , $ theme = "" , $ path = "template " )
5054 {
5155 global $ conf , $ lang_info ;
@@ -88,6 +92,8 @@ function Template($root = ".", $theme= "", $path = "template")
8892 $ this ->smarty ->register_block ('html_head ' , array (&$ this , 'block_html_head ' ) );
8993 $ this ->smarty ->register_function ('combine_script ' , array (&$ this , 'func_combine_script ' ) );
9094 $ this ->smarty ->register_function ('get_combined_scripts ' , array (&$ this , 'func_get_combined_scripts ' ) );
95+ $ this ->smarty ->register_function ('combine_css ' , array (&$ this , 'func_combine_css ' ) );
96+ $ this ->smarty ->register_function ('get_combined_css ' , array (&$ this , 'func_get_combined_css ' ) );
9197 $ this ->smarty ->register_block ('footer_script ' , array (&$ this , 'block_footer_script ' ) );
9298 $ this ->smarty ->register_function ('known_script ' , array (&$ this , 'func_known_script ' ) );
9399 $ this ->smarty ->register_prefilter ( array ('Template ' , 'prefilter_white_space ' ) );
@@ -385,8 +391,7 @@ function flush()
385391 {
386392 if (!$ this ->scriptLoader ->did_head ())
387393 {
388- $ search = "\n</head> " ;
389- $ pos = strpos ( $ this ->output , $ search );
394+ $ pos = strpos ( $ this ->output , self ::COMBINED_SCRIPTS_TAG );
390395 if ($ pos !== false )
391396 {
392397 $ scripts = $ this ->scriptLoader ->get_head_scripts ();
@@ -399,10 +404,34 @@ function flush()
399404 .'"></script> ' ;
400405 }
401406
402- $ this ->output = substr_replace ( $ this ->output , "\n" .implode ( "\n" , $ content ), $ pos , 0 );
407+ $ this ->output = substr_replace ( $ this ->output , "\n" .implode ( "\n" , $ content ), $ pos , strlen ( self :: COMBINED_SCRIPTS_TAG ) );
403408 } //else maybe error or warning ?
404409 }
405410
411+ if (!empty ($ this ->css_by_priority ))
412+ {
413+ ksort ($ this ->css_by_priority );
414+ $ combiner = new FileCombiner ('css ' );
415+ foreach ($ this ->css_by_priority as $ files )
416+ {
417+ foreach ($ files as $ file_ver )
418+ {
419+ $ combiner ->add ( $ file_ver [0 ], $ file_ver [1 ] );
420+ }
421+ }
422+ if ( $ combiner ->combine ( $ out_file , $ out_version ) )
423+ {
424+ $ href = get_root_url () . $ out_file ;
425+ if ($ out_version !== false )
426+ $ href .= '?v ' . ($ out_version ? $ out_version : PHPWG_VERSION );
427+ // trigger the event for eventual use of a cdn
428+ $ href = trigger_event ('combined_css ' , $ href , $ out_file , $ out_version );
429+ $ this ->output = str_replace (self ::COMBINED_CSS_TAG ,
430+ '<link rel="stylesheet" type="text/css" href=" ' .$ href .'"> ' ,
431+ $ this ->output );
432+ }
433+ }
434+
406435 if ( count ($ this ->html_head_elements ) )
407436 {
408437 $ search = "\n</head> " ;
@@ -535,17 +564,7 @@ function func_get_combined_scripts($params, &$smarty)
535564
536565 if ($ load ==0 )
537566 {
538- if ($ this ->scriptLoader ->did_head ())
539- fatal_error ('get_combined_scripts several times header ' );
540-
541- $ scripts = $ this ->scriptLoader ->get_head_scripts ();
542- foreach ($ scripts as $ id => $ script )
543- {
544- $ content []=
545- '<script type="text/javascript" src=" '
546- . Template::make_script_src ($ script )
547- .'"></script> ' ;
548- }
567+ return self ::COMBINED_SCRIPTS_TAG ;
549568 }
550569 else
551570 {
@@ -612,6 +631,21 @@ function block_footer_script($params, $content, &$smarty, &$repeat)
612631 $ this ->html_footer_raw_script [] = $ content ;
613632 }
614633 }
634+
635+ function func_combine_css ($ params , &$ smarty )
636+ {
637+ !empty ($ params ['path ' ]) || fatal_error ('combine_css missing path ' );
638+ $ order = (int )@$ params ['order ' ];
639+ $ version = isset ($ params ['version ' ]) ? $ params ['version ' ] : 0 ;
640+ $ this ->css_by_priority [$ order ][] = array ( $ params ['path ' ], $ version );
641+ //var_export( $this->css_by_priority ); echo "<br>";
642+ }
643+
644+ function func_get_combined_css ($ params , &$ smarty )
645+ {
646+ return self ::COMBINED_CSS_TAG ;
647+ }
648+
615649
616650 /**
617651 * This function allows to declare a Smarty prefilter from a plugin, thus allowing
@@ -723,22 +757,23 @@ static function prefilter_language($source, &$smarty)
723757 static function prefilter_local_css ($ source , &$ smarty )
724758 {
725759 $ css = array ();
726-
727760 foreach ($ smarty ->get_template_vars ('themes ' ) as $ theme )
728761 {
729- if (file_exists (PHPWG_ROOT_PATH .'local/css/ ' .$ theme ['id ' ].'-rules.css ' ))
762+ $ f = 'local/css/ ' .$ theme ['id ' ].'-rules.css ' ;
763+ if (file_exists (PHPWG_ROOT_PATH .$ f ))
730764 {
731- array_push ($ css , ' <link rel="stylesheet" type="text/css" href="{$ROOT_URL}local/css/ ' . $ theme [ ' id ' ]. ' -rules.css"> ' );
765+ array_push ($ css , " {combine_css path=' $ f ' order=10} " );
732766 }
733767 }
734- if (file_exists (PHPWG_ROOT_PATH .'local/css/rules.css ' ))
768+ $ f = 'local/css/rules.css ' ;
769+ if (file_exists (PHPWG_ROOT_PATH .$ f ))
735770 {
736- array_push ($ css , ' <link rel="stylesheet" type="text/css" href="{$ROOT_URL}local/css/rules.css"> ' );
771+ array_push ($ css , " {combine_css path=' $ f ' order=10} " );
737772 }
738773
739774 if (!empty ($ css ))
740775 {
741- $ source = str_replace ("\n</head> " , "\n" .implode ( "\n" , $ css )."\n</head> " , $ source );
776+ $ source = str_replace ("\n{get_combined_css} " , "\n" .implode ( "\n" , $ css )."\n{get_combined_css} " , $ source );
742777 }
743778
744779 return $ source ;
@@ -979,4 +1014,122 @@ private static function cmp_by_mode_and_order($s1, $s2)
9791014 }
9801015}
9811016
1017+
1018+ /*Allows merging of javascript and css files into a single one.*/
1019+ final class FileCombiner
1020+ {
1021+ const OUT_SUB_DIR = 'local/combined/ ' ;
1022+ private $ type ; // js or css
1023+ private $ files = array ();
1024+ private $ versions = array ();
1025+
1026+ function FileCombiner ($ type )
1027+ {
1028+ $ this ->type = $ type ;
1029+ }
1030+
1031+ function add ($ file , $ version )
1032+ {
1033+ $ this ->files [] = $ file ;
1034+ $ this ->versions [] = $ version ;
1035+ }
1036+
1037+ function clear ()
1038+ {
1039+ $ this ->files = array ();
1040+ $ this ->versions = array ();
1041+ }
1042+
1043+ function combine (&$ out_file , &$ out_version )
1044+ {
1045+ //var_export($this);
1046+ if (count ($ this ->files ) == 0 )
1047+ {
1048+ return false ;
1049+ }
1050+ if (count ($ this ->files ) == 1 )
1051+ {
1052+ $ out_file = $ this ->files [0 ];
1053+ $ out_version = $ this ->versions [0 ];
1054+ $ this ->clear ();
1055+ return 1 ;
1056+ }
1057+
1058+ global $ conf ;
1059+ $ key = array ();
1060+
1061+ for ($ i =0 ; $ i <count ($ this ->files ); $ i ++)
1062+ {
1063+ $ key [] = $ this ->files [$ i ];
1064+ $ key [] = $ this ->versions [$ i ];
1065+ if ($ conf ['template_compile_check ' ]) $ key [] = filemtime ( PHPWG_ROOT_PATH . $ this ->files [$ i ] );
1066+ }
1067+ $ key = join ('> ' , $ key );
1068+
1069+ $ file = base_convert (crc32 ($ key ),10 ,36 );
1070+ $ file = self ::OUT_SUB_DIR . $ file . '. ' . $ this ->type ;
1071+ if (file_exists ( PHPWG_ROOT_PATH . $ file ) )
1072+ {
1073+ $ out_file = $ file ;
1074+ $ out_version = false ;
1075+ $ this ->clear ();
1076+ return 2 ;
1077+ }
1078+
1079+ $ output = '' ;
1080+ if ($ conf ['debug_template ' ])
1081+ $ output .= "/* " .join ("\n" , $ this ->files )."*/ \n" ;
1082+ foreach ($ this ->files as $ input_file )
1083+ {
1084+ $ output .= "/* BEGIN $ input_file */ \n" ;
1085+ if ($ this ->type == "css " )
1086+ {
1087+ $ output .= $ this ->process_css ($ input_file );
1088+ }
1089+ else
1090+ $ output .= file_get_contents (PHPWG_ROOT_PATH . $ input_file );
1091+ $ output .= "\n" ;
1092+ }
1093+
1094+ file_put_contents ( PHPWG_ROOT_PATH . $ file , $ output );
1095+ $ out_file = $ file ;
1096+ $ out_version = false ;
1097+ $ this ->clear ();
1098+ return 2 ;
1099+ }
1100+
1101+ private function process_css ($ file )
1102+ {
1103+ static $ PATTERN = "#url\(\s*['| \"]{0,1}(.*?)['| \"]{0,1}\s*\)# " ;
1104+ $ css = file_get_contents (PHPWG_ROOT_PATH . $ file );
1105+ if (preg_match_all ($ PATTERN , $ css , $ matches , PREG_SET_ORDER ))
1106+ {
1107+ $ search = $ replace = array ();
1108+ foreach ($ matches as $ match )
1109+ {
1110+ if ( !url_is_remote ($ match [1 ]) || $ match [1 ][0 ] != '/ ' )
1111+ {
1112+ $ relative = dirname ($ file ) . "/ $ match [1 ]" ;
1113+ $ search [] = $ match [0 ];
1114+ $ replace [] = "url(' " . get_absolute_root_url (false ) . $ relative . "') " ;
1115+ }
1116+ }
1117+ $ css = str_replace ($ search , $ replace , $ css );
1118+ }
1119+
1120+ $ imports = preg_match_all ("#@import\s*['| \"]{0,1}(.*?)['| \"]{0,1};# " , $ css , $ matches , PREG_SET_ORDER );
1121+ if ($ imports )
1122+ {
1123+ $ search = $ replace = array ();
1124+ foreach ($ matches as $ match )
1125+ {
1126+ $ search [] = $ match [0 ];
1127+ $ replace [] = $ this ->process_css (dirname ($ file ) . "/ $ match [1 ]" );
1128+ }
1129+ $ css = str_replace ($ search , $ replace , $ css );
1130+ }
1131+ return $ css ;
1132+ }
1133+ }
1134+
9821135?>
0 commit comments