git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH V3 4/4] git-mw: Add preview subcommand into git mw.

From
Bbenoit.person@ensimag.fr <benoit.person@ensimag.fr>
Date
Jun 16, 2013, 02:31 UTC
Message-ID
<1371349893-7789-5-git-send-email-benoit.person@ensimag.fr>
In-Reply-To
<1371349893-7789-1-git-send-email-benoit.person@ensimag.fr>
From: Benoit Person <benoit.person@ensimag.fr>

Add the subcommand to 'git-mw.perl'. Add a new constant in GitMediawiki.pm 'HTTP_CODE_PAGE_NOT_FOUND'.

Signed-off-by: Benoit Person <benoit.person@ensimag.fr>
Signed-off-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>
---
changes from V2:
  - Remove the --blob option, distinction between files and blobs is now 
    automatic.
  - Add a --verbose option to output more information on what's going on.
  - Rewrote the doc and the commit message.
  - Rewrote of the template retrieving code (see 'get_template' sub).
  - Use a configuration variable to define the content ID search in the
    template. Default value set as 'bodyContent' since it seems more standard
    than 'mw-content-text'.
  - Final content is now saved as utf-8 to solve encoding issues.
  - Perlcritic changes:
    - All 'print's specify their output streams.
    --> Same useless warnings left in git-remote-mediawiki.perl after célestin's 
        work and git-mw.perl after this patch :) .
 contrib/mw-to-git/GitMediawiki.pm |   3 +-
 contrib/mw-to-git/git-mw.perl     | 303 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 304 insertions(+), 2 deletions(-)
diff --git a/contrib/mw-to-git/GitMediawiki.pm b/contrib/mw-to-git/GitMediawiki.pm
index beae6d0..d1f2c41 100644
--- a/contrib/mw-to-git/GitMediawiki.pm
+++ b/contrib/mw-to-git/GitMediawiki.pm
@@ -19,7 +19,7 @@ require Exporter;
 
 # Methods which can be called as standalone functions as well:
 @EXPORT_OK = qw(clean_filename smudge_filename connect_maybe
-				EMPTY HTTP_CODE_OK);
+				EMPTY HTTP_CODE_OK HTTP_CODE_PAGE_NOT_FOUND);
 }
 
 # Mediawiki filenames can contain forward slashes. This variable decides by which pattern they should be replaced
@@ -30,6 +30,7 @@ use constant EMPTY => q{};
 
 # HTTP codes
 use constant HTTP_CODE_OK => 200;
