Bug 23862: add enumchron to holds-table
[koha.git] / koha-tmpl / intranet-tmpl / lib / tiny_mce / tiny_mce_gzip.pl
1 #!/usr/bin/perl
2 use strict;
3 use warnings FATAL => 'all', NONFATAL => 'redefine';
4
5
6 ###################################################
7 #
8 #  @author Clinton Gormley
9 #  @copyright Copyright 2008, Clinton Gormley, All rights reserved.
10 #
11 #  This file compresses the TinyMCE JavaScript using GZip and
12 #  enables the browser to do two requests instead of one for each .js file.
13 #  Notice: This script defaults the button_tile_map option to true for
14 #  extra performance.
15 #
16 ##################################################
17 our $VERSION  = '2.0.3';
18
19 # Default cache path is subdir to tinymce
20 my $cache_path = 'cache';
21
22 # Custom extra javascripts to pack
23 my @custom_files = qw();
24
25 # Cache for 10 days
26 our $cache_for = 3600 * 24 * 10;
27
28
29 ##################################################
30
31 use File::Spec();
32 use Compress::Zlib();
33 use Digest::MD5 qw(md5_hex);
34 use File::Slurp qw(slurp write_file);
35
36 my %P = %{ get_params() };
37 our $cwd = get_cwd();
38
39 my @extra_headers
40     = $P{charset}
41     ? 'Content-type: text/javascript; charset=' . $P{charset}
42     : 'Content-type: text/javascript';
43
44 # If this file is requested directly, send the JS compressor file
45 # and init with default settings
46 unless ( $P{js} eq 'true' ) {
47     print headers(@extra_headers);
48     print slurp_file('tiny_mce_gzip.js');
49     print "tinyMCE_GZ.init({})\n";
50     exit;
51 }
52
53 my %modules = (
54          custom_files => \@custom_files,
55          ( map { $_ => [ split( ',', $P{$_} ) ] } qw(plugins languages themes) )
56 );
57
58 ## Check if it supports gzip
59 my $accept_header = $ENV{HTTP_ACCEPT_ENCODING} || '';
60 my ($supports_gzip) = ( $accept_header =~ m/((?:x-)?gzip)\b/i );
61 my $compress = ( $supports_gzip && $P{compress} ne 'false' ) ? 1 : 0;
62
63 # Get params for cache
64 $cache_path
65     = File::Spec->file_name_is_absolute($cache_path)
66     ? $cache_path
67     : File::Spec->catdir( $cwd, 'cache' );
68 my $cache_key = get_cache_key( \%modules,         $P{suffix} );
69 my $js_file   = File::Spec->catfile( $cache_path, $cache_key . '.js' );
70 my $gz_file   = File::Spec->catfile( $cache_path, $cache_key . '.gz' );
71 my $cache_file = $compress ? $gz_file : $js_file;
72
73 # Use cached data or generate new?
74 my $data
75     = ( $P{diskcache} eq 'true' && -e $cache_file )
76     ? slurp_file($cache_file)
77     : generate_and_cache_data( \%modules, $compress, $js_file,
78                                $gz_file,  $P{core},  $P{suffix} );
79
80 # Send data
81 if ($compress) {
82     push @extra_headers, "Content-Encoding: $supports_gzip";
83 }
84
85 print headers(@extra_headers);
86 print $data;
87
88 exit;
89
90 #===================================
91 sub get_params {
92 #===================================
93     my $qs = $ENV{QUERY_STRING} || '';
94     $qs =~ s/(%([0-9a-fA-F]{2,2}))/my $c = hex($2); $c < 256 ? chr($c) : $1/eg;
95     my @raw_params = split( /[&;]+/, $qs );
96     my %parsed = map { $_ => '' } qw(
97         plugins languages themes
98         diskcache js compress
99         core suffix charset );
100     while ( my $pair = shift @raw_params ) {
101         my ( $key, $value ) = split( /=/, $pair );
102         next unless exists $parsed{$key};
103         $value ||= '';
104         $value =~ tr/0-9a-zA-Z\-_,//cd;
105         $parsed{$key} = $value;
106     }
107     $parsed{suffix} = $parsed{suffix} eq '_src' ? '_src' : '';
108     return \%parsed;
109 }
110
111 #===================================
112 sub headers {
113 #===================================
114     my @extra_headers = @_;
115     my $date          = scalar( gmtime( time + $cache_for ) ) . " GMT";
116     return <<HEADERS. join( "\n", @extra_headers ) . "\n\n";
117 Vary: Accept-Encoding
118 Expires: $date
119 HEADERS
120
121 }
122
123 #===================================
124 sub slurp_file {
125 #===================================
126     my $file = File::Spec->catfile(@_);
127     unless ( File::Spec->file_name_is_absolute($file) ) {
128         $file = File::Spec->catfile( $cwd, $file );
129     }
130     return slurp( $file, binmode => ':raw' );
131 }
132
133 #===================================
134 sub get_cwd {
135 #===================================
136     return File::Spec->catpath(
137               File::Spec->no_upwards( ( File::Spec->splitpath($0) )[ 0, 1 ] ) );
138 }
139
140 #===================================
141 sub get_cache_key {
142 #===================================
143     my $modules = shift;
144     my $suffix  = shift;
145     my $cache_key = md5_hex(
146                          join( '',
147                                ( map { @{ $modules->{$_} } }
148                                      qw(plugins languages themes custom_files )
149                                ),
150                                $suffix
151                          )
152     );
153
154     # Untaint cache_key
155     ($cache_key) = ( $cache_key =~ /^([0-9a-f]+)$/ );
156     die "Couldn't generate cache key - problem with MD5 libraries?"
157         unless $cache_key;
158
159     return $cache_key;
160 }
161
162 #===================================
163 sub generate_and_cache_data {
164 #===================================
165     my $modules  = shift;
166     my $compress = shift;
167     my $js_file  = shift;
168     my $gz_file  = shift;
169     my $core     = shift;
170     my $suffix   = shift;
171
172     # Core file plus langs
173     my @langs = @{ $modules->{languages} };
174
175     my $js_data = join( '', map { slurp_file( 'langs', "$_.js" ) } @langs );
176
177     # Themes plus their langs
178     foreach my $theme ( @{ $modules->{themes} } ) {
179         $js_data
180             .= slurp_file( 'themes', $theme, "editor_template${suffix}.js" )
181             . join( '',
182                     map { slurp_file( 'themes', $theme, 'langs', "$_.js" ) }
183                         @langs );
184     }
185
186     # Plugins plus their langs
187     foreach my $plugin ( @{ $modules->{plugins} } ) {
188         $js_data
189             .= slurp_file( 'plugins', $plugin, "editor_plugin${suffix}.js" )
190             . join(
191             '',
192             map {
193                 eval {
194                     slurp_file( 'plugins', $plugin, 'langs', "$_.js" );
195                     }
196                     || ''
197                 } @langs
198             );
199     }
200
201     # Any custom files
202     $js_data .= slurp_file($_) for ( @{ $modules->{custom_files} } );
203
204     # If the core is required, add that too
205     unless ( $core eq 'false' ) {
206         $js_data
207             = slurp_file("tiny_mce${suffix}.js")
208             . 'tinyMCE_GZ.start();'
209             . $js_data
210             . 'tinyMCE_GZ.end();';
211     }
212
213     # Compress data
214     my $gz_data = Compress::Zlib::memGzip($js_data)
215         or die "Couldn't gzip data";
216
217     # write files to disk
218     write_file( $js_file, { binmode => ':raw' }, $js_data );
219     write_file( $gz_file, { binmode => ':raw' }, $gz_data );
220
221     # Choose the correct data to be sent
222     return $compress ? $gz_data : $js_data;
223 }
224
225 1;
226
227 =head1 NAME
228
229 TinyMCE Compressor Perl version 2.0.3
230
231 =head1 DESCRIPTION
232
233 TinyMCE Compressor gzips all javascript files in TinyMCE to a single
234 streamable file. This makes the overall download size 75% smaller and
235 the number of requests will also be reduced. The overall initialisation
236 time for TinyMCE will be reduced dramatically if you use this script.
237
238 The Perl fork of the TinyMCE compressor project page is at
239 L<http://github.com/clintongormley/tinymce_compressor>
240
241 =head2 Installation
242
243 Here is a step by step list on how to install the GZip compressor.
244
245 =over
246
247 =item Prerequisites
248
249 Use CPAN to install L<File::Spec>, L<File::Slurp>,
250 L<Digest::MD5> and L<Compress::Zlib>.
251
252 =item Installing files
253
254 Copy the tiny_mce_gzip.js and tiny_mce_gzip.pl to the tiny_mce
255 directory. The same directory that contains the tiny_mce.js file.
256
257 =item Create a cache directory
258
259 Create the sub directory 'C<cache>' under your tiny_mce
260 directory and give your web server permission to write to it, eg:
261
262         cd /path/to/tinymce
263         mkdir cache
264         chown apache cache
265         chmod u+rwx,og-rwx cache
266
267
268 B<PLEASE NOTE:> If you upgrade your Tiny MCE editor, you will need to
269 clear out the cache directory.
270
271 =item Update your code
272
273 Remove the current script tag:
274
275     <script type="text/javascript" src="tinymce/jscripts/tiny_mce/tiny_mce.js"></script>
276
277 And replace it with:
278
279     <script type="text/javascript" src="tinymce/jscripts/tiny_mce/tiny_mce_gzip.js"></script>
280
281 Add the new GZip initialization call (see below) that will
282 tell the compressor what to include in the output. This should be the
283 sum of all themes, plugins and languages contained on page.
284
285 =back
286
287 =head2 Running under mod_perl
288
289 You either need to set up your web server to execute
290 C<tiny_mce_gzip.pl> as a CGI script, or you can configure Apache to run
291 it under mod_perl, which will greatly speed up the response.
292
293 To do this, you could use a configuration like this:
294
295     <Location /tiny_mce/tiny_mce_gzip.pl>
296         SetHandler perl-script
297         PerlResponseHandler ModPerl::Registry
298         PerlOptions +ParseHeaders
299         Options +ExecCGI
300         Order allow,deny
301         Allow from all
302     </Location>
303
304 =head2 Example of configuration
305
306 The example below will pack both themes and all plugins into one
307 file/stream. Remove the things you don't need or add you custom plugins
308 to the settings below. Remember that the tinyMCE_GZ.init call must be
309 placed in B<it's own script tag.>
310
311     <script type="text/javascript" src="tinymce/jscripts/tiny_mce/tiny_mce_gzip.js"></script>
312     <script type="text/javascript">
313         tinyMCE_GZ.init({
314             plugins     : 'style,layer,...etc',
315             themes      : 'simple,advanced',
316             languages   : 'en',
317             disk_cache  : true
318         });
319     </script>
320
321     <!-- Needs to be seperate script tags! -->
322
323     <script type="text/javascript">
324         tinyMCE.init({
325             .. your normal init ..
326         });
327     </script>
328
329 =head2 Troubleshooting
330
331 =over
332
333 =item *
334
335 The GZip compressor can fail to load if the server has odd settings or
336 is missing the required support for it to function. To see compilation
337 errors or other problems we suggest that you use HTTP debugging tools
338 like HTTP Fiddler or, in Firefox, the Firebug addon, or point you
339 browser directly to the GZip file.
340
341 =item *
342
343 Consult the changelog of this script and make sure that you use the
344 latest version of TinyMCE. These two parts are pretty much tied
345 together so there is no guarantee that it will work with older versions
346 of TinyMCE.
347
348 =back
349
350 Visit the TinyMCE forum for help with the TinyMCE Gzip Compressor.
351
352 =head2 Changelog and Bugs
353
354 See the ChangeLog here : changelog.txt
355
356 Please report any bugs that you find to clint@traveljury.com
357
358 =head2 License notice
359
360 The perl part of this library has been written by Clinton Gormley
361 (clint@traveljury.com).
362
363 The javascript part has been taken from the PHP compressor available at
364 MoxieCode.
365
366 This library is under LGPL license but it uses the zlib library, which
367 is free to use in commercial applications. (Read the zlib licence).
368
369 =cut