# Copyright (C) 2008, 2012  Alex Schroeder <alex@gnu.org>
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.

use strict;
use v5.10;

AddModuleDescription('translation-links.pl', 'Translation Links');

=head1 Translation Links

This package allows Oddmuse to support translation links.

The prerequisite is that you set the C<%Languages> option.

Example:

     %Languages = ('de' => '\b(der|die|das|und|oder)\b',
                   'en' => '\b(the|he|she|that|this)\b');

This defines the I<known languages>: de and en, in our example.

The known languages should not contain any characters with a special
meaning in a regular expresion, nor should it contain a space or an
underscore.

=head2 %TranslationLinkTarget

This option maps languages to URLs. If this option is not set, all
links will point to the default URL of the wiki, C<$ScriptName>.

You can use this together with the namespace extension. Here's an
example:

    $TranslationLinkTarget{en} = "$ScriptName/En";
    $TranslationLinkTarget{de} = "$ScriptName/De";

Or you can use a setup using a different wrapper script per language:

    $TranslationLinkTarget{en} = "$ScriptName-en";
    $TranslationLinkTarget{de} = "$ScriptName-de";

=head2 $TranslationLinkHelpPage

When translating a page, people will be offered a link to a help page.
The default page is called TranslationHelp.

=cut

our ($q, %Action, %Page, $OpenPageName, %Translate, %Languages, $LinkPattern, $FreeLinks, $FreeLinkPattern, $WikiLinks, @MyRules, @MyInitVariables, $FS);
our ($TranslationLinkPattern, %TranslationLinkTarget,
	    $TranslationLinkHelpPage);

$TranslationLinkHelpPage = 'TranslationHelp';

=head2 $TranslationLinkPattern

This regular expression is formed via all the defined languages and
C<$FreeLinkPattern> if C<$FreeLinks> is set and/or C<$LinkPattern> if
C<$WikiLinks> is set. It matches things such as C<[[en:HomePage]]>.

=cut

push(@MyInitVariables, \&TranslationLinkInit);

my %TranslationLinkData;

sub TranslationLinkInit {
  $TranslationLinkPattern = '\[\[(' . join('|', keys %Languages) . '):(';
  $TranslationLinkPattern .= $FreeLinkPattern if $FreeLinks;
  $TranslationLinkPattern .= '|' if $FreeLinks or $WikiLinks;
  $TranslationLinkPattern .= $LinkPattern if $WikiLinks;
  $TranslationLinkPattern .= ')\]\]';
  %TranslationLinkData = ();
}

=head2 TranslationLinkRule

The translation links will not be rendered, that is text such as
C<[[en:HomePage]]> does not produce any HTML output directly. We will
collect the translation links as we go, however, and store them in our
C<%Page> which will be saved to disk if no HTML cache exists.

=cut

push(@MyRules, \&TranslationLinkRule);

sub TranslationLinkRule {
  if (m/\G$TranslationLinkPattern/cg) {
    $TranslationLinkData{$1} = $2;
    $Page{translations} = join($FS, %TranslationLinkData);
    return '';
  }
  return;
}

=head2 Footer Links

We hook into C<GetFooterLinks> and print a line of translations, as
well as a link to the translate action.

=cut

*TranslationLinkOldGetFooterLinks = \&GetFooterLinks;
*GetFooterLinks = \&TranslationLinkNewGetFooterLinks;

sub TranslationLinkNewGetFooterLinks {
  my $html = TranslationLinkOldGetFooterLinks(@_);
  my ($id, $rev) = @_;
  if ($id and not $rev) {
    OpenPage($id);
    my $bar;
    my %translations;
    if ($Page{translations}) {
      %translations = split(/$FS/, $Page{translations});
      $bar = join(' ', map {
	my $url;
	if ($TranslationLinkTarget{$_}) {
	  $url = $TranslationLinkTarget{$_};
	  $url =~ s/\%s/$translations{$_}/g or $url .= $translations{$_};
	} else {
	  $url = ScriptUrl($translations{$_});
	}
	$q->a({-href=>$url, -class=>"translation $_"}, T($_));
      } keys %translations);
    }
    my %missing;
    foreach (keys %Languages) {
      if (not $translations{$_}) {
	$missing{$_} = 1;
      }
    }
    # If the current page is autodetected to have exactly one
    # translation, then remove that language from the list of missing
    # languages.
    my @current = split(/,/, $Page{languages});
    if ($#current == 0) {
      delete $missing{$current[0]};
    }
    if (scalar keys %missing) {
      $bar .= ' ' . ScriptLink("action=translate;id=$id;missing="
			       . join('_', sort keys %missing),
			       T('Add Translation'), 'translation new');
    }
    $html = $q->span({-class=>'translation bar'}, $q->br(), $bar) . $html;
  }
  return $html;
}

=head2 Translate Action

The translate action knows what page the user is trying to translate,
and it knows what translations seem to be missing. By selecting the
appropriate checkbox, a translation link will be added to the source
page.

=cut

$Action{translate} = \&DoTranslationLink;

sub DoTranslationLink {
  my $source = shift;
  my $target = FreeToNormal(GetParam('target', ''));
  my $error = ValidId($target)
    || ($source eq $target and T("Please provide a different page name for the translation."));
  my $lang = GetParam('translation', '');
  if (not $error and $lang) {
    # make sure the translation target cannot contain spam if using banning-regexps.pl
    if (my $rule = BannedContent(NormalToFree($source)) || BannedContent(NormalToFree($target))) {
      ReportError(T('Edit Denied'), '403 FORBIDDEN', undef, $q->p(T('The page contains banned text.')),
		  $q->p(T('Contact the wiki administrator for more information.')), $q->p($rule));
    }
    OpenPage(FreeToNormal($source));
    Save($OpenPageName, "[[$lang:$target]]\n" . $Page{text},
	 Tss('Added translation: %1 (%2)',
	     NormalToFree($target), T($lang)), 1);
    DoEdit($target);
  } else {
    my @missing = split(/_/, GetParam('missing', ''));
    print GetHeader(undef, Ts('Translate %s', NormalToFree($source)));
    print $q->start_div({-class=>'content translate'}), GetFormStart();
    print $q->p(Ts('Thank you for writing a translation of %s.', $source),
	        T('Please indicate what language you will be using.'));
    if (defined $q->param('target') and not $lang) {
      print $q->div({-class=>'message'}, $q->p(T('Language is missing')));
    }
    print $q->p(T('Suggested languages:')),
      $q->p($q->radio_group(-name=>'translation',
			    -values=>\@missing,
			    -linebreak=>'true',
			    -labels=>\%Translate));
    print $q->p(Ts('Please indicate a page name for the translation of %s.',
		   $source),
	        Ts('More help may be available here: %s.',
		   GetPageLink($TranslationLinkHelpPage)));
    if (defined $q->param('target') and $error) {
      print $q->div({-class=>'message'}, $q->p($error));
    }
    print $q->p($q->label({-for=>'target'}, T('Translated page:')), ' ',
		$q->textfield('target', '', 40),
		# don't use $q->hidden or you'll get encoding errors
		$q->input({-type=>'hidden', -name=>'id',
			   -value=>$source}),
		$q->input({-type=>'hidden', -name=>'action',
			   -value=>'translate'}),
		$q->input({-type=>'hidden', -name=>'missing',
			   -value=>GetParam('missing', '')}),
		$q->submit('dotranslate', T('Go!')));
    print $q->end_form, $q->end_div();
    PrintFooter();
  }
}
