 442f5aaccc
			
		
	
	442f5aaccc
	
	
	
		
			
			Change the link to the canonical one, the old link redirects to the new one. Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
		
			
				
	
	
		
			369 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			369 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/perl
 | |
| 
 | |
| # Copyright (C) 2013
 | |
| #     Benoit Person <benoit.person@ensimag.imag.fr>
 | |
| #     Celestin Matte <celestin.matte@ensimag.imag.fr>
 | |
| # License: GPL v2 or later
 | |
| 
 | |
| # Set of tools for git repo with a mediawiki remote.
 | |
| # Documentation & bugtracker: https://github.com/Git-Mediawiki/Git-Mediawiki
 | |
| 
 | |
| use strict;
 | |
| use warnings;
 | |
| 
 | |
| use Getopt::Long;
 | |
| use URI::URL qw(url);
 | |
| use LWP::UserAgent;
 | |
| use HTML::TreeBuilder;
 | |
| 
 | |
| use Git;
 | |
| use MediaWiki::API;
 | |
| use Git::Mediawiki qw(clean_filename connect_maybe
 | |
| 					EMPTY HTTP_CODE_PAGE_NOT_FOUND);
 | |
| 
 | |
| # By default, use UTF-8 to communicate with Git and the user
 | |
| binmode STDERR, ':encoding(UTF-8)';
 | |
| binmode STDOUT, ':encoding(UTF-8)';
 | |
| 
 | |
| # Global parameters
 | |
| my $verbose = 0;
 | |
