diff options
Diffstat (limited to 'contrib/mw-to-git/git-mw.perl')
| -rwxr-xr-x | contrib/mw-to-git/git-mw.perl | 368 | 
1 files changed, 368 insertions, 0 deletions
diff --git a/contrib/mw-to-git/git-mw.perl b/contrib/mw-to-git/git-mw.perl new file mode 100755 index 0000000000..28df3ee321 --- /dev/null +++ b/contrib/mw-to-git/git-mw.perl @@ -0,0 +1,368 @@ +#!/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/moy/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; +}  | 