+use constant HTTP_CODE_PAGE_NOT_FOUND => 404;
 
 sub clean_filename {
 	my $filename = shift;
diff --git a/contrib/mw-to-git/git-mw.perl b/contrib/mw-to-git/git-mw.perl
index 320c00e..0b83108 100644
--- a/contrib/mw-to-git/git-mw.perl
+++ b/contrib/mw-to-git/git-mw.perl
@@ -12,10 +12,41 @@ use strict;
 use warnings;
 
 use Getopt::Long;
+use URI::URL qw(url);
+use LWP::UserAgent;
+use HTML::TreeBuilder;
+
+use Git;
+use MediaWiki::API;
+use GitMediawiki qw(smudge_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)';
+
+#preview parameters
+my $file_name = EMPTY;
+my $remote_name = EMPTY;
+my $preview_file_name = EMPTY;
+my $autoload = 0;
+my $verbose = 0;
+sub file {
+	$file_name = shift;
+	return $file_name;
+}
 
 my %commands = (
 	'help' =>
-		[\&help, {}, \&help]
+		[\&help, {}, \&help],
+	'preview' =>
+		[\&preview, {
+			'<>' => \&file,
+			'output|o=s' => \$preview_file_name,
+			'remote|r=s' => \$remote_name,
+			'autoload|a' => \$autoload,
+			'verbose|v'  => \$verbose
+		}, \&preview_help]
 );
 
 # Search for sub-command
@@ -33,6 +64,275 @@ GetOptions( %{$cmd->[1]},
 # Launch command
 &{$cmd->[0]};
 
+############################# Preview Functions ################################
+
+# @TODO : add documentation for verbose option
+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
+distant 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 ($content, $content_tree, $template, $html_tree, $mw_content_text);
+	my $file_content;
+	my $template_content_id = 'bodyContent';
+
+	# file_name argumeent is mandatory
+	if (!defined $file_name) {
+		die "File not set, see `git mw help` \n";
+	}
+
+	v_print("### SELECTING REMOTE ###\n");
+
+	if ($remote_name eq EMPTY) {
+		# Search current branch upstream branch remote
+		my $current_branch = git_cmd_try {
+			Git::command_oneline('symbolic-ref', '--short', 'HEAD') }
+			"%s failed w/ code %d";
+		$remote_name = Git::config("branch.${current_branch}.remote");
+
+		if ($remote_name) {
+			$remote_url = mediawiki_remote_url_maybe($remote_name);
+		}
+
+		# Search all possibles mediawiki remotes
+		if (! $remote_url) {
+			my @remotes = git_cmd_try {
+				Git::command('remote'); }
+				"%s failed w/ code %d";
+
+			my @valid_remotes = ();
+			foreach my $remote (@remotes) {
+				$remote_url = mediawiki_remote_url_maybe($remote);
+				if ($remote_url) {
+					push(@valid_remotes, $remote);
+				}
+			}
+
+			if ($#valid_remotes == 0) {
+				print {*STDERR} "Can not find any mediawiki remote in this repo. \n";
+				exit 1;
+			} else {
+				print {*STDERR} "There are multiple mediawiki remotes, which of:\n";
+				foreach my $remote (@remotes) {
+					print {*STDERR} "\t${remote}\n";
+				}
+				print {*STDERR} "do you want ? Use the -r option to specify the remote\n";
+			}
+
+			exit 0;
+		}
+	} else {
+		# Check remote name
+		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_name) {
+				$found_remote = 1;
+				last;
+			}
+		}
+		if (!$found_remote) {
+			die "${remote_name} is not a remote\n";
+		}
+
+		# Find remote url
+		$remote_url = mediawiki_remote_url_maybe($remote_name);
+		if (! $remote_url) {
+			die "the remote you selected is not a mediawiki remote\n";
+		}
+	}
+	v_print("selected remote:\n\tname: ${remote_name}\n\turl: ${remote_url}\n");
+
+	# Create and connect to the wiki if necessary
+	$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";
+	}
+
+	# Transform file_name into a mediawiki page name
+	$wiki_page_name = smudge_filename($file_name);
+	$wiki_page_name =~ s/\.[^.]+$//;
+
+	# Default preview_file_name is file_name with .html ext
+	if ($preview_file_name eq EMPTY) {
+		$preview_file_name = $file_name;
+		$preview_file_name =~ s/\.[^.]+$/.html/;
+	}
+
+	v_print("### Retrieving template\n");
+	$template = get_template($remote_url, $wiki_page_name);
+
+	v_print("### Parsing & merging contents\n");
+	# Parsing template page
+	$html_tree = HTML::TreeBuilder->new;
+	$html_tree->parse($template);
+
+	# Load new content
+	$content = $wiki->api({
+		action => 'parse',
+		text => $file_content,
+		title => $wiki_page_name
+	}, {
+		skip_encoding => 1
+	}) or die "No response from distant mediawiki\n";
+	$content = $content->{'parse'}->{'text'}->{'*'};
+	$content_tree = HTML::TreeBuilder->new;
+	$content_tree->parse($content);
+
+	# Replace old content with new one
+	$template_content_id = Git::config('mediawiki.IDContent')
+		|| $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 parsed content with the template. You might want to
+configure `mediawiki.IDContent` in your config:
+	git config --add mediawiki.IDContent <your_template_content_element_id>
+CONFIG
+	}
+	$mw_content_text->delete_content();
+	$mw_content_text->push_content($content_tree);
+
+	# Transform relative links into absolute ones
+	for (@{ $html_tree->extract_links() }) {
+		my ($link, $element, $attr) = @{ $_ };
+		my $url = url($link)->canonical;
+		$element->attr($attr, URI->new_abs($url, $remote_url));
+	}
+
+	# Save the preview file
+	open(my $save_fh, '>:encoding(UTF-8)', $preview_file_name)
+		or die "Could not open: $!\n";
+	print {$save_fh} $html_tree->as_HTML;
+	close($save_fh)
+		or die "Could not close: $!\n";
+
+	# Auto-loading in browser
+	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;
+}
+
+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;
+}
+
+sub v_print {
+	if ($verbose) {
+		print {*STDERR} @_;
+	}
+}
+
 ############################## Help Functions ##################################
 
 sub help {
@@ -41,6 +341,7 @@ 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;
 }
\ No newline at end of file
-- 
1.8.3.GIT
Previous: benoit.person@ensimag.frNext: Matthieu Moy
Message 11 of 13 in “git-remote-mediawiki: new tool to preview local changes without pushing”
  1. 0/4 git-remote-mediawiki: new tool to preview local changes without pushingbenoit.person@ensimag.fr, Jun 16, 2013
  2. 1/4 git-mw: Introduction of GitMediawiki.pmbenoit.person@ensimag.fr, Jun 16, 2013
  3. Matthieu MoyJun 16, 2013
  4. Benoît PersonJun 16, 2013
  5. Matthieu MoyJun 17, 2013
  6. Benoît PersonJun 18, 2013
  7. Matthieu MoyJun 18, 2013
  8. Matthieu MoyJun 18, 2013
  9. 2/4 git-mw: Move some functions from git-remote-mediawiki.perl to GitMediawiki.pmbenoit.person@ensimag.fr, Jun 16, 2013
  10. 3/4 git-mw: Adding git-mw commandbenoit.person@ensimag.fr, Jun 16, 2013
  11. 4/4 git-mw: Add preview subcommand into git mw.benoit.person@ensimag.fr, Jun 16, 2013
  12. Matthieu MoyJun 16, 2013
  13. Ramkumar RamachandraJun 16, 2013

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.