View | Details | Raw Unified | Return to bug 29494
Collapse All | Expand All

(-)a/installer/html-template-to-template-toolkit.pl (-239 lines)
Lines 1-238 Link Here
1
#!/usr/bin/perl
2
3
use Modern::Perl;
4
use Carp qw( croak );
5
6
use Getopt::Long qw( GetOptions );
7
use File::Copy qw( copy );
8
9
my $help_msg = <<EOH;
10
This script does a first-cut conversion of koha HTML::Template template files (.tmpl).
11
It creates a mirror of koha-tmpl called koha-tt where converted files will be placed.
12
By default all files will be converted: use the --file (-f) argument to specify
13
  individual files to process.
14
15
Options:
16
    --koharoot (-r): Root directory of koha installation.
17
    --type (-t): template file extenstions to match
18
        (defaults to tmpl|inc|xsl).
19
    --copyall (-c): Also copy across all files in template directory
20
    --file (-f): specify individual files to process
21
    --debug (-d): output more information.
22
EOH
23
24
my $tmpl_in_dir      = 'koha-tmpl';
25
my $tmpl_out_dir     = 'koha-tt';
26
27
# template toolkit variables NOT to scope, in other words, variables that need to remain global (case sensitive)
28
my @globals = ( "themelang","JacketImages","OPACAmazonCoverImages","GoogleJackets","BakerTaylorEnabled",
29
    "SyndeticsEnabled", "OpacRenewalAllowed", "item_level_itypes","noItemTypeImages",
30
    "virtualshelves", "OPACHoldRequests", "COinSinOPACResults", "OPACXSLTResultsDisplay",
31
    "OPACItemsResultsDisplay", "LibraryThingForLibrariesID", "opacuserlogin", "TagsEnabled",
32
    "TagsShowOnList", "TagsInputOnList","loggedinusername","opacbookbag",
33
    "OPACAmazonEnabled", "SyndeticsCoverImages" );
34
35
# Arguments:
36
my $KOHA_ROOT;
37
my $tmpl_extn_match  = "tmpl|inc|xsl"; # Type match defaults to *.tmpl plus *.inc if not specified
38
my $copy_other_files = 0;
39
my @template_files;
40
my @files_w_tmpl_loops;
41
my $verbose          = 0;
42
GetOptions (
43
    "koharoot=s"        => \$KOHA_ROOT,
44
    "type|t=s"          => \$tmpl_extn_match,
45
    "copyall|c"         => \$copy_other_files,
46
    "file|f=s"          => \@template_files,         # array of filenames
47
    "verbose+"          => \$verbose,                # incremental flag
48
) or die $help_msg;
49
50
if ( ! $KOHA_ROOT || ! -d $KOHA_ROOT ) {
51
    croak "Koha root not passed or is not correct.";
52
}
53
if ( ! -d "$KOHA_ROOT/$tmpl_in_dir" ) {
54
    croak "Cannot find template dir ($tmpl_in_dir)";
55
}
56
57
# Attempt to create koha-tt dir..
58
if ( ! -d "$KOHA_ROOT/$tmpl_out_dir" ) {
59
    mkdir("$KOHA_ROOT/$tmpl_out_dir") #, '0755'
60
       or croak "Cannot create $tmpl_out_dir directory in $KOHA_ROOT: $!";
61
}
62
63
# Obtain list of files to process - go recursively through tmpl_in_dir and subdirectories..
64
unless ( scalar(@template_files) ) {
65
    @template_files = mirror_template_dir_structure_return_files("$KOHA_ROOT/$tmpl_in_dir", "$tmpl_extn_match");
66
}
67
foreach my $file (@template_files) {
68
    (my $new_path = $file) =~ s/$tmpl_in_dir/$tmpl_out_dir/;
69
    $new_path =~ s/\.tmpl/.tt/;
70
    $new_path = "$KOHA_ROOT/$new_path" unless ( $new_path =~ m/^$KOHA_ROOT/ );
71
72
    open my $ITMPL, '<', $file or croak "Can't open $file for input: $!";
73
    open my $OTT, '>', $new_path or croak "Can't open $new_path for output: $!";
74
75
    # allows 'proper' handling of for loop scope
76
    # cur_scope is a stack of scopes, the last being the current
77
    #   when opening a for loop push scope onto end, when closing for loop pop
78
    my @cur_scope = ("");
79
    # flag representing if we've found a for loop this iteration
80
    my $for_loop_found = 0;
81
82
    for my $input_tmpl(<$ITMPL>){
83
        my @parts = split "<", $input_tmpl;
84
        for( my $i=0; $i<=$#parts; ++$i ){
85
            my $input_tmpl = $i ? "<" . $parts[$i] : $parts[$i]; # add < sign back in to every part except the first
86
	$for_loop_found = 0;
87
88
	# handle poorly names variable such as f1!, f1+, f1-, f1| and mod
89
	$input_tmpl =~ s/"(\w+)\|"/"$1pipe"/ig;
90
	$input_tmpl =~ s/"(\w+)\+"/"$1plus"/ig;
91
	$input_tmpl =~ s/"(\w+)\-"/"$1minus"/ig;
92
	$input_tmpl =~ s/"(\w+)!"/"$1exclamation"/ig;
93
	$input_tmpl =~ s/"(\w+),(\w+)"/"$1comma$2"/ig; #caused a problem in patron search
94
	$input_tmpl =~ s/NAME="mod"/NAME="modname"/ig;
95
	# handle 'naked' TMPL_VAR "parameter" by turning them into what they should be, TMPL_VAR NAME="parameter"
96
	$input_tmpl =~ s/TMPL_VAR\s+"(\w+)"/TMPL_VAR NAME="$1"/ig;
97
	# make an end (ESCAPE NAME DEFAULT) into a ned (NAME ESCAPE DEFAULT)
98
	$input_tmpl =~ s/ESCAPE="(\w+?)"\s+NAME=['"](\w+?)['"]\s+DEFAULT=['"](.+?)['"]/NAME="$2" ESCAPE="$1" DEFAULT="$3"/ig;
99
100
	# Process..
101
	# NB: if you think you're seeing double, you probably are, *some* (read:most) patterns appear twice: once with quotations marks, once without.
102
    #     trying to combine them into a single pattern proved troublesome as a regex like ['"]?(.*?)['"]? was causing problems and fixing the problem caused (a lot) more complex regex
103
104
	# variables
105
	$input_tmpl =~ s/<[!-]*\s*TMPL_VAR\s+NAME\s?=\s?['"]?\s*(\w*?)\s*['"]?\s+ESCAPE=['"](\w*?)['"]\s+DEFAULT=['"]?(.*?)['"]?\s*-*>/[% DEFAULT $cur_scope[-1]$1="$3" |$2 %]/ig;
106
	$input_tmpl =~ s/<[!-]*\s*TMPL_VAR\s+NAME\s?=\s?['"]\s*(\w*?)\s*['"]\s+ESCAPE=['"]?(\w*?)['"]?\s*-*>/[% $cur_scope[-1]$1 |$2 %]/ig;
107
	$input_tmpl =~ s/<[!-]*\s*TMPL_VAR\s+NAME\s?=\s?(\w*?)\s+ESCAPE=['"]?(\w*?)['"]?\s*-*>/[% $cur_scope[-1]$1 |$2 %]/ig;
108
	$input_tmpl =~ s/<[!-]*\s*TMPL_VAR\s+ESCAPE=['"]?(\w*?)['"]?\s+NAME\s?=\s?['"]?([\w-]*?)['"]?\s*-*>/[% $cur_scope[-1]$2 |$1 %]/ig;
109
	$input_tmpl =~ s/<[!-]*\s*TMPL_VAR\s+NAME\s?=\s?['"]?(\w*?)['"]?\s+DEFAULT=['"](.*?)['"]\s*-*>/[% DEFAULT $cur_scope[-1]$1="$2" %]/ig;
110
	$input_tmpl =~ s/<[!-]*\s*TMPL_VAR\s+NAME\s?=\s?['"]?\s*(\w*?)\s*['"]?\s+DEFAULT=(.*?)\s*-*>/[% DEFAULT $cur_scope[-1]$1=$2 %]/ig;
111
	$input_tmpl =~ s/<[!-]*\s*TMPL[_\s]VAR\s+NAME\s?=\s?['"]?\s*(\w*?)\s*['"]?\s*-*>/[% $cur_scope[-1]$1 %]/ig;
112
	$input_tmpl =~ s/<[!-]*\s*TMPL[_\s]VAR\s+EXPR\s?=\s?['"](.*?)['"]\s*-*>/[% $1 %]/ig;     # TMPL_VAR NAME and TMPL_VAR EXPR are logically equiv
113
	$input_tmpl =~ s/<[!-]*\s*TMPL[_\s]VAR\s+EXPR\s?=\s?(.*?)\s*-*>/[% $1 %]/ig;
114
115
	# if, elseif and unless blocks
116
	$input_tmpl =~ s/<[!-]*\s*TMPL_IF\s+EXPR\s?=\s?['"](.*?)['"]\s*-*>/[% IF ( $1 ) %]/ig;
117
	$input_tmpl =~ s/<[!-]*\s*TMPL_IF\s+EXPR\s?=\s?(.*?)\s*-*>/[% IF ( $1 ) %]/ig;
118
	$input_tmpl =~ s/<[!-]*\s*TMPL_IF\s+NAME\s?=\s?['"]\s*(\w*?)\s*['"]\s*-*>/[% IF ( $cur_scope[-1]$1 ) %]/ig;
119
	$input_tmpl =~ s/<[!-]*\s*TMPL_IF\s+NAME\s?=\s?(\w*?)\s*-*>/[% IF ( $cur_scope[-1]$1 ) %]/ig;
120
	$input_tmpl =~ s/<[!-]*\s*TMPL_IF\s+['"](.*?)['"]\s*-*>/[% IF ( $cur_scope[-1]$1 ) %]/ig;
121
	$input_tmpl =~ s/<[!-]*\s*TMPL_IF\s+([\w\s]*?)\s*-*>/[% IF ( $cur_scope[-1]$1 ) %]/ig;
122
123
	$input_tmpl =~ s/<[!-]*\s*TMPL_ELSIF\s+EXPR\s?=\s?['"](.*?)['"]\s*-*>/[% ELSIF ( $1 ) %]/ig;
124
	$input_tmpl =~ s/<[!-]*\s*TMPL_ELSIF\s+EXPR\s?=\s?(.*?)\s*-*>/[% ELSIF ( $1 ) %]/ig;
125
	$input_tmpl =~ s/<[!-]*\s*TMPL_ELSIF\s+NAME\s?=\s?['"](\w*?)['"]\s*-*>/[% ELSIF ( $cur_scope[-1]$1 ) %]/ig;
126
	$input_tmpl =~ s/<[!-]*\s*TMPL_ELSIF\s+NAME\s?=\s?(\w*?)\s*-*>/[% ELSIF ( $cur_scope[-1]$1 ) %]/ig;
127
	$input_tmpl =~ s/<[!-]*\s*TMPL_ELSIF\s+['"](\w*?)['"]\s*-*>/[% ELSIF ( $cur_scope[-1]$1 ) %]/ig;
128
	$input_tmpl =~ s/<[!-]*\s*TMPL_ELSIF\s+(\w*?)\s*-*>/[% ELSIF ( $cur_scope[-1]$1 ) %]/ig;
129
130
	$input_tmpl =~ s/<[!-]*\s*TMPL_ELSE\s*-*>/[% ELSE %]/ig;
131
	$input_tmpl =~ s/<[!-]*\s*\/TMPL_IF\s*-*>/[% END %]/ig;
132
133
	$input_tmpl =~ s/<[!-]*\s*TMPL_UNLESS\s+NAME\s?=\s?['"]?(\w*?)['"]?\s*-*>/[% UNLESS ( $cur_scope[-1]$1 ) %]/ig;
134
	$input_tmpl =~ s/<[!-]*\s*\/TMPL_UNLESS\s*-*>/[% END %]/ig;
135
	# includes
136
	$input_tmpl =~ s/<[!-]*\s*TMPL_INCLUDE\s+NAME\s?=\s?"(.*?\.inc)"\s*-*>/[% INCLUDE '$1' %]/ig;
137
	$input_tmpl =~ s/<[!-]*\s*TMPL_INCLUDE\s+NAME\s?=\s?"(.*?)"\s*-*>/[% INCLUDE $1 %]/ig;
138
139
        #reverse scoping bug fix
140
        for my $tag (@globals){
141
            next unless $cur_scope[-1];
142
            $input_tmpl =~ s/$cur_scope[-1]$tag/$tag/g;
143
        }
144
145
 	if ($input_tmpl =~ m/<[!-]*\s*TMPL_LOOP/i ){
146
	    $for_loop_found = 1;
147
	}
148
149
	$input_tmpl =~ s/<[!-]*\s*TMPL_LOOP\s+NAME\s?=\s?['"](?<SCOPE>.*?)['"]\s*-*>/"[% FOREACH " . substr($+{SCOPE}, 0, -1) . " IN $cur_scope[-1]$1 %]"/ieg;
150
	$input_tmpl =~ s/<[!-]*\s*TMPL_LOOP\s+NAME\s?=\s?(?<SCOPE>.*?)\s*-*>/"[% FOREACH " . substr($+{SCOPE}, 0, -1) . " IN $cur_scope[-1]$1 %]"/ieg;
151
152
	# handle new scope
153
	if($for_loop_found){
154
	    my $scope = substr($+{SCOPE}, 0, -1) . ".";
155
	    push(@cur_scope, $scope);
156
	    $for_loop_found = 0;
157
	}
158
159
	# handle loops and old scope
160
	if ( $input_tmpl =~ m/<!--[\s\/]*TMPL_LOOP\s*-->/i ) {
161
	    push(@files_w_tmpl_loops, $new_path);
162
	    pop(@cur_scope);
163
	}
164
165
	$input_tmpl =~ s/<[!-]*\s*\/TMPL_LOOP\s*-*>/[% END %]/ig;
166
167
	# misc 'patches'
168
	$input_tmpl =~ s/\seq\s/ == /ig;
169
	$input_tmpl =~ s/HTML/html/g;
170
	$input_tmpl =~ s/URL/url/g;
171
        $input_tmpl =~ s/dhtmlcalendar_dateformat/DHTMLcalendar_dateformat/ig;
172
	$input_tmpl =~ s/\w*\.__first__/loop.first/ig;
173
	$input_tmpl =~ s/\w*\.__last__/loop.last/ig;
174
	$input_tmpl =~ s/\w*\.__odd__/loop.odd/ig;
175
	$input_tmpl =~ s/\w*\.__even__/loop.even/ig;
176
	$input_tmpl =~ s/\w*\.__counter__/loop.count/ig; #loop.count gives the range (0..max) whereas loop.index gives the range (1..max+1), __counter__ is unknown
177
178
	# hack to get around lack of javascript filter
179
	$input_tmpl =~ s/\|\s*JS/|replace("'", "\\'") |replace('"', '\\"') |replace('\\n', '\\\\n') |replace('\\r', '\\\\r')/ig;
180
    
181
	# Write out..
182
        print $OTT $input_tmpl;
183
        }
184
    }
185
    close $ITMPL;
186
    close $OTT;
187
}
188
189
if ( scalar(@files_w_tmpl_loops) && $verbose ) {
190
    print "\nThese files contain TMPL_LOOPs that need double checking:\n";
191
    foreach my $file (@files_w_tmpl_loops) {
192
        print "$file\n";
193
    }
194
}
195
196
## SUB-ROUTINES ##
197
198
# Create new directory structure and return list of template files
199
sub mirror_template_dir_structure_return_files {
200
    my($dir, $type) = @_;
201
202
    my @files = ();
203
    if ( opendir(DIR, $dir) ) {
204
        my @dirent = readdir DIR;   # because DIR is shared when recursing
205
        closedir DIR;
206
        for my $dirent (@dirent) {
207
            my $path = "$dir/$dirent";
208
            if ( $dirent =~ /^\./ ) {
209
              ;
210
            }
211
            elsif ( -f $path ) {
212
                (my $new_path = $path) =~ s/$tmpl_in_dir/$tmpl_out_dir/;
213
                $new_path = "$KOHA_ROOT/$new_path" unless ( $new_path =~ m/^$KOHA_ROOT/ );
214
                if ( !defined $type || $dirent =~ /\.(?:$type)$/) {
215
                    push(@files, $path);
216
                }
217
                elsif ( $copy_other_files ) {
218
                    copy($path, $new_path)
219
                      or croak "Failed to copy $path to $new_path: $!";
220
                }
221
            }
222
            elsif ( -d $path ) {
223
                (my $new_path = $path) =~ s/$tmpl_in_dir/$tmpl_out_dir/;
224
                $new_path = "$KOHA_ROOT/$new_path" unless ( $new_path =~ m/^$KOHA_ROOT/ );
225
                if ( ! -d $new_path ) {
226
                    mkdir($new_path) #, '0755'
227
                      or croak "Failed to create " . $new_path ." directory: $!";
228
                }
229
                my @sub_files = mirror_template_dir_structure_return_files($path, $type);
230
                push(@files, @sub_files) if ( scalar(@sub_files) );
231
            }
232
        }
233
    } else {
234
        warn("Cannot open $dir: $! ... skipping");
235
    }
236
237
    return @files;
238
}
239
- 

Return to bug 29494