Skip to content

Commit

Permalink
Item13590: add completePageHander
Browse files Browse the repository at this point in the history
And prepare for release.
  • Loading branch information
gac410 committed Aug 2, 2015
1 parent fa5a4dc commit c8b72f0
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 29 deletions.
51 changes: 34 additions & 17 deletions data/System/ExternalLinkPlugin.txt
Expand Up @@ -4,7 +4,7 @@

This will only work for forced links marked up using the so-called double bracket syntax, like:
<verbatim>
[[http://external.site][an external site]]
[[http://external.site][an external site]] and [[http://external.site]]
</verbatim>

URLs that are just written in as http://external.site are left untouched.
Expand All @@ -14,20 +14,19 @@ URLs that are just written in as http://external.site are left untouched.
---++ Syntax Rules
Remember to use the double bracket link syntax.


---++ Plugin Settings
Change default settings in [[%SCRIPTURL{"configure"}%/#ExternalLinkPlugin$Extensions][configure]].
<!--
* Set SHORTDESCRIPTION = %$SHORTDESCRIPTION%
-->
%TABLE{databg="#ffffff"}%
%TABLE{databg="#ffffff" sort="off"}%
| *Configure setting* | *Meaning* | *Default* |
| ={<nop>MarkerImage}= | link marker image; see below for a list of images | %PUBURL%/%SYSTEMWEB%/ExternalLinkPlugin/external.gif |
| ={<nop>CssOnly}= | If set to =1=, does not add an image but writes CSS to the page; use if your users have modern browsers | =0= |
| ={CheckCompletePage}= | If set to =1=, disables the commonTagsHandler and uses a completePageHandler to check and stylize _all_ external links on the page. | =0= |
| ={Debug}= | If set to =1=, writes debugging info to =data/debug.txt= | =0= |

---+++ Marker images
%TABLE{databg="#ffffff"}%
%TABLE{databg="#ffffff" sort="off"}%
| *Code text* | *Image* |
| =%<nop>PUBURLPATH%/%<nop>SYSTEMWEB%/ExternalLinkPlugin/external.gif= | %PUBURL%/%SYSTEMWEB%/ExternalLinkPlugin/external.gif |
| =%<nop>PUBURLPATH%/%<nop>SYSTEMWEB%/ExternalLinkPlugin/diagonal.gif= | %PUBURL%/%SYSTEMWEB%/ExternalLinkPlugin/diagonal.gif |
Expand All @@ -38,6 +37,17 @@ This plugin adds one CSS class:
%TABLE{databg="#ffffff"}%
| =.externalLink= | link class to mark outgoing links |

---++ IDN Considerations

International Domain Names allow use of the UNICODE character set in domain
names. This enables a form of spoofing, where an alternate domain name is
crafted with characters that appear like your local domain name, but lead to
an external site.

Enabling the ={CheckCompletePage}= setting will check the generated HTML for
all links on the page, and will stylize any external link on the page with the external link
marker.

---++ Plugin Installation Instructions
%$INSTALL_INSTRUCTIONS%

Expand All @@ -47,31 +57,38 @@ To test if the plugin is correctly installed, check the following links.

The links in the "If plugin enabled"-column should be marked with a marker if the plugin is functioning correctly.

%TABLE{databg="#ffffff"}%
| *Code text* | *If plugin enabled* |
%TABLE{databg="#ffffff" sort="off"}%
| *Code text* | *If plugin enabled* |
| =[<nop>[http://foswiki.org][foswiki.org]]= | [[http://foswiki.org][foswiki.org]] |
| =[<nop>[http://foswiki.org]]= | [[http://foswiki.org]] |

Unsurprisingly, local links won't be marked as being external. The following links should not be marked:

%TABLE{databg="#ffffff"}%
| *Code text* | *If plugin enabled* |
%TABLE{databg="#ffffff" sort="off"}%
| *Code text* | *If plugin enabled* |
| =[<nop>[%<nop>ATTACHURL%/bli.png][link to (nonexistent) local attachment]]= | [[%ATTACHURL%/bli.png][link to (nonexistent) local attachment]] |

If you have enabled the ={CheckCompletePage}= option in the configuration, then the following html links will also be checked:
%TABLE{databg="#ffffff" sort="off"}%
| *Code text* | *If ={CheckCompletelPage}= enabled* |
| =&lt;a href='http://foswiki.org'>foswiki.org&lt;/a>= | <a href='http://foswiki.org'>foswiki.org</a> |

But local links will not be marked external:
%TABLE{databg="#ffffff" sort="off"}%
| *Code text* | *If ={CheckCompletelPage}= enabled* |
| =&lt;a href='%PUBURL%/System/ProjectLogos/foswiki-badge.png'>logo&lt;/a>= | <a href='%PUBURL%/System/ProjectLogos/foswiki-badge.png'>logo</a> |

---++ Plugin Info

| Authors: | TWiki:Main.AurelioAHeckert, TWiki:Main.NelsonFerraz, TWiki:Main.AntonioTerceiro, Foswiki:Main.ArthurClemens |
| Copyright: | &copy; 2005: Aurelio A Heckert, Nelson Ferraz, Antonio Terceiro; 2006: Steffen Poulsen; 2009: Foswiki:Main.ArthurClemens |
| Copyright: | &copy; 2005: Aurelio A Heckert, Nelson Ferraz, Antonio Terceiro; 2006: Steffen Poulsen; 2009: Foswiki:Main.ArthurClemens, 2015: Foswiki Contributors |
| License: | [[http://www.gnu.org/licenses/gpl.html][GPL (Gnu General Public License)]] |
| Version: | %$VERSION% |
| Release: | %$RELEASE% |
| Change History: | <!-- newest on top -->&nbsp; |
| 01 Aug 2015 | v1.30 - Foswikitask:Item8446: Don't nest externalLink spans<br />\
Foswikitask:Item8563: Recognize short URLs as internal links.<br />\
Foswikitask:Item13590: Update for Foswiki 2.0, test with utf-8 international links. Check [<nop[...]] forced links to detect idn based spoofing attacks. Add optional =completePageHandler= to process all html links. |
| 31 Dec 2009 | v1.21 - fixed closing css declaration. |
| 02 Oct 2009 | v1.20 - Ported to Foswiki; changed =img= HTML tag to CSS style; moved plugin settings to Configure. |
| 24 Apr 2006 | v1.11 - =%<nop>TOPIC% removed from =MARK= definition, wasn't expanded correctly. |
| 23 Apr 2006 | v1.10 - Incorporated patch from TWiki:Main.TobiasRoeser (only mark external link once in an included scenario). Minor doc brush-up. Changed default marker to =external.gif= graphic. (TWiki:Main.SteffenPoulsen). |
| 02 Nov 2005 | v1.00 - Initial release |
| CPAN Dependencies: | none |
| Other Dependencies: | none |
| Perl Version: | 5.005 |
| Plugin Home: | http://foswiki.org/Extensions/%TOPIC% |
| Support: | http://foswiki.org/Support/%TOPIC% |
58 changes: 50 additions & 8 deletions lib/Foswiki/Plugins/ExternalLinkPlugin.pm
Expand Up @@ -18,6 +18,9 @@

package Foswiki::Plugins::ExternalLinkPlugin;

use strict;
use warnings;

our $VERSION = '1.30';
our $RELEASE = '1.30';
our $NO_PREFS_IN_TOPIC = 1;
Expand All @@ -37,13 +40,23 @@ sub initPlugin {
return 0;
}


# Choose one. Either the commonTagsHandler or the completePageHandler is needed
# not both.
if ( $Foswiki::cfg{Plugins}{ExternalLinkPlugin}{CheckCompletePage} ) {
undef *commonTagsHandler;
}
else {
undef *completePageHandler;
}

$protocolsPattern =
Foswiki::Func::getRegularExpression('linkProtocolPattern');
$addedToHead = 0;
addStyleToHead();

Foswiki::Func::writeDebug(
"- Foswiki::Plugins::${pluginName}::initPlugin( $web.$topic ) is OK")
"- Foswiki::Plugins::ExternalLinkPlugin::initPlugin( $web.$topic ) is OK")
if $Foswiki::cfg{Plugins}{ExternalLinkPlugin}{Debug};
return 1;
}
Expand All @@ -62,7 +75,16 @@ sub addStyleToHead {

$header =~
s/%markerimage%/$Foswiki::cfg{Plugins}{ExternalLinkPlugin}{MarkerImage}/g;
Foswiki::Func::addToHEAD( $pluginName, $header );

if ( $Foswiki::Plugins::VERSION >= 2.1 ) {
# Foswiki 1.1.0 and later.
Foswiki::Func::addToZone( 'head', 'EXTERNALLINKPLUGIN_CSS', $header );
}
else {
# compatibility with Foswiki 1.0
Foswiki::Func::addToHEAD( $pluginName, $header );
}


$addedToHead = 1;
}
Expand All @@ -79,10 +101,30 @@ sub commonTagsHandler {

#print STDERR "===COMMONTAGS CALLED===\n($_[0])\n\n";

#Scan for [[protocol://links][With text]]
#Scan for [[protocol://links][With text]] and [[protocol://links]] without text
# - zero length look behind negative assertion - not already part of an external link span
$_[0] =~
s#(?<!<span class='externalLink'>)(\[\[($protocolsPattern://[^]]+?)\]\[[^]]+?\]\]([&]nbsp;)?)# handleExternalLink($1, $2) #ge;
s#(?<!<span class='externalLink'>)(\[\[($protocolsPattern://[^]]+?)\](?:\[[^]]+?\])?\]([&]nbsp;)?)# handleExternalLink($1, $2) #ge;

}

# The completePageHandler is done this way so that the unit tests
# can run the handler directly. The init routine will undef this handler.
# but the private method will remain available.
sub completePageHandler {
goto &_completePageHandler;
}

sub _completePageHandler {
### my ( $html, $headers ) = @_; # do not uncomment, use $_[0], $_[1]... instead

Foswiki::Func::writeDebug(
"- ${pluginName}::completePageHandler ")
if $Foswiki::cfg{Plugins}{ExternalLinkPlugin}{Debug};

#Scan for raw html links: <a..></a>
# - zero length look behind negative assertion - not already part of an external link span
$_[0] =~ s#(?<!<span class='externalLink'>)(<a .*?href=(["'])($protocolsPattern://.*?)\2.*?>.*?</a>)# handleExternalLink($1, $3) #ge;
}

sub handleExternalLink {
Expand All @@ -91,13 +133,13 @@ sub handleExternalLink {
my $scriptUrl = "$Foswiki::cfg{DefaultUrlHost}$Foswiki::cfg{ScriptUrlPath}";
my $pubUrl = "$Foswiki::cfg{DefaultUrlHost}$Foswiki::cfg{PubUrlPath}";

Foswiki::Func::writeDebug(
"Comparing SCRIPTURL ($scriptUrl)\n pubUrl ($pubUrl)\n with ($wholeLink)")
if $Foswiki::cfg{Plugins}{ExternalLinkPlugin}{Debug};
#Foswiki::Func::writeDebug(
# "Comparing SCRIPTURL ($scriptUrl)\n pubUrl ($pubUrl)\n defaultUrl ($Foswiki::cfg{DefaultUrlHost})\nwith $url ($wholeLink)" )
# if $Foswiki::cfg{Plugins}{ExternalLinkPlugin}{Debug};

if ( ( $url =~ /^$scriptUrl/ )
|| ( $url =~ /^$pubUrl/ )
|| ( $url =~ /^$Foswiki::cfg{DefaultUrlHost}\// )
|| ( $url =~ /^$Foswiki::cfg{DefaultUrlHost}(?:\/|$)/ )
|| ( $wholeLink =~ /[&]nbsp;$/ ) )
{
return $wholeLink;
Expand Down
5 changes: 5 additions & 0 deletions lib/Foswiki/Plugins/ExternalLinkPlugin/Config.spec
Expand Up @@ -4,5 +4,10 @@
# External link image.
$Foswiki::cfg{Plugins}{ExternalLinkPlugin}{MarkerImage} = '%PUBURLPATH%/%SYSTEMWEB%/ExternalLinkPlugin/external.gif';
# **BOOLEAN**
# Enable html link checking for the complete page. Setting this disables the
# commonTagsHandler and enables a completePageHandler. This may be useful to detect external
# links that are crafted with international characters to appear like internal links.
$Foswiki::cfg{Plugins}{ExternalLinkPlugin}{CheckCompletePage} = 0;
# **BOOLEAN**
# Enable debugging (debug messages will be written to data/debug.txt)
$Foswiki::cfg{Plugins}{ExternalLinkPlugin}{Debug} = 0;
39 changes: 35 additions & 4 deletions test/unit/ExternalLinkPlugin/ExternalLinkPluginTests.pm
Expand Up @@ -29,8 +29,11 @@ sub loadExtraConfig {
$Foswiki::cfg{Plugins}{ExternalLinkPlugin}{Module} =
'Foswiki::Plugins::ExternalLinkPlugin';
$Foswiki::cfg{Plugins}{ExternalLinkPlugin}{Enabled} = 1;
$Foswiki::cfg{Plugins}{ExternalLinkPlugin}{CheckCompletePage} = 0;

$Foswiki::cfg{DefaultUrlHost}='http://foobar.xxx';

# Setup for full URLs, test later overrides to short URLs
$Foswiki::cfg{ScriptUrlPath}='/foswiki/bin';
$Foswiki::cfg{ScriptUrlPaths}{view} = '/foswiki/bin/view';
}
Expand Down Expand Up @@ -65,9 +68,10 @@ sub test_renderExternalLinks {
)
);

# Simple external link, no text (Does not get external mark)
# Simple external link, no text
# Now check for external links - detect idn collisions
$this->assert_html_equals(
"<a href='http://google.com/'>http://google.com/</a>",
"<span class='externalLink'><a href='http://google.com/'>http://google.com/</a></span>",
Foswiki::Func::renderText(
Foswiki::Func::expandCommonVariables(
" [[http://google.com/]]", 'Sandbox'
Expand Down Expand Up @@ -136,8 +140,6 @@ sub test_renderI18nLinks {
my ($this) = @_;

$Foswiki::cfg{DefaultUrlHost}='http://εμπορικ.xxx';
$Foswiki::cfg{ScriptUrlPath}='/foswiki/bin';
$Foswiki::cfg{ScriptUrlPaths}{view} = '/foswiki/bin/view';
$this->createNewFoswikiSession();

my $url = $Foswiki::cfg{DefaultUrlHost};
Expand All @@ -163,6 +165,35 @@ sub test_renderI18nLinks {
)
);

}

sub test_completePageHandler {
my $this = shift;

my $url = $Foswiki::cfg{DefaultUrlHost};
my $completePage = <<"DONE";
<a href='http://google.com'>google</a>
<a href='https://google.com'>ssl google</a>
<a href='ftp://google.com'>ftp google</a>
<a href='$url/Main/WebHome'>internal short URL</a>
<a href='$url'>home no trailing slash</a>
<a href='$url/'>home trailing slash</a>
DONE

my $expectedPage = <<"DONE";
<span class='externalLink'><a href='http://google.com'>google</a></span>
<span class='externalLink'><a href='https://google.com'>ssl google</a></span>
<span class='externalLink'><a href='ftp://google.com'>ftp google</a></span>
<a href='$url/Main/WebHome'>internal short URL</a>
<a href='$url'>home no trailing slash</a>
<a href='$url/'>home trailing slash</a>
DONE

Foswiki::Plugins::ExternalLinkPlugin::_completePageHandler( $completePage );

$this->assert_html_equals( $expectedPage, $completePage );

}

Expand Down

0 comments on commit c8b72f0

Please sign in to comment.