Feature Proposal: Allow params in a squab view [[Page?p=data]]

Motivation

From a user request at my company:
We’ve got some twiki pages where we provide links back to specific previous versions that corresponded to earlier releases of the product. If in web W I have page P, the reference &091;[P?rev=3]] fails (renders as P?), but the reference &091;[http://twiki.corp.com/view/W/P?rev=3]] works correctly.

In a related use case, it would often be handy to be able to write &091;[TopicName?raw="on"]]

Foswiki being still pretty close to TWiki in how it works, I'm presuming you haven't done this yet.. Please forgive if you have.

Description and Documentation

Examples

Impact

%WHATDOESITAFFECT%
edit

-- Contributors: VickiBrown

Implementation

Against 5795 -- CrawfordCurrie - 15 Dec 2009
Index: core/lib/Foswiki/Render.pm
===================================================================
--- core/lib/Foswiki/Render.pm   (revision 5792)
+++ core/lib/Foswiki/Render.pm   (working copy)
@@ -565,8 +565,8 @@
 =cut
 
 sub internalLink {
-    my ( $this, $web, $topic, $linkText, $anchor, $linkIfAbsent, $keepWebPrefix,
-        $hasExplicitLinkLabel )
+    my ( $this, $web, $topic, $linkText, $anchor,
+         $linkIfAbsent, $keepWebPrefix, $hasExplicitLinkLabel, $params )
       = @_;
 
     # SMELL - shouldn't it be callable by Foswiki::Func as well?
@@ -611,13 +611,13 @@
     # Add <nop> before WikiWord inside link text to prevent double links
     $linkText =~ s/(?<=[\s\(])([$Foswiki::regex{upperAlpha}])/<nop>$1/go;
     return _renderWikiWord( $this, $web, $topic, $linkText, $anchor,
-        $linkIfAbsent, $keepWebPrefix );
+        $linkIfAbsent, $keepWebPrefix, $params );
 }
 
 # TODO: this should be overridable by plugins.
 sub _renderWikiWord {
     my ( $this, $web, $topic, $linkText, $anchor, $linkIfAbsent,
-        $keepWebPrefix ) = @_;
+        $keepWebPrefix, $params ) = @_;
     my $session = $this->{session};
     my $topicExists = $session->topicExists( $web, $topic );
 
@@ -640,7 +640,7 @@
           if $Foswiki::cfg{Cache}{Enabled};
 
         return _renderExistingWikiWord( $this, $web, $topic, $linkText,
-            $anchor );
+            $anchor, $params );
     }
     if ($linkIfAbsent) {
 
@@ -651,6 +651,7 @@
 
         # add a dependency so that the page gets invalidated as soon as the
         # WikiWord comes into existance
+        # Note we *ignore* the params if the target topic does not exist
         $this->{session}->{cache}->addDependency($web, $topic)
           if $Foswiki::cfg{Cache}{Enabled};
 
@@ -664,7 +665,7 @@
 }
 
 sub _renderExistingWikiWord {
-    my ( $this, $web, $topic, $text, $anchor ) = @_;
+    my ( $this, $web, $topic, $text, $anchor, $params ) = @_;
 
     my @cssClasses;
     push(@cssClasses, 'foswikiCurrentWebHomeLink')
@@ -677,6 +678,10 @@
 
     my @attrs;
     my $href = $this->{session}->getScriptUrl( 0, 'view', $web, $topic );
+    if ($params) {
+        $href .= $params;
+    }
+
     if ($anchor) {
         $anchor = $this->_makeAnchorName($anchor);
 
@@ -777,9 +782,9 @@
     $link =~ s/^\s+//;
     $link =~ s/\s+$//;
 
-    my $hasExplicitLinkLabel = $text ? 1 : undef;
+    my $hasExplicitLinkLabel = $text ? 1 : 0;
 
-    # Explicit external [[$link][$text]]-style can be handled directly
+    # Explicit external [[http://$link][$text]]-style can be handled directly
     if ( $link =~ m!^($Foswiki::regex{linkProtocolPattern}\:|/)! ) {
         if ( defined $text ) {
 
@@ -807,11 +812,21 @@
 
     $text ||= $link;
 
+    # Extract '?params'
+    # $link =~ s/(\?.*?)(?>#|$)//;
+    my $params = '';
+    if ( $link =~ s/(\?.*$)// ) {
+        $params = $1;
+        my $p = quotemeta($params);
+        $text =~ s/$p//;
+    }
+
     # Extract '#anchor'
     # $link =~ s/(\#[a-zA-Z_0-9\-]*$)//;
     my $anchor = '';
     if ( $link =~ s/($Foswiki::regex{anchorRegex}$)// ) {
         $anchor = $1;
+        #$text =~ s/#$anchor//;
     }
 
     # filter out &any; entities (legacy)
@@ -844,7 +859,7 @@
     my ( $web, $topic ) =
       $this->{session}->normalizeWebTopicName( $topicObject->web, $link );
     return $this->internalLink( $web, $topic, $text, $anchor, 1, undef,
-        $hasExplicitLinkLabel );
+        $hasExplicitLinkLabel, $params );
 }
 
 # Handle an external link typed directly into text. If it's an image
Index: UnitTestContrib/test/unit/FormattingTests.pm
===================================================================
--- UnitTestContrib/test/unit/FormattingTests.pm   (revision 5789)
+++ UnitTestContrib/test/unit/FormattingTests.pm   (working copy)
@@ -88,6 +88,42 @@
 [[$Foswiki::cfg{HomeTopicName}]]
 ACTUAL
     $this->do_test( $expected, $actual );
+
+    # [[WikiWord#anchor]]
+    $expected = <<EXPECTED;
+<a class="foswikiCurrentWebHomeLink" href="$this->{sup}/$this->{test_web}/$Foswiki::cfg{HomeTopicName}#anchor">$Foswiki::cfg{HomeTopicName}#anchor</a>
+EXPECTED
+    $actual = <<ACTUAL;
+[[$Foswiki::cfg{HomeTopicName}#anchor]]
+ACTUAL
+    $this->do_test( $expected, $actual );
+
+    # [[WikiWord?param=data]]
+    $expected = <<EXPECTED;
+<a class="foswikiCurrentWebHomeLink" href="$this->{sup}/$this->{test_web}/$Foswiki::cfg{HomeTopicName}?param=data">$Foswiki::cfg{HomeTopicName}</a>
+EXPECTED
+    $actual = <<ACTUAL;
+[[$Foswiki::cfg{HomeTopicName}?param=data]]
+ACTUAL
+    $this->do_test( $expected, $actual );
+
+    # [[WikiWord?param=data#anchor]]
+    $expected = <<EXPECTED;
+<a class="foswikiCurrentWebHomeLink" href="$this->{sup}/$this->{test_web}/$Foswiki::cfg{HomeTopicName}?param=data#anchor">$Foswiki::cfg{HomeTopicName}</a>
+EXPECTED
+    $actual = <<ACTUAL;
+[[$Foswiki::cfg{HomeTopicName}?param=data#anchor]]
+ACTUAL
+    $this->do_test( $expected, $actual );
+
+    # [[WikiWord#anchor?param=data]]
+    $expected = <<EXPECTED;
+<a class="foswikiCurrentWebHomeLink" href="$this->{sup}/$this->{test_web}/$Foswiki::cfg{HomeTopicName}?param=data#anchor">$Foswiki::cfg{HomeTopicName}</a>
+EXPECTED
+    $actual = <<ACTUAL;
+[[$Foswiki::cfg{HomeTopicName}?param=data#anchor]]
+ACTUAL
+    $this->do_test( $expected, $actual );
 }
 
 # [[Web.WikiWord]]
@@ -101,6 +137,36 @@
 [[$Foswiki::cfg{SystemWebName}.$Foswiki::cfg{HomeTopicName}]]
 ACTUAL
     $this->do_test( $expected, $actual );
+
+    # [[Web.WikiWord#anchor]]
+    $expected = <<EXPECTED;
+<a href="$this->{sup}/$Foswiki::cfg{SystemWebName}/$Foswiki::cfg{HomeTopicName}#anchor">$Foswiki::cfg{SystemWebName}.$Foswiki::cfg{HomeTopicName}#anchor</a>
+EXPECTED
+
+    $actual = <<ACTUAL;
+[[$Foswiki::cfg{SystemWebName}.$Foswiki::cfg{HomeTopicName}#anchor]]
+ACTUAL
+    $this->do_test( $expected, $actual );
+
+    # [[Web.WikiWord?param=data]]
+    $expected = <<EXPECTED;
+<a class="foswikiCurrentWebHomeLink" href="$this->{sup}/$this->{test_web}/$Foswiki::cfg{HomeTopicName}?param=data">$Foswiki::cfg{HomeTopicName}</a>
+EXPECTED
+
+    $actual = <<ACTUAL;
+[[$Foswiki::cfg{HomeTopicName}?param=data]]
+ACTUAL
+    $this->do_test( $expected, $actual );
+
+    # [[Web.WikiWord?param=data#anchor]]
+    $expected = <<EXPECTED;
+<a href="$this->{sup}/$Foswiki::cfg{SystemWebName}/$Foswiki::cfg{HomeTopicName}?param=data#anchor">$Foswiki::cfg{SystemWebName}.$Foswiki::cfg{HomeTopicName}</a>
+EXPECTED
+
+    $actual = <<ACTUAL;
+[[$Foswiki::cfg{SystemWebName}.$Foswiki::cfg{HomeTopicName}?param=data#anchor]]
+ACTUAL
+    $this->do_test( $expected, $actual );
 }
 
 # [[Web.WikiWord][Alt TextAlt]]
@@ -129,6 +195,32 @@
     $this->do_test( $expected, $actual );
 }
 
+# [[Web.WikiWord]]
+sub test_squabbedWebWikiword_params {
+    my $this     = shift;
+    my $expected = <<EXPECTED;
+<a href="$this->{sup}/$Foswiki::cfg{SystemWebName}/$Foswiki::cfg{HomeTopicName}?param=data">$Foswiki::cfg{SystemWebName}.$Foswiki::cfg{HomeTopicName}</a>
+EXPECTED
+
+    my $actual = <<ACTUAL;
+[[$Foswiki::cfg{SystemWebName}.$Foswiki::cfg{HomeTopicName}?param=data]]
+ACTUAL
+    $this->do_test( $expected, $actual );
+}
+
+# [[Web.WikiWord][Alt TextAlt]]
+sub test_squabbedWebWikiWordAltText_params {
+    my $this     = shift;
+    my $expected = <<EXPECTED;
+<a href="$this->{sup}/$Foswiki::cfg{SystemWebName}/$Foswiki::cfg{HomeTopicName}?param=data">Alt <nop>TextAlt</a>
+EXPECTED
+
+    my $actual = <<ACTUAL;
+[[$Foswiki::cfg{SystemWebName}.$Foswiki::cfg{HomeTopicName}?param=data][Alt TextAlt]]
+ACTUAL
+    $this->do_test( $expected, $actual );
+}
+
 sub test_escapedWikiWord {
     my $this     = shift;
     my $expected = <<EXPECTED;

Discussion

I have wanted this for years. No idea why I never implemented it.

Being more specific, there are (at least) three cases:
  • [[HuntSnark?avoid=boojum]]
  • [[hunt snark?avoid=boojum]]
  • [[Web.HuntSnark?avoid=boojum]]
Suggest that everything between the ? and the ] be taken "as-is", and no attempt made to parse or otherwise interpret it. Anchors also have to be handled.

-- CrawfordCurrie - 15 Dec 2009

Excellent idea. I've wanted this as well. I'd also like to suggest that there is some way to automatically pass the current query parameters onto the linked topic. I've needed this with topics that are tailored using URLPARAM input. An example might be our own ApacheConfigGenerator where we could have a related .htconfig file generator, and the current customized query string could be passed over to the related topic in the link. Since %QUERYSTRING% doesn't expand to include the ?, something simple like [[HuntSnark%QUERYSTRING%]] won't work, and including the question mark will violate the constraint that everything between the ? and ] be taken "as-is". Not sure of a good solution.

-- GeorgeClark - 16 Dec 2009

I am for. Params and anchors should be passed.

-- ArthurClemens - 16 Dec 2009

[[HuntSnark?%QUERYSTRING%]] should work OK. You can have an empty string after the ?, can't you?

-- CrawfordCurrie - 16 Dec 2009

Above you state Suggest that everything between the ? and the ] be taken "as-is", and no attempt made to parse or otherwise interpret it. Wouldn't this cause the querystring to not be expanded, resulting in the link including the verbatim =%QUERYSTRING% as the query string and not the actual querystring? The more I think through this, I suspect that it would be more useful to interpret Foswiki macros and allow them to be skipped by escaping them in some way.

-- GeorgeClark - 16 Dec 2009

Ah no. Remember that the rendering process is two-phase; macros are expanded first, and then TML is rendered. Since we are talking about a TML construct here, the macros will all be full expanded before it hits.

-- CrawfordCurrie - 17 Dec 2009

I might be missing something obvious here, but...

Surely the <nop> used in the examples above is sufficient to include, as George wrote, "the verbatim %QUERYSTRING% as the query string and not the actual querystring", i.e. %<nop>QUERYSTRING% ?

-- MichaelTempest - 18 Dec 2009

Some more examples might help. Imagine we are on a page that was viewed ?raw=on
  • [[HuntSnark?%QUERYSTRING#CabbagePatch]] will pass "?%QUERYSTRING#CabbagePatch" to the view of HuntSnark
  • [[HuntSnark?avoid=%20boojum%20#CabbagePatch]] will pass "?avoid= boojum #CabbagePatch" to the view of HuntSnark
  • [[HuntSnark?#CabbagePatch]] will pass "?raw=on#CabbagePatch"
Now imagine we have no params:
  • [[HuntSnark?#CabbagePatch]] will pass "?"

You can try this out on trunk, since I unintentionally checked in the patch last night.

-- CrawfordCurrie - 18 Dec 2009

Good initiative. I have had users wanting this and I have had to guide them to the full URL method. I support this 100%

-- KennethLavrsen - 19 Dec 2009
Topic revision: r14 - 06 Dec 2010, GeorgeClark
The copyright of the content on this website is held by the contributing authors, except where stated elsewhere. See Copyright Statement. Creative Commons License    Legal Imprint    Privacy Policy