Skip to content

Commit

Permalink
Item14339: Implement rest/jsonrpc support for paramaterized URLs
Browse files Browse the repository at this point in the history
This adjusts the API documentation for Func::getScriptUrl and
Foswiki::getScriptUrl to claify that the Web and Topic positional
parameters are not always a web or topic name.

Add support for:
 rest    subject= verb=
 jsonrpc namespace= method=

Update documentation and unit tests.
  • Loading branch information
gac410 committed Mar 14, 2017
1 parent 2ba0daa commit 60ec492
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 66 deletions.
83 changes: 72 additions & 11 deletions UnitTestContrib/test/unit/Fn_SCRIPTURL.pm
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ sub new {
return $self;
}

sub test_SCRIPTURL {
sub test_SCRIPTURL_view {
my $this = shift;

$Foswiki::cfg{ScriptUrlPaths}{snarf} = "sausages";
Expand Down Expand Up @@ -45,20 +45,11 @@ sub test_SCRIPTURL {
"$Foswiki::cfg{ScriptUrlPath}/view.dot/Main/WebHome?web=System",
$result );

# SMELL: rest and jsonrpc cannot built the path automatically. Ignore all params.
$result =
$this->{test_topicObject}->expandMacros(
"%SCRIPTURLPATH{\"rest\" topic=\"Main.WebHome\" web=\"System\"}%");
$this->assert_str_equals(
"<div class='foswikiAlert'>Parameters are not supported when generating rest or jsonrpc URLs.</div>",
$result
);

$result =
$this->{test_topicObject}->expandMacros(
"%SCRIPTURLPATH{\"jsonrpc\" topic=\"Main.WebHome\" web=\"System\"}%");
$this->assert_str_equals(
"<div class='foswikiAlert'>Parameters are not supported when generating rest or jsonrpc URLs.</div>",
"<div class='foswikiAlert'>jsonrpc requires the 'namespace' parameter if other parameters are supplied.</div>",
$result
);

Expand Down Expand Up @@ -91,4 +82,74 @@ sub test_SCRIPTURL {
);
}

sub test_SCRIPTURL_rest {
my $this = shift;

$Foswiki::cfg{ScriptUrlPaths}{snarf} = "sausages";
undef $Foswiki::cfg{ScriptUrlPaths}{view};
$Foswiki::cfg{ScriptSuffix} = ".dot";

my $result =
$this->{test_topicObject}->expandMacros(
"%SCRIPTURLPATH{\"rest\" topic=\"Main.WebHome\" web=\"System\"}%");
$this->assert_str_equals(
"<div class='foswikiAlert'>rest requires both 'subject' and 'verb' parameters if other parameters are supplied.</div>",
$result
);

$result =
$this->{test_topicObject}->expandMacros(
"%SCRIPTURLPATH{\"rest\" subject=\"Weeble\" topic=\"Main.WebHome\" web=\"System\"}%"
);
$this->assert_str_equals(
"<div class='foswikiAlert'>rest requires both 'subject' and 'verb' parameters if other parameters are supplied.</div>",
$result
);

$result =
$this->{test_topicObject}->expandMacros(
"%SCRIPTURLPATH{\"rest\" subject=\"Weeble\" verb=\"wobble\" topic=\"Main.WebHome\" web=\"System\"}%"
);
$this->assert_str_equals(
"/bin/rest.dot/Weeble/wobble?topic=Main.WebHome;web=System", $result );
}

sub test_SCRIPTURL_jsonrpc {
my $this = shift;

$Foswiki::cfg{ScriptUrlPaths}{snarf} = "sausages";
undef $Foswiki::cfg{ScriptUrlPaths}{view};
$Foswiki::cfg{ScriptSuffix} = ".dot";

my $result =
$this->{test_topicObject}->expandMacros(
"%SCRIPTURLPATH{\"jsonrpc\" topic=\"Main.WebHome\" web=\"System\"}%");
$this->assert_str_equals(
"<div class='foswikiAlert'>jsonrpc requires the 'namespace' parameter if other parameters are supplied.</div>",
$result
);

$result =
$this->{test_topicObject}->expandMacros(
"%SCRIPTURLPATH{\"jsonrpc\" namespace=\"Weeble\" topic=\"Main.WebHome\" web=\"System\"}%"
);
$this->assert_str_equals(
"/bin/jsonrpc.dot/Weeble?topic=Main.WebHome;web=System", $result );

$result =
$this->{test_topicObject}->expandMacros(
"%SCRIPTURLPATH{\"jsonrpc\" namespace=\"Weeble\" method=\"wobble\" topic=\"Main.WebHome\" web=\"System\"}%"
);
$this->assert_str_equals(
"/bin/jsonrpc.dot/Weeble/wobble?topic=Main.WebHome;web=System",
$result );

$result =
$this->{test_topicObject}->expandMacros(
"%SCRIPTURLPATH{\"jsonrpc\" namespace=\"Weeble\" method=\"wobble\" topic=\"Main.WebHome\" web=\"System\"}%"
);
$this->assert_str_equals(
"/bin/jsonrpc.dot/Weeble/wobble?topic=Main.WebHome;web=System",
$result );
}
1;
15 changes: 12 additions & 3 deletions core/data/System/VarSCRIPTURL.txt
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
%META:TOPICINFO{author="ProjectContributor" date="1458616800" format="1.1" version="1"}%
%META:TOPICINFO{author="ProjectContributor" date="1489457305" format="1.1" version="1"}%
%META:TOPICPARENT{name="Macros"}%
---+ SCRIPTURL -- URL of script(s)
Expands to the URL of a script, or the base URL of all scripts
%STARTSECTION{"params"}%
---++ Parameters
| *Parameter* | *Description* | *Default* |
| ="$script"= | Name of script | |
| =web= | Web name to add to URL | |
| =topic= | Topic (or Web.Topic) to add to URL | |
| =#= | Anchor (URL Fragment) appended to the URL | |
| ==rest== script parameters |||
| =subject= | The plugin name registering the handler |
| =verb= | The rest handler name |
| ==jsonrpc== script parameters |||
| =namespace= | The namespace for the jsonrpc rquest |
| =method= | The Json method |
| Any other parameters to the macro will be added as parameters to the URL |||
%ENDSECTION{"params"}%
---++ Examples
* =%<nop>SCRIPTURL{"view" topic="Cartoons.EvilMonkey"}%= will expand to =%SCRIPTURL{"view" topic="Cartoons.EvilMonkey"}%=
* =%<nop>SCRIPTURL{"view" web="Cartoons"}%= will expand to =%SCRIPTURL{"view" web="Cartoons"}%=
* =%<nop>SCRIPTURL{"view" topic="Cartoons.EvilMonkey" rev="1"}%= will expand to =%SCRIPTURL{"view" topic="Cartoons.EvilMonkey" rev="1" }%=
* =%<nop>SCRIPTURL{"edit" web="Cartoons" topic="EvilMonkey" t="%<nop>GMTIME{"$epoch"}%"}%= expands to =%SCRIPTURL{"edit" web="Cartoons" topic="EvilMonkey" t="%GMTIME{"$epoch"}%"}%=
* =%<nop>SCRIPTURL%= expands to =%SCRIPTURL%=
* =%<nop>SCRIPTURL{script}%= expands to =%SCRIPTURL{script}%=
* =%<nop>SCRIPTURL{"rest" subject="CommentPlugin" verb="comment" topic="Web.Topic"}%= expands to =%SCRIPTURL{"rest" subject="CommentPlugin" verb="comment" topic="Web.Topic"}%=
* =%<nop>SCRIPTURL{"jsonrpc" namespace="Configure" }%= expands to =%SCRIPTURL{"jsonrpc" namespace="Configure" }%=
<div class="foswikiHelp">
%H% In most cases you should use [[VarSCRIPTURLPATH][SCRIPTURLPATH]] instead, as it works much better with URL rewriting

%H% The =edit= script should always be used in conjunction a =t="%<nop>GMTIME{"$epoch"}%"= parameter to ensure pages about to be edited are not cached in the browser

%X% The 'old' way of building URLs using =SCRIPTURL= involved concatenating the web and topic names to the =SCRIPTURL= e.g. =%<nop>SCRIPTURL{"script"}%/Cartoons/EvilMonkey=. This practice is *strongly* discouraged, as it does not correctly handle encoding of the parts of the URL. At the first opportunity you should replace all such URLs with the equivalent =%<nop>SCRIPTURL%{"script" topic="Cartoons.EvilMonkey"}%=, which will handle URL encoding for you.

%X% The SCRIPTURL macro does NOT support building =jsonrpc= or =rest= requests with parameters. They should still use the "contatenation" method. This is expected to be fixed in Foswiki 2.2.
</div>
%STOPINCLUDE%
---++ Related
Expand Down
14 changes: 5 additions & 9 deletions core/data/System/VarSCRIPTURLPATH.txt
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
%META:TOPICINFO{author="ProjectContributor" date="1458616800" format="1.1" version="1"}%
%META:TOPICINFO{author="ProjectContributor" date="1489457305" format="1.1" version="1"}%
%META:TOPICPARENT{name="Macros"}%
---+ SCRIPTURLPATH -- URL path of script(s)
Expands to the base URL of scripts, without protocol or host
---++ Parameters
| *Parameter* | *Description* | *Default* |
| ="$script"= | Name of script | |
| =web= | Web name to add to URL | |
| =topic= | Topic (or Web.Topic) to add to URL | |
| Any other parameters to the macro will be added as parameters to the URL |||
%INCLUDE{"VarSCRIPTURL" section="params"}%
---++ Examples
* =%<nop>SCRIPTURLPATH{"view" topic="Cartoons.EvilMonkey"}%= expands to =%SCRIPTURLPATH{"view" topic="Cartoons.EvilMonkey"}%=
* =%<nop>SCRIPTURLPATH{"view" web="Cartoons"}%= expands to =%SCRIPTURLPATH{"view" web="Cartoons"}%=
* =%<nop>SCRIPTURLPATH{"view" topic="Cartoons.EvilMonkey" rev="1"}%= will expand to =%SCRIPTURLPATH{"view" topic="Cartoons.EvilMonkey" rev="1" }%=
* =%<nop>SCRIPTURLPATH{"edit" web="Cartoons" topic="EvilMonkey" t="%<nop>GMTIME{"$epoch"}%"}%= expands to =%SCRIPTURLPATH{"edit" web="Cartoons" topic="EvilMonkey" t="%GMTIME{"$epoch"}%"}%=
* =%<nop>SCRIPTURLPATH%= expands to =%SCRIPTURLPATH%=
* =%<nop>SCRIPTURLPATH{script}%= expands to =%SCRIPTURLPATH{script}%=
* =%<nop>SCRIPTURLPATH{"rest" subject="CommentPlugin" verb="comment" topic="Web.Topic"}%= expands to =%SCRIPTURLPATH{"rest" subject="CommentPlugin" verb="comment" topic="Web.Topic"}%=
* =%<nop>SCRIPTURLPATH{"jsonrpc" namespace="Configure" }%= expands to =%SCRIPTURLPATH{"jsonrpc" namespace="Configure" }%=
<div class="foswikiHelp">
%H% The =edit= script should always be used in conjunction with a =t="%<nop>GMTIME{"$epoch"}%"= parameter to ensure pages about to be edited are not cached in the browser

%H% See =[[VarSCRIPTURL][SCRIPTURL]]= if you expect to need the protocol and host e.g. if you are saving the HTML of the page and using it on a different host.

%X% The 'old' way of building URLs using =SCRIPTURLPATH= involved concatenating the web and topic names to the =SCRIPTURLPATH= e.g. =%<nop>SCRIPTURLPATH{"script"}%/Cartoons/EvilMonkey=. This practice is *strongly* discouraged, as it does not correctly handle encoding of the parts of the URL. At the first opportunity you should replace such URLs with the equivalent %<nop>SCRIPTURLPATH%{"script" topic="Cartoons.EvilMonkey"}%, which will handle URL encoding for you.

%X% The SCRIPTURL macro does NOT support building =jsonrpc= or =rest= requests with parameters. They should still use the "contatenation" method. This is expected to be fixed in Foswiki 2.2.</div>
</div>
%STOPINCLUDE%
---++ Related
[[VarPUBURLPATH][PUBURLPATH]], [[VarSCRIPTNAME][SCRIPTNAME]], [[VarSCRIPTSUFFIX][SCRIPTSUFFIX]], [[VarSCRIPTURL][SCRIPTURL]]
35 changes: 25 additions & 10 deletions core/lib/Foswiki.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1515,14 +1515,16 @@ sub getSkin {

=begin TML
---++ ObjectMethod getScriptUrl( $absolute, $script, $web, $topic, ... ) -> $scriptURL
---++ ObjectMethod getScriptUrl( $absolute, $script, $path1, $path2, ... ) -> $scriptURL
Returns the URL to a Foswiki script, providing the web and topic as
"path info" parameters. The result looks something like this:
"http://host/foswiki/bin/$script/$web/$topic".
* =...= - an arbitrary number of name,value parameter pairs that will
* ="http://host/foswiki/bin/$script/$path1/$path2".=
=...= is an arbitrary number of name,value parameter pairs that will
be url-encoded and added to the url. The special parameter name '#' is
reserved for specifying an anchor. e.g.
=getScriptUrl('x','y','view','#'=>'XXX',a=>1,b=>2)= will give
=.../view/x/y?a=1&b=2#XXX=
Expand All @@ -1538,6 +1540,10 @@ are relative, then they will be converted to absolute when required (e.g.
when running from the command line, or when generating rss). If
$script is not given, absolute URLs will always be generated.
$path1 and $path2 are interpreted based upon the script name:
* =rest=, $path1 is the subject, and $path2 is the verb
* =jsonrpc=, $path1 is the Namespace and $path2 (optional) is the method.
For all other scripts, $path1 is the Web, and Path2 is the Topic.
If either the web or the topic is defined, will generate a full url (including web and topic). Otherwise will generate only up to the script name. An undefined web will default to the main web name.
As required by RFC3986, the returned URL will only contain the
Expand All @@ -1546,7 +1552,7 @@ allowed characters -A-Za-z0-9_.~!*\'();:@&=+$,/?%#[]
=cut

sub getScriptUrl {
my ( $this, $absolute, $script, $web, $topic, @params ) = @_;
my ( $this, $absolute, $script, $path1, $path2, @params ) = @_;

$absolute ||=
( $this->inContext('command_line') || $this->inContext('absolute_urls') );
Expand Down Expand Up @@ -1579,14 +1585,23 @@ sub getScriptUrl {
$url = $this->{urlHost} . $url;
}

if ($topic) {
( $web, $topic ) = $this->normalizeWebTopicName( $web, $topic );
if ( defined $script && ( $script eq 'rest' || $script eq 'jsonrpc' ) ) {
if ( defined $path1 ) {
$url .= urlEncode( '/' . $path1 );
$url .= urlEncode( '/' . $path2 ) if defined $path2;
}
}
else {
if ($path2) {
my ( $web, $topic ) =
$this->normalizeWebTopicName( $path1, $path2 );

$url .= urlEncode( '/' . $web . '/' . $topic );
$url .= urlEncode( '/' . $web . '/' . $topic );

}
elsif ($web) {
$url .= urlEncode( '/' . $web );
}
elsif ($path1) {
$url .= urlEncode( '/' . $path1 );
}
}
$url .= make_params(@params);

Expand Down
58 changes: 42 additions & 16 deletions core/lib/Foswiki/Func.pm
Original file line number Diff line number Diff line change
Expand Up @@ -151,67 +151,93 @@ sub getUrlHost {

=begin TML
---+++ getScriptUrl( $web, $topic, $script, ... ) -> $url
---+++ getScriptUrl( $path1, $path2, $script, ... ) -> $url
Compose fully qualified URL
* =$web= - Web name, e.g. ='Main'=
* =$topic= - Topic name, e.g. ='WebNotify'=
Compose fully qualified URL consisting of =script/path1/path2=
For most scripts, $path1 is the Web name and $path2 is the Topic name
* =$path1= - Web name, e.g. ='Main'=
* =$path2= - Topic name, e.g. ='WebNotify'=
* =$script= - Script name, e.g. ='view'=
* =...= - an arbitrary number of name=>value parameter pairs that will be url-encoded and added to the url. The special parameter name '#' is reserved for specifying an anchor. e.g. <tt>getScriptUrl('x','y','view','#'=>'XXX',a=>1,b=>2)</tt> will give <tt>.../view/x/y?a=1&b=2#XXX</tt>
* =...= - an arbitrary number of name=>value parameter pairs that will be url-encoded and added to the url. The special parameter name '#' is reserved for specifying an anchor.
* e.g. <tt>getScriptUrl('x','y','view','#'=>'XXX',a=>1,b=>2)</tt> will give <tt>.../view/x/y?a=1&b=2#XXX</tt>
* For the =rest= script, $path1 is the Subject and $path2 is the verb.
* For the =jsonrpc= script, $path1 is the Namespace, and $path2 (optional) is the method.
Return: =$url= URL, e.g. ="http://example.com:80/cgi-bin/view.pl/Main/WebNotify"=
*Examples:*
<verbatim class="perl">
my $url;
# $url eq 'http://wiki.example.org/url/to/bin'
$url = Foswiki::Func::getScriptUrl();
# $url eq 'http://wiki.example.org/url/to/bin/edit'
$url = Foswiki::Func::getScriptUrl(undef, undef, 'edit');
# $url eq 'http://wiki.example.org/url/to/bin/edit/Web/Topic'
$url = Foswiki::Func::getScriptUrl('Web', 'Topic', 'edit');</verbatim>
$url = Foswiki::Func::getScriptUrl('Web', 'Topic', 'edit');
# $url eq 'http://wiki.example.org/url/to/bin/rest/CommentPlugn/comment?topic=Sandbox.SampleTopic
$url = Foswiki::Func::getScriptUrl('CommentPlugin', 'comment', 'rest', 'topic'=>'Sandbox.SampleTopic');
# $url eq 'http://wiki.example.org/url/to/bin/jsonrpc/Configure
$url = Foswiki::Func::getScriptUrl('Configure', undef, 'jsonrpc');
</verbatim>
=cut

sub getScriptUrl {
my $web = shift;
my $topic = shift;
my $path1 = shift;
my $path2 = shift;
my $script = shift;
ASSERT($Foswiki::Plugins::SESSION) if DEBUG;

return $Foswiki::Plugins::SESSION->getScriptUrl( 1, $script, $web, $topic,
@_ );
return $Foswiki::Plugins::SESSION->getScriptUrl( 1, $script, $path1,
$path2, @_ );
}

=begin TML
---+++ getScriptUrlPath( $web, $topic, $script, ... ) -> $path
---+++ getScriptUrlPath( $path1, $path2, $script, ... ) -> $path
Compose absolute URL path. See Foswiki::Func::getScriptUrl
*Examples:*
<verbatim class="perl">
my $path;
# $path eq '/path/to/bin'
$path = Foswiki::Func::getScriptUrlPath();
# $path eq '/path/to/bin/edit'
$path = Foswiki::Func::getScriptUrlPath(undef, undef, 'edit');
# $path eq '/path/to/bin/edit/Web/Topic'
$path = Foswiki::Func::getScriptUrlPath('Web', 'Topic', 'edit');</verbatim>
$path = Foswiki::Func::getScriptUrlPath('Web', 'Topic', 'edit');
# $path eq '/path/to/bin/rest/CommentPlugn/comment?topic=Sandbox.SampleTopic
$path = Foswiki::Func::getScriptUrlPath('CommentPlugin', 'comment', 'rest', 'topic'=>'Sandbox.SampleTopic');
# $path eq '/path/to/bin/jsonrpc/Configure
$path = Foswiki::Func::getScriptUrlPath('Configure', undef, 'jsonrpc');
</verbatim>
*Since:* 19 Jan 2012 (when called without parameters, this function is
backwards-compatible with the old version which was deprecated 28 Nov 2008).
=cut

sub getScriptUrlPath {
my $web = shift;
my $topic = shift;
my $path1 = shift;
my $path2 = shift;
my $script = shift;
ASSERT($Foswiki::Plugins::SESSION) if DEBUG;

return $Foswiki::Plugins::SESSION->getScriptUrl( 0, $script, $web, $topic,
@_ );
return $Foswiki::Plugins::SESSION->getScriptUrl( 0, $script, $path1,
$path2, @_ );
}

=begin TML
Expand Down
Loading

0 comments on commit 60ec492

Please sign in to comment.