| sub v_print {
 | |
| 	if ($verbose) {
 | |
| 		return print {*STDERR} @_;
 | |
| 	}
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| # Preview parameters
 | |
| my $file_name = EMPTY;
 | |
| my $remote_name = EMPTY;
 | |
| my $preview_file_name = EMPTY;
 | |
| my $autoload = 0;
 | |
| sub file {
 | |
| 	$file_name = shift;
 | |
| 	return $file_name;
 | |
| }
 | |
| 
 | |
| my %commands = (
 | |
| 	'help' =>
 | |
| 		[\&help, {}, \&help],
 | |
| 	'preview' =>
 | |
| 		[\&preview, {
 | |
| 			'<>' => \&file,
 | |
| 			'output|o=s' => \$preview_file_name,
 | |
| 			'remote|r=s' => \$remote_name,
 | |
| 			'autoload|a' => \$autoload
 | |
| 		}, \&preview_help]
 | |
| );
 | |
| 
 | |
| # Search for sub-command
 | |
| my $cmd = $commands{'help'};
 | |
| for (0..@ARGV-1) {
 | |
| 	if (defined $commands{$ARGV[$_]}) {
 | |
| 		$cmd = $commands{$ARGV[$_]};
 | |
| 		splice @ARGV, $_, 1;
 | |
| 		last;
 | |
| 	}
 | |
| };
 | |
| GetOptions( %{$cmd->[1]},
 | |
| 	'help|h' => \&{$cmd->[2]},
 | |
| 	'verbose|v'  => \$verbose);
 | |
| 
 | |
| # Launch command
 | |
| &{$cmd->[0]};
 | |
| 
 | |
| ############################# Preview Functions ################################
 | |
| 
 | |
| sub preview_help {
 | |
| 	print {*STDOUT} <<'END';
 | |
| USAGE: git mw preview [--remote|-r <remote name>] [--autoload|-a]
 | |
|                       [--output|-o <output filename>] [--verbose|-v]
 | |
|                       <blob> | <filename>
 | |
| 
 | |
| DESCRIPTION:
 | |
| Preview is an utiliy to preview local content of a mediawiki repo as if it was
 | |
| pushed on the remote.
 | |
| 
 | |
| For that, preview searches for the remote name of the current branch's
 | |
| upstream if --remote is not set. If that remote is not found or if it
 | |
| is not a mediawiki, it lists all mediawiki remotes configured and asks
 | |
| you to replay your command with the --remote option set properly.
 | |
| 
 | |
| Then, it searches for a file named 'filename'. If it's not found in
 | |
| the current dir, it will assume it's a blob.
 | |
| 
 | |
| The content retrieved in the file (or in the blob) will then be parsed
 | |
| by the remote mediawiki and combined with a template retrieved from
 | |
| the mediawiki.
 | |
| 
 | |
| Finally, preview will save the HTML result in a file. and autoload it
 | |
| in your default web browser if the option --autoload is present.
 | |
| 
 | |
| OPTIONS:
 | |
|     -r <remote name>, --remote <remote name>
 | |
|         If the remote is a mediawiki, the template and the parse engine
 | |
|         used for the preview will be those of that remote.
 | |
|         If not, a list of valid remotes will be shown.
 | |
| 
 | |
|     -a, --autoload
 | |
|         Try to load the HTML output in a new tab (or new window) of your
 | |
|         default web browser.
 | |
| 
 | |
|     -o <output filename>, --output <output filename>
 | |
|         Change the HTML output filename. Default filename is based on the
 | |
|         input filename with its extension replaced by '.html'.
 | |
| 
 | |
|     -v, --verbose
 | |
|         Show more information on what's going on under the hood.
 | |
| END
 | |
| 	exit;
 | |
| }
 | |
| 
 | |
| sub preview {
 | |
| 	my $wiki;
 | |
| 	my ($remote_url, $wiki_page_name);
 | |
| 	my ($new_content, $template);
 | |
| 	my $file_content;
 | |
| 
 | |
| 	if ($file_name eq EMPTY) {
 | |
| 		die "Missing file argument, see `git mw help`\n";
 | |
| 	}
 | |
| 
 | |
| 	v_print("### Selecting remote\n");
 | |
| 	if ($remote_name eq EMPTY) {
 | |
| 		$remote_name = find_upstream_remote_name();
 | |
| 		if ($remote_name) {
 | |
| 			$remote_url = mediawiki_remote_url_maybe($remote_name);
 | |
| 		}
 | |
| 
 | |
| 		if (! $remote_url) {
 | |
| 			my @valid_remotes = find_mediawiki_remotes();
 | |
| 
 | |
| 			if ($#valid_remotes == 0) {
 | |
| 				print {*STDERR} "No mediawiki remote in this repo. \n";
 | |
| 				exit 1;
 | |
| 			} else {
 | |
| 				my $remotes_list = join("\n\t", @valid_remotes);
 | |
| 				print {*STDERR} <<"MESSAGE";
 | |
| There are multiple mediawiki remotes, which of:
 | |
| 	${remotes_list}
 | |
| do you want ? Use the -r option to specify the remote.
 | |
| MESSAGE
 | |
| 			}
 | |
| 
 | |
| 			exit 1;
 | |
| 		}
 | |
| 	} else {
 | |
| 		if (!is_valid_remote($remote_name)) {
 | |
| 			die "${remote_name} is not a remote\n";
 | |
| 		}
 | |
| 
 | |
| 		$remote_url = mediawiki_remote_url_maybe($remote_name);
 | |
| 		if (! $remote_url) {
 | |
| 			die "${remote_name} is not a mediawiki remote\n";
 | |
| 		}
 | |
| 	}
 | |
| 	v_print("selected remote:\n\tname: ${remote_name}\n\turl: ${remote_url}\n");
 | |
| 
 | |
| 	$wiki = connect_maybe($wiki, $remote_name, $remote_url);
 | |
| 
 | |
| 	# Read file content
 | |
| 	if (! -e $file_name) {
 | |
| 		$file_content = git_cmd_try {
 | |
| 			Git::command('cat-file', 'blob', $file_name); }
 | |
| 			"%s failed w/ code %d";
 | |
| 
 | |
| 		if ($file_name =~ /(.+):(.+)/) {
 | |
| 			$file_name = $2;
 | |
| 		}
 | |
| 	} else {
 | |
| 		open my $read_fh, "<", $file_name
 | |
| 			or die "could not open ${file_name}: $!\n";
 | |
| 		$file_content = do { local $/ = undef; <$read_fh> };
 | |
| 		close $read_fh
 | |
| 			or die "unable to close: $!\n";
 | |
| 	}
 | |
| 
 | |
| 	v_print("### Retrieving template\n");
 | |
| 	($wiki_page_name = clean_filename($file_name)) =~ s/\.[^.]+$//;
 | |
| 	$template = get_template($remote_url, $wiki_page_name);
 | |
| 
 | |
| 	v_print("### Parsing local content\n");
 | |
| 	$new_content = $wiki->api({
 | |
| 		action => 'parse',
 | |
| 		text => $file_content,
 | |
| 		title => $wiki_page_name
 | |
| 	}, {
 | |
| 		skip_encoding => 1
 | |
| 	}) or die "No response from remote mediawiki\n";
 | |
| 	$new_content = $new_content->{'parse'}->{'text'}->{'*'};
 | |
| 
 | |
| 	v_print("### Merging contents\n");
 | |
| 	if ($preview_file_name eq EMPTY) {
 | |
| 		($preview_file_name = $file_name) =~ s/\.[^.]+$/.html/;
 | |
| 	}
 | |
| 	open(my $save_fh, '>:encoding(UTF-8)', $preview_file_name)
 | |
| 		or die "Could not open: $!\n";
 | |
| 	print {$save_fh} merge_contents($template, $new_content, $remote_url);
 | |
| 	close($save_fh)
 | |
| 		or die "Could not close: $!\n";
 | |
| 
 | |
| 	v_print("### Results\n");
 | |
| 	if ($autoload) {
 | |
| 		v_print("Launching browser w/ file: ${preview_file_name}");
 | |
| 		system('git', 'web--browse', $preview_file_name);
 | |
| 	} else {
 | |
| 		print {*STDERR} "Preview file saved as: ${preview_file_name}\n";
 | |
| 	}
 | |
| 
 | |
| 	exit;
 | |
| }
 | |
| 
 | |
| # uses global scope variable: $remote_name
 | |
| sub merge_contents {
 | |
| 	my $template = shift;
 | |
| 	my $content = shift;
 | |
| 	my $remote_url = shift;
 | |
| 	my ($content_tree, $html_tree, $mw_content_text);
 | |
| 	my $template_content_id = 'bodyContent';
 | |
| 
 | |
| 	$html_tree = HTML::TreeBuilder->new;
 | |
| 	$html_tree->parse($template);
 | |
| 
 | |
| 	$content_tree = HTML::TreeBuilder->new;
 | |
| 	$content_tree->parse($content);
 | |
| 
 | |
| 	$template_content_id = Git::config("remote.${remote_name}.mwIDcontent")
 | |
| 		|| $template_content_id;
 | |
| 	v_print("Using '${template_content_id}' as the content ID\n");
 | |
| 
 | |
| 	$mw_content_text = $html_tree->look_down('id', $template_content_id);
 | |
| 	if (!defined $mw_content_text) {
 | |
| 		print {*STDERR} <<"CONFIG";
 | |
| Could not combine the new content with the template. You might want to
 | |
| configure `mediawiki.IDContent` in your config:
 | |
| 	git config --add remote.${remote_name}.mwIDcontent <id>
 | |
| and re-run the command afterward.
 | |
| CONFIG
 | |
| 		exit 1;
 | |
| 	}
 | |
| 	$mw_content_text->delete_content();
 | |
| 	$mw_content_text->push_content($content_tree);
 | |
| 
 | |
| 	make_links_absolute($html_tree, $remote_url);
 | |
| 
 | |
| 	return $html_tree->as_HTML;
 | |
| }
 | |
| 
 | |
| sub make_links_absolute {
 | |
| 	my $html_tree = shift;
 | |
| 	my $remote_url = shift;
 | |
| 	for (@{ $html_tree->extract_links() }) {
 | |
| 		my ($link, $element, $attr) = @{ $_ };
 | |
| 		my $url = url($link)->canonical;
 | |
| 		if ($url !~ /#/) {
 | |
| 			$element->attr($attr, URI->new_abs($url, $remote_url));
 | |
| 		}
 | |
| 	}
 | |
| 	return $html_tree;
 | |
| }
 | |
| 
 | |
| sub is_valid_remote {
 | |
| 	my $remote = shift;
 | |
| 	my @remotes = git_cmd_try {
 | |
| 		Git::command('remote') }
 | |
| 		"%s failed w/ code %d";
 | |
| 	my $found_remote = 0;
 | |
| 	foreach my $remote (@remotes) {
 | |
| 		if ($remote eq $remote) {
 | |
| 			$found_remote = 1;
 | |
| 			last;
 | |
| 		}
 | |
| 	}
 | |
| 	return $found_remote;
 | |
| }
 | |
| 
 | |
| sub find_mediawiki_remotes {
 | |
| 	my @remotes = git_cmd_try {
 | |
| 		Git::command('remote'); }
 | |
| 		"%s failed w/ code %d";
 | |
| 	my $remote_url;
 | |
| 	my @valid_remotes = ();
 | |
| 	foreach my $remote (@remotes) {
 | |
| 		$remote_url = mediawiki_remote_url_maybe($remote);
 | |
| 		if ($remote_url) {
 | |
| 			push(@valid_remotes, $remote);
 | |
| 		}
 | |
| 	}
 | |
| 	return @valid_remotes;
 | |
| }
 | |
| 
 | |
| sub find_upstream_remote_name {
 | |
| 	my $current_branch = git_cmd_try {
 | |
| 		Git::command_oneline('symbolic-ref', '--short', 'HEAD') }
 | |
| 		"%s failed w/ code %d";
 | |
| 	return Git::config("branch.${current_branch}.remote");
 | |
| }
 | |
| 
 | |
| sub mediawiki_remote_url_maybe {
 | |
| 	my $remote = shift;
 | |
| 
 | |
| 	# Find remote url
 | |
| 	my $remote_url = Git::config("remote.${remote}.url");
 | |
| 	if ($remote_url =~ s/mediawiki::(.*)/$1/) {
 | |
| 		return url($remote_url)->canonical;
 | |
| 	}
 | |
| 
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| sub get_template {
 | |
| 	my $url = shift;
 | |
| 	my $page_name = shift;
 | |
| 	my ($req, $res, $code, $url_after);
 | |
| 
 | |
| 	$req = LWP::UserAgent->new;
 | |
| 	if ($verbose) {
 | |
| 		$req->show_progress(1);
 | |
| 	}
 | |
| 
 | |
| 	$res = $req->get("${url}/index.php?title=${page_name}");
 | |
| 	if (!$res->is_success) {
 | |
| 		$code = $res->code;
 | |
| 		$url_after = $res->request()->uri(); # resolve all redirections
 | |
| 		if ($code == HTTP_CODE_PAGE_NOT_FOUND) {
 | |
| 			if ($verbose) {
 | |
| 				print {*STDERR} <<"WARNING";
 | |
| Warning: Failed to retrieve '$page_name'. Create it on the mediawiki if you want
 | |
| all the links to work properly.
 | |
| Trying to use the mediawiki homepage as a fallback template ...
 | |
| WARNING
 | |
| 			}
 | |
| 
 | |
| 			# LWP automatically redirects GET request
 | |
| 			$res = $req->get("${url}/index.php");
 | |
| 			if (!$res->is_success) {
 | |
| 				$url_after = $res->request()->uri(); # resolve all redirections
 | |
| 				die "Failed to get homepage @ ${url_after} w/ code ${code}\n";
 | |
| 			}
 | |
| 		} else {
 | |
| 			die "Failed to get '${page_name}' @ ${url_after} w/ code ${code}\n";
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return $res->decoded_content;
 | |
| }
 | |
| 
 | |
| ############################## Help Functions ##################################
 | |
| 
 | |
| sub help {
 | |
| 	print {*STDOUT} <<'END';
 | |
| usage: git mw <command> <args>
 | |
| 
 | |
| git mw commands are:
 | |
|     help        Display help information about git mw
 | |
|     preview     Parse and render local file into HTML
 | |
| END
 | |
| 	exit;
 | |
| }
 |