diff --git a/UnitTestContrib/test/unit/Fn_SCRIPTURL.pm b/UnitTestContrib/test/unit/Fn_SCRIPTURL.pm index d07c5c6d39..b0af23deba 100644 --- a/UnitTestContrib/test/unit/Fn_SCRIPTURL.pm +++ b/UnitTestContrib/test/unit/Fn_SCRIPTURL.pm @@ -15,7 +15,7 @@ sub new { return $self; } -sub test_SCRIPTURL { +sub test_SCRIPTURL_view { my $this = shift; $Foswiki::cfg{ScriptUrlPaths}{snarf} = "sausages"; @@ -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( -"
Parameters are not supported when generating rest or jsonrpc URLs.
", - $result - ); - $result = $this->{test_topicObject}->expandMacros( "%SCRIPTURLPATH{\"jsonrpc\" topic=\"Main.WebHome\" web=\"System\"}%"); $this->assert_str_equals( -"
Parameters are not supported when generating rest or jsonrpc URLs.
", +"
jsonrpc requires the 'namespace' parameter if other parameters are supplied.
", $result ); @@ -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( +"
rest requires both 'subject' and 'verb' parameters if other parameters are supplied.
", + $result + ); + + $result = + $this->{test_topicObject}->expandMacros( +"%SCRIPTURLPATH{\"rest\" subject=\"Weeble\" topic=\"Main.WebHome\" web=\"System\"}%" + ); + $this->assert_str_equals( +"
rest requires both 'subject' and 'verb' parameters if other parameters are supplied.
", + $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( +"
jsonrpc requires the 'namespace' parameter if other parameters are supplied.
", + $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; diff --git a/core/data/System/VarSCRIPTURL.txt b/core/data/System/VarSCRIPTURL.txt index a15445c082..6fe43953b4 100644 --- a/core/data/System/VarSCRIPTURL.txt +++ b/core/data/System/VarSCRIPTURL.txt @@ -1,13 +1,22 @@ -%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 * =%SCRIPTURL{"view" topic="Cartoons.EvilMonkey"}%= will expand to =%SCRIPTURL{"view" topic="Cartoons.EvilMonkey"}%= * =%SCRIPTURL{"view" web="Cartoons"}%= will expand to =%SCRIPTURL{"view" web="Cartoons"}%= @@ -15,14 +24,14 @@ Expands to the URL of a script, or the base URL of all scripts * =%SCRIPTURL{"edit" web="Cartoons" topic="EvilMonkey" t="%GMTIME{"$epoch"}%"}%= expands to =%SCRIPTURL{"edit" web="Cartoons" topic="EvilMonkey" t="%GMTIME{"$epoch"}%"}%= * =%SCRIPTURL%= expands to =%SCRIPTURL%= * =%SCRIPTURL{script}%= expands to =%SCRIPTURL{script}%= + * =%SCRIPTURL{"rest" subject="CommentPlugin" verb="comment" topic="Web.Topic"}%= expands to =%SCRIPTURL{"rest" subject="CommentPlugin" verb="comment" topic="Web.Topic"}%= + * =%SCRIPTURL{"jsonrpc" namespace="Configure" }%= expands to =%SCRIPTURL{"jsonrpc" namespace="Configure" }%=
%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="%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. =%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 =%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.
%STOPINCLUDE% ---++ Related diff --git a/core/data/System/VarSCRIPTURLPATH.txt b/core/data/System/VarSCRIPTURLPATH.txt index 85c252fb01..0dadb3550f 100644 --- a/core/data/System/VarSCRIPTURLPATH.txt +++ b/core/data/System/VarSCRIPTURLPATH.txt @@ -1,13 +1,8 @@ -%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 * =%SCRIPTURLPATH{"view" topic="Cartoons.EvilMonkey"}%= expands to =%SCRIPTURLPATH{"view" topic="Cartoons.EvilMonkey"}%= * =%SCRIPTURLPATH{"view" web="Cartoons"}%= expands to =%SCRIPTURLPATH{"view" web="Cartoons"}%= @@ -15,14 +10,15 @@ Expands to the base URL of scripts, without protocol or host * =%SCRIPTURLPATH{"edit" web="Cartoons" topic="EvilMonkey" t="%GMTIME{"$epoch"}%"}%= expands to =%SCRIPTURLPATH{"edit" web="Cartoons" topic="EvilMonkey" t="%GMTIME{"$epoch"}%"}%= * =%SCRIPTURLPATH%= expands to =%SCRIPTURLPATH%= * =%SCRIPTURLPATH{script}%= expands to =%SCRIPTURLPATH{script}%= + * =%SCRIPTURLPATH{"rest" subject="CommentPlugin" verb="comment" topic="Web.Topic"}%= expands to =%SCRIPTURLPATH{"rest" subject="CommentPlugin" verb="comment" topic="Web.Topic"}%= + * =%SCRIPTURLPATH{"jsonrpc" namespace="Configure" }%= expands to =%SCRIPTURLPATH{"jsonrpc" namespace="Configure" }%=
%H% The =edit= script should always be used in conjunction with a =t="%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. =%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 %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.
+ %STOPINCLUDE% ---++ Related [[VarPUBURLPATH][PUBURLPATH]], [[VarSCRIPTNAME][SCRIPTNAME]], [[VarSCRIPTSUFFIX][SCRIPTSUFFIX]], [[VarSCRIPTURL][SCRIPTURL]] diff --git a/core/lib/Foswiki.pm b/core/lib/Foswiki.pm index 38e0bf4cd2..7e8ff4401c 100644 --- a/core/lib/Foswiki.pm +++ b/core/lib/Foswiki.pm @@ -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= @@ -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 @@ -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') ); @@ -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); diff --git a/core/lib/Foswiki/Func.pm b/core/lib/Foswiki/Func.pm index 145786bc59..93a6f20ec1 100644 --- a/core/lib/Foswiki/Func.pm +++ b/core/lib/Foswiki/Func.pm @@ -151,53 +151,79 @@ 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. getScriptUrl('x','y','view','#'=>'XXX',a=>1,b=>2) will give .../view/x/y?a=1&b=2#XXX + * =...= - 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 + + * 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:* 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'); +$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'); + + =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:* 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'); +$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'); + *Since:* 19 Jan 2012 (when called without parameters, this function is backwards-compatible with the old version which was deprecated 28 Nov 2008). @@ -205,13 +231,13 @@ 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 diff --git a/core/lib/Foswiki/Macros/SCRIPTURL.pm b/core/lib/Foswiki/Macros/SCRIPTURL.pm index d08008bb18..e297f2d9f4 100644 --- a/core/lib/Foswiki/Macros/SCRIPTURL.pm +++ b/core/lib/Foswiki/Macros/SCRIPTURL.pm @@ -14,25 +14,51 @@ BEGIN { } sub SCRIPTURL { - my ( $this, $params, $topicObject, $relative ) = @_; - my ( $web, $topic, $script ); - $script = $params->{_DEFAULT}; - $web = $params->{web}; + my ( $this, $params, $path2Object, $relative ) = @_; + my ( $path1, $path2, $script ); - my $jsonrest = ( defined $script && $script =~ m/^jsonrpc|rest(:?auth)?$/ ); + $script = $params->{_DEFAULT}; - if ( !$jsonrest && defined $params->{topic} ) { - my @path = split( /[\/.]+/, $params->{topic} ); - $topic = pop(@path) if scalar(@path); - if ( scalar(@path) ) { - $web = join( '/', @path ) - ; # web= is ignored, so preserve it in the query string + if ( defined $script && substr( $script, 0, 4 ) eq 'rest' ) { + if ( defined $params->{subject} ) { + $path1 = $params->{subject}; + delete $params->{subject}; + } + if ( defined $params->{verb} ) { + $path2 = $params->{verb}; + delete $params->{verb}; + } + } + elsif ( defined $script && $script eq 'jsonrpc' ) { + if ( defined $params->{namespace} ) { + $path1 = $params->{namespace}; + delete $params->{namespace}; } - else { + if ( defined $params->{method} ) { + $path2 = $params->{method}; + delete $params->{method}; + } + } + else { + if ( defined $params->{topic} ) { + my @path = split( /[\/.]+/, $params->{topic} ); + $path2 = pop(@path) if scalar(@path); + if ( scalar(@path) ) { + $path1 = join( '/', @path ) + ; # web= is ignored, so preserve it in the query string + } + else { + $path1 = $params->{web}; + delete $params + ->{web}; # web= used, so drop the duplicate query param. + } + delete $params->{topic}; + } + elsif ( defined $params->{web} ) { + $path1 = $params->{web}; delete $params->{web}; # web= used, so drop the duplicate query param. } - delete $params->{topic}; } my @p = @@ -40,12 +66,18 @@ sub SCRIPTURL { grep { !/^(_.*|path)$/ } keys %$params; - if ( $jsonrest && ( $params->{web} || $params->{topic} || scalar @p ) ) { - return -"
Parameters are not supported when generating rest or jsonrpc URLs.
"; + if ( defined $script && scalar @p ) { + if ( substr( $script, 0, 4 ) eq 'rest' && ( !$path1 || !$path2 ) ) { + return +"
$script requires both 'subject' and 'verb' parameters if other parameters are supplied.
"; + } + if ( $script eq 'jsonrpc' && !$path1 && scalar @p ) { + return +"
$script requires the 'namespace' parameter if other parameters are supplied.
"; + } } - return $this->getScriptUrl( !$relative, $script, $web, $topic, @p ); + return $this->getScriptUrl( !$relative, $script, $path1, $path2, @p ); } 1;