12
12
use warnings;
13
13
14
14
use Getopt::Long;
15
+ use URI::URL qw( url) ;
16
+ use LWP::UserAgent;
17
+ use HTML::TreeBuilder;
18
+
19
+ use Git;
20
+ use MediaWiki::API;
21
+ use Git::Mediawiki qw( clean_filename connect_maybe
22
+ EMPTY HTTP_CODE_PAGE_NOT_FOUND) ;
15
23
16
24
# By default, use UTF-8 to communicate with Git and the user
17
25
binmode STDERR , ' :encoding(UTF-8)' ;
@@ -26,9 +34,26 @@ sub v_print {
26
34
return ;
27
35
}
28
36
37
+ # Preview parameters
38
+ my $file_name = EMPTY;
39
+ my $remote_name = EMPTY;
40
+ my $preview_file_name = EMPTY;
41
+ my $autoload = 0;
42
+ sub file {
43
+ $file_name = shift ;
44
+ return $file_name ;
45
+ }
46
+
29
47
my %commands = (
30
48
' help' =>
31
- [\&help, {}, \&help]
49
+ [\&help, {}, \&help],
50
+ ' preview' =>
51
+ [\&preview, {
52
+ ' <>' => \&file,
53
+ ' output|o=s' => \$preview_file_name ,
54
+ ' remote|r=s' => \$remote_name ,
55
+ ' autoload|a' => \$autoload
56
+ }, \&preview_help]
32
57
);
33
58
34
59
# Search for sub-command
@@ -47,6 +72,288 @@ sub v_print {
47
72
# Launch command
48
73
&{$cmd -> [0]};
49
74
75
+ # ############################ Preview Functions ################################
76
+
77
+ sub preview_help {
78
+ print {*STDOUT } <<'END' ;
79
+ USAGE: git mw preview [--remote|-r <remote name>] [--autoload|-a]
80
+ [--output|-o <output filename>] [--verbose|-v]
81
+ <blob> | <filename>
82
+
83
+ DESCRIPTION:
84
+ Preview is an utiliy to preview local content of a mediawiki repo as if it was
85
+ pushed on the remote.
86
+
87
+ For that, preview searches for the remote name of the current branch's
88
+ upstream if --remote is not set. If that remote is not found or if it
89
+ is not a mediawiki, it lists all mediawiki remotes configured and asks
90
+ you to replay your command with the --remote option set properly.
91
+
92
+ Then, it searches for a file named 'filename'. If it's not found in
93
+ the current dir, it will assume it's a blob.
94
+
95
+ The content retrieved in the file (or in the blob) will then be parsed
96
+ by the remote mediawiki and combined with a template retrieved from
97
+ the mediawiki.
98
+
99
+ Finally, preview will save the HTML result in a file. and autoload it
100
+ in your default web browser if the option --autoload is present.
101
+
102
+ OPTIONS:
103
+ -r <remote name>, --remote <remote name>
104
+ If the remote is a mediawiki, the template and the parse engine
105
+ used for the preview will be those of that remote.
106
+ If not, a list of valid remotes will be shown.
107
+
108
+ -a, --autoload
109
+ Try to load the HTML output in a new tab (or new window) of your
110
+ default web browser.
111
+
112
+ -o <output filename>, --output <output filename>
113
+ Change the HTML output filename. Default filename is based on the
114
+ input filename with its extension replaced by '.html'.
115
+
116
+ -v, --verbose
117
+ Show more information on what's going on under the hood.
118
+ END
119
+ exit ;
120
+ }
121
+
122
+ sub preview {
123
+ my $wiki ;
124
+ my ($remote_url , $wiki_page_name );
125
+ my ($new_content , $template );
126
+ my $file_content ;
127
+
128
+ if ($file_name eq EMPTY) {
129
+ die " Missing file argument, see `git mw help`\n " ;
130
+ }
131
+
132
+ v_print(" ### Selecting remote\n " );
133
+ if ($remote_name eq EMPTY) {
134
+ $remote_name = find_upstream_remote_name();
135
+ if ($remote_name ) {
136
+ $remote_url = mediawiki_remote_url_maybe($remote_name );
137
+ }
138
+
139
+ if (! $remote_url ) {
140
+ my @valid_remotes = find_mediawiki_remotes();
141
+
142
+ if ($#valid_remotes == 0) {
143
+ print {*STDERR } " No mediawiki remote in this repo. \n " ;
144
+ exit 1;
145
+ } else {
146
+ my $remotes_list = join (" \n\t " , @valid_remotes );
147
+ print {*STDERR } <<"MESSAGE" ;
148
+ There are multiple mediawiki remotes, which of:
149
+ ${remotes_list}
150
+ do you want ? Use the -r option to specify the remote.
151
+ MESSAGE
152
+ }
153
+
154
+ exit 1;
155
+ }
156
+ } else {
157
+ if (!is_valid_remote($remote_name )) {
158
+ die " ${remote_name} is not a remote\n " ;
159
+ }
160
+
161
+ $remote_url = mediawiki_remote_url_maybe($remote_name );
162
+ if (! $remote_url ) {
163
+ die " ${remote_name} is not a mediawiki remote\n " ;
164
+ }
165
+ }
166
+ v_print(" selected remote:\n\t name: ${remote_name} \n\t url: ${remote_url} \n " );
167
+
168
+ $wiki = connect_maybe($wiki , $remote_name , $remote_url );
169
+
170
+ # Read file content
171
+ if (! -e $file_name ) {
172
+ $file_content = git_cmd_try {
173
+ Git::command(' cat-file' , ' blob' , $file_name ); }
174
+ " %s failed w/ code %d " ;
175
+
176
+ if ($file_name =~ / (.+):(.+)/ ) {
177
+ $file_name = $2 ;
178
+ }
179
+ } else {
180
+ open my $read_fh , " <" , $file_name
181
+ or die " could not open ${file_name} : $! \n " ;
182
+ $file_content = do { local $/ = undef ; <$read_fh > };
183
+ close $read_fh
184
+ or die " unable to close: $! \n " ;
185
+ }
186
+
187
+ v_print(" ### Retrieving template\n " );
188
+ ($wiki_page_name = clean_filename($file_name )) =~ s /\. [^.]+$// ;
189
+ $template = get_template($remote_url , $wiki_page_name );
190
+
191
+ v_print(" ### Parsing local content\n " );
192
+ $new_content = $wiki -> api({
193
+ action => ' parse' ,
194
+ text => $file_content ,
195
+ title => $wiki_page_name
196
+ }, {
197
+ skip_encoding => 1
198
+ }) or die " No response from remote mediawiki\n " ;
199
+ $new_content = $new_content -> {' parse' }-> {' text' }-> {' *' };
200
+
201
+ v_print(" ### Merging contents\n " );
202
+ if ($preview_file_name eq EMPTY) {
203
+ ($preview_file_name = $file_name ) =~ s /\. [^.]+$/ .html/ ;
204
+ }
205
+ open (my $save_fh , ' >:encoding(UTF-8)' , $preview_file_name )
206
+ or die " Could not open: $! \n " ;
207
+ print {$save_fh } merge_contents($template , $new_content , $remote_url );
208
+ close ($save_fh )
209
+ or die " Could not close: $! \n " ;
210
+
211
+ v_print(" ### Results\n " );
212
+ if ($autoload ) {
213
+ v_print(" Launching browser w/ file: ${preview_file_name} " );
214
+ system (' git' , ' web--browse' , $preview_file_name );
215
+ } else {
216
+ print {*STDERR } " Preview file saved as: ${preview_file_name} \n " ;
217
+ }
218
+
219
+ exit ;
220
+ }
221
+
222
+ # uses global scope variable: $remote_name
223
+ sub merge_contents {
224
+ my $template = shift ;
225
+ my $content = shift ;
226
+ my $remote_url = shift ;
227
+ my ($content_tree , $html_tree , $mw_content_text );
228
+ my $template_content_id = ' bodyContent' ;
229
+
230
+ $html_tree = HTML::TreeBuilder-> new;
231
+ $html_tree -> parse($template );
232
+
233
+ $content_tree = HTML::TreeBuilder-> new;
234
+ $content_tree -> parse($content );
235
+
236
+ $template_content_id = Git::config(" remote.${remote_name} .mwIDcontent" )
237
+ || $template_content_id ;
238
+ v_print(" Using '${template_content_id} ' as the content ID\n " );
239
+
240
+ $mw_content_text = $html_tree -> look_down(' id' , $template_content_id );
241
+ if (!defined $mw_content_text ) {
242
+ print {*STDERR } <<"CONFIG" ;
243
+ Could not combine the new content with the template. You might want to
244
+ configure `mediawiki.IDContent` in your config:
245
+ git config --add remote.${remote_name} .mwIDcontent <id>
246
+ and re-run the command afterward.
247
+ CONFIG
248
+ exit 1;
249
+ }
250
+ $mw_content_text -> delete_content();
251
+ $mw_content_text -> push_content($content_tree );
252
+
253
+ make_links_absolute($html_tree , $remote_url );
254
+
255
+ return $html_tree -> as_HTML;
256
+ }
257
+
258
+ sub make_links_absolute {
259
+ my $html_tree = shift ;
260
+ my $remote_url = shift ;
261
+ for (@{ $html_tree -> extract_links() }) {
262
+ my ($link , $element , $attr ) = @{ $_ };
263
+ my $url = url($link )-> canonical;
264
+ if ($url !~ / #/ ) {
265
+ $element -> attr($attr , URI-> new_abs($url , $remote_url ));
266
+ }
267
+ }
268
+ return $html_tree ;
269
+ }
270
+
271
+ sub is_valid_remote {
272
+ my $remote = shift ;
273
+ my @remotes = git_cmd_try {
274
+ Git::command(' remote' ) }
275
+ " %s failed w/ code %d " ;
276
+ my $found_remote = 0;
277
+ foreach my $remote (@remotes ) {
278
+ if ($remote eq $remote ) {
279
+ $found_remote = 1;
280
+ last ;
281
+ }
282
+ }
283
+ return $found_remote ;
284
+ }
285
+
286
+ sub find_mediawiki_remotes {
287
+ my @remotes = git_cmd_try {
288
+ Git::command(' remote' ); }
289
+ " %s failed w/ code %d " ;
290
+ my $remote_url ;
291
+ my @valid_remotes = ();
292
+ foreach my $remote (@remotes ) {
293
+ $remote_url = mediawiki_remote_url_maybe($remote );
294
+ if ($remote_url ) {
295
+ push (@valid_remotes , $remote );
296
+ }
297
+ }
298
+ return @valid_remotes ;
299
+ }
300
+
301
+ sub find_upstream_remote_name {
302
+ my $current_branch = git_cmd_try {
303
+ Git::command_oneline(' symbolic-ref' , ' --short' , ' HEAD' ) }
304
+ " %s failed w/ code %d " ;
305
+ return Git::config(" branch.${current_branch} .remote" );
306
+ }
307
+
308
+ sub mediawiki_remote_url_maybe {
309
+ my $remote = shift ;
310
+
311
+ # Find remote url
312
+ my $remote_url = Git::config(" remote.${remote} .url" );
313
+ if ($remote_url =~ s / mediawiki::(.*)/ $1 / ) {
314
+ return url($remote_url )-> canonical;
315
+ }
316
+
317
+ return ;
318
+ }
319
+
320
+ sub get_template {
321
+ my $url = shift ;
322
+ my $page_name = shift ;
323
+ my ($req , $res , $code , $url_after );
324
+
325
+ $req = LWP::UserAgent-> new;
326
+ if ($verbose ) {
327
+ $req -> show_progress(1);
328
+ }
329
+
330
+ $res = $req -> get(" ${url} /index.php?title=${page_name} " );
331
+ if (!$res -> is_success) {
332
+ $code = $res -> code;
333
+ $url_after = $res -> request()-> uri(); # resolve all redirections
334
+ if ($code == HTTP_CODE_PAGE_NOT_FOUND) {
335
+ if ($verbose ) {
336
+ print {*STDERR } <<"WARNING" ;
337
+ Warning: Failed to retrieve '$page_name '. Create it on the mediawiki if you want
338
+ all the links to work properly.
339
+ Trying to use the mediawiki homepage as a fallback template ...
340
+ WARNING
341
+ }
342
+
343
+ # LWP automatically redirects GET request
344
+ $res = $req -> get(" ${url} /index.php" );
345
+ if (!$res -> is_success) {
346
+ $url_after = $res -> request()-> uri(); # resolve all redirections
347
+ die " Failed to get homepage @ ${url_after} w/ code ${code} \n " ;
348
+ }
349
+ } else {
350
+ die " Failed to get '${page_name} ' @ ${url_after} w/ code ${code} \n " ;
351
+ }
352
+ }
353
+
354
+ return $res -> decoded_content;
355
+ }
356
+
50
357
# ############################# Help Functions ##################################
51
358
52
359
sub help {
@@ -55,6 +362,7 @@ sub help {
55
362
56
363
git mw commands are:
57
364
help Display help information about git mw
365
+ preview Parse and render local file into HTML
58
366
END
59
367
exit ;
60
368
}
0 commit comments