URL Title with Slash Redirect

By tsangk tsangk

Description

Allows _template or ListPages variables like title which can hold slashes (e.g. Show/Hide content from specific users) to be used in NewPage url:
e.g. http://wikidot.com/test/edit/true/title/Re:Show/Hide content from specific users will change
to
http://wikidot.com/test/edit/true/title/Re:Show%2FHide content from specific users will change

Changelog:

  • 4th Jan 2017
    • Fixes unescaped percentage sign issue reported by rurwin below
    • Adopts the URL protocol supplied in the hash (rather than defaulting to http).
    • Adds a click-through link in case the Javascript-based redirect fails.
    • Fixes (round 2): script tag duplicated

Code

<html><head><title>Please wait...</title></head><body><div>Please wait... redirecting.</div><divstyle="font-size: 80%"style="display: none"id="alt-redirect-link-para">or <ahrefid="manual-redirect-link">click here</a></div><scripttype="text/javascript">
//
// URL Title with Slash Redirect
// for Wikidot
//
// by @tsangk
//
// First published: 4 Apr 2010
// 
// Revised 4 Jan 2017:
// - Adopts provided URI schema
// - Makes sures other URI special characters like `%` are properly escaped
//
 
//
/* URL class for JavaScript
 * Copyright (C) 2003 Johan Känngård, <johanATkanngardDOTnet>
 * http://dev.kanngard.net/
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * The GPL is located at: http://www.gnu.org/licenses/gpl.txt
 */
 
/* Creates a new URL object with the specified url String. */
function URL(url){
 if(url.length==0) eval('throw "Invalid URL ['+url+'];');
 this.url=url;
 this.port=-1;
 this.query=(this.url.indexOf('?')>=0)?this.url.substring(this.url.indexOf('?')+1):'';
 if(this.query.indexOf('#')>=0) this.query=this.query.substring(0,this.query.indexOf('#'));
 this.protocol='';
 this.host='';
 var protocolSepIndex=this.url.indexOf('://');
 if(protocolSepIndex>=0){
 this.protocol=this.url.substring(0,protocolSepIndex).toLowerCase();
 this.host=this.url.substring(protocolSepIndex+3);
 if(this.host.indexOf('/')>=0) this.host=this.host.substring(0,this.host.indexOf('/'));
 var atIndex=this.host.indexOf('@');
 if(atIndex>=0){
 var credentials=this.host.substring(0,atIndex);
 var colonIndex=credentials.indexOf(':');
 if(colonIndex>=0){
 this.username=credentials.substring(0,colonIndex);
 this.password=credentials.substring(colonIndex);
 }else{
 this.username=credentials;
 }
 this.host=this.host.substring(atIndex+1);
 }
 var portColonIndex=this.host.indexOf(':');
 if(portColonIndex>=0){
 this.port=this.host.substring(portColonIndex);
 this.host=this.host.substring(0,portColonIndex);
 }
 this.file=this.url.substring(protocolSepIndex+3);
 this.file=this.file.substring(this.file.indexOf('/'));
 }else{
 this.file=this.url;
 }
 if(this.file.indexOf('?')>=0) this.file=this.file.substring(0, this.file.indexOf('?'));
 var refSepIndex=url.indexOf('#');
 if(refSepIndex>=0){
 this.file=this.file.substring(0,refSepIndex);
 this.reference=this.url.substring(this.url.indexOf('#'));
 }else{
 this.reference='';
 }
 this.path=this.file;
 if(this.query.length>0) this.file+='?'+this.query;
 if(this.reference.length>0) this.file+='#'+this.reference;
 
 this.getPort=getPort;
 this.getQuery=getQuery;
 this.getProtocol=getProtocol;
 this.getHost=getHost;
 this.getUserName=getUserName;
 this.getPassword=getPassword;
 this.getFile=getFile;
 this.getReference=getReference;
 this.getPath=getPath;
 this.getArgumentValue=getArgumentValue;
 this.getArgumentValues=getArgumentValues;
 this.toString=toString;
 
 /* Returns the port part of this URL, i.e. '8080' in the url 'http://server:8080/' */
 function getPort(){
 return this.port;
 }
 
 /* Returns the query part of this URL, i.e. 'Open' in the url 'http://server/?Open' */
 function getQuery(){
 return this.query;
 }
 
 /* Returns the protocol of this URL, i.e. 'http' in the url 'http://server/' */
 function getProtocol(){
 return this.protocol;
 }
 
 /* Returns the host name of this URL, i.e. 'server.com' in the url 'http://server.com/' */
 function getHost(){
 return this.host;
 }
 
 /* Returns the user name part of this URL, i.e. 'joe' in the url 'http://joe@server.com/' */
 function getUserName(){
 return this.username;
 }
 
 /* Returns the password part of this url, i.e. 'secret' in the url 'http://joe:secret@server.com/' */
 function getPassword(){
 return this.password;
 }
 
 /* Returns the file part of this url, i.e. everything after the host name. */
 function getFile(){
 return this.file;
 }
 
 /* Returns the reference of this url, i.e. 'bookmark' in the url 'http://server/file.html#bookmark' */
 function getReference(){
 return this.reference;
 }
 
 /* Returns the file path of this url, i.e. '/dir/file.html' in the url 'http://server/dir/file.html' */
 function getPath(){
 return this.path;
 }
 
 /* Returns the FIRST matching value to the specified key in the query.
 If the url has a non-value argument, like 'Open' in '?Open&bla=12', this method
 returns the same as the key: 'Open'...
 The url must be correctly encoded, ampersands must encoded as &amp;
 I.e. returns 'value' if the key is 'key' in the url 'http://server/?Open&amp;key=value' */
 function getArgumentValue(key){
 var a=this.getArgumentValues();
 if(a.length<1) return '';
 for(i=0;i<a.length;i++){
 if(a[i][0]==key) returna[i][1];
 }
 return '';
 }
 
 /* Returnsallkey / valuepairsinthequeryasatwodimensionalarray */
 functiongetArgumentValues(){
 vara=newArray();
 varb=this.query.split('&amp;');
 varc='';
 if(b.length<1) returna;
 for(i=0;i<b.length;i++){
 c=b[i].split('=');
 a[i]=newArray(c[0],((c.length==1)?c[0]:c[1]));
 }
 returna;
 }
 
 /* ReturnsaStringrepresentationofthisurl */
 functiontoString(){
 returnthis.url;
 }
}
 
varurl=newURL(window.location.hash.substring(1));
varpath=escape(unescape(url.getPath()));
varstr=path.split("/");
varxtest;
 
varlink = "";
 
for (varxinstr) {
 if(x>0){
 xtest=x%2;
 if(!xtest){
 if(str[x]=="edit"){
 link+="/"+str[x];
 }
 else if(str[x]=="parentPage"){
 link+="/"+str[x];
 }
 else if(str[x]=="tags"){
 link+="/"+str[x];
 }
 else if(str[x]=="title"){
 link+="/"+str[x];
 }
 else
 {
 link+="%2F"+str[x];
 }
 }
 else
 {
 link+="/"+str[x];
 }
 }
 
}
 
var newLink = url.getProtocol() + "://" + url.getHost() + link;
document.getElementById("manual-redirect-link").href = newLink;
document.getElementById("alt-redirect-link-para").style.display = "block";
window.location = newLink;
 
// Just in case the location wasn't set above, we'll try again once the page loads
window.onload = function(){
window.location.href = newLink;
}
</script></body></html>
<scripttype="text/javascript">
/* URL class for JavaScript
 * Copyright (C) 2003 Johan Känngård, <johanATkanngardDOTnet>
 * http://dev.kanngard.net/
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * The GPL is located at: http://www.gnu.org/licenses/gpl.txt
 */
 
/* Creates a new URL object with the specified url String. */
function URL(url){
 if(url.length==0) eval('throw "Invalid URL ['+url+'];');
 this.url=url;
 this.port=-1;
 this.query=(this.url.indexOf('?')>=0)?this.url.substring(this.url.indexOf('?')+1):'';
 if(this.query.indexOf('#')>=0) this.query=this.query.substring(0,this.query.indexOf('#'));
 this.protocol='';
 this.host='';
 var protocolSepIndex=this.url.indexOf('://');
 if(protocolSepIndex>=0){
 this.protocol=this.url.substring(0,protocolSepIndex).toLowerCase();
 this.host=this.url.substring(protocolSepIndex+3);
 if(this.host.indexOf('/')>=0) this.host=this.host.substring(0,this.host.indexOf('/'));
 var atIndex=this.host.indexOf('@');
 if(atIndex>=0){
 var credentials=this.host.substring(0,atIndex);
 var colonIndex=credentials.indexOf(':');
 if(colonIndex>=0){
 this.username=credentials.substring(0,colonIndex);
 this.password=credentials.substring(colonIndex);
 }else{
 this.username=credentials;
 }
 this.host=this.host.substring(atIndex+1);
 }
 var portColonIndex=this.host.indexOf(':');
 if(portColonIndex>=0){
 this.port=this.host.substring(portColonIndex);
 this.host=this.host.substring(0,portColonIndex);
 }
 this.file=this.url.substring(protocolSepIndex+3);
 this.file=this.file.substring(this.file.indexOf('/'));
 }else{
 this.file=this.url;
 }
 if(this.file.indexOf('?')>=0) this.file=this.file.substring(0, this.file.indexOf('?'));
 var refSepIndex=url.indexOf('#');
 if(refSepIndex>=0){
 this.file=this.file.substring(0,refSepIndex);
 this.reference=this.url.substring(this.url.indexOf('#'));
 }else{
 this.reference='';
 }
 this.path=this.file;
 if(this.query.length>0) this.file+='?'+this.query;
 if(this.reference.length>0) this.file+='#'+this.reference;
 
 this.getPort=getPort;
 this.getQuery=getQuery;
 this.getProtocol=getProtocol;
 this.getHost=getHost;
 this.getUserName=getUserName;
 this.getPassword=getPassword;
 this.getFile=getFile;
 this.getReference=getReference;
 this.getPath=getPath;
 this.getArgumentValue=getArgumentValue;
 this.getArgumentValues=getArgumentValues;
 this.toString=toString;
 
 /* Returns the port part of this URL, i.e. '8080' in the url 'http://server:8080/' */
 function getPort(){
 return this.port;
 }
 
 /* Returns the query part of this URL, i.e. 'Open' in the url 'http://server/?Open' */
 function getQuery(){
 return this.query;
 }
 
 /* Returns the protocol of this URL, i.e. 'http' in the url 'http://server/' */
 function getProtocol(){
 return this.protocol;
 }
 
 /* Returns the host name of this URL, i.e. 'server.com' in the url 'http://server.com/' */
 function getHost(){
 return this.host;
 }
 
 /* Returns the user name part of this URL, i.e. 'joe' in the url 'http://joe@server.com/' */
 function getUserName(){
 return this.username;
 }
 
 /* Returns the password part of this url, i.e. 'secret' in the url 'http://joe:secret@server.com/' */
 function getPassword(){
 return this.password;
 }
 
 /* Returns the file part of this url, i.e. everything after the host name. */
 function getFile(){
 return this.file;
 }
 
 /* Returns the reference of this url, i.e. 'bookmark' in the url 'http://server/file.html#bookmark' */
 function getReference(){
 return this.reference;
 }
 
 /* Returns the file path of this url, i.e. '/dir/file.html' in the url 'http://server/dir/file.html' */
 function getPath(){
 return this.path;
 }
 
 /* Returns the FIRST matching value to the specified key in the query.
 If the url has a non-value argument, like 'Open' in '?Open&bla=12', this method
 returns the same as the key: 'Open'...
 The url must be correctly encoded, ampersands must encoded as &amp;
 I.e. returns 'value' if the key is 'key' in the url 'http://server/?Open&amp;key=value' */
 function getArgumentValue(key){
 var a=this.getArgumentValues();
 if(a.length<1) return '';
 for(i=0;i<a.length;i++){
 if(a[i][0]==key) returna[i][1];
 }
 return '';
 }
 
 /* Returnsallkey / valuepairsinthequeryasatwodimensionalarray */
 functiongetArgumentValues(){
 vara=newArray();
 varb=this.query.split('&amp;');
 varc='';
 if(b.length<1) returna;
 for(i=0;i<b.length;i++){
 c=b[i].split('=');
 a[i]=newArray(c[0],((c.length==1)?c[0]:c[1]));
 }
 returna;
 }
 
 /* ReturnsaStringrepresentationofthisurl */
 functiontoString(){
 returnthis.url;
 }
}
varurlfix=unescape(window.location.hash.substring(1));
 
varurl=newURL(urlfix);
varstr=url.getPath();
varpath=str;
varxtest;
varokay;
varlink;
str=str.split("/");
varx;
for (xinstr)
{
if(x>0){
xtest=x%2;
if(!xtest){
if(str[x]=="edit"){
link+="/"+str[x];
}
else if(str[x]=="parentPage"){
link+="/"+str[x];
}
else if(str[x]=="tags"){
link+="/"+str[x];
}
else if(str[x]=="title"){
link+="/"+str[x];
}
else
{
link+="%2F"+str[x];
}
}
else
{
link+="/"+str[x];
}
}
 
}
link.replace("undefined","");
//alert("http://"+url.getHost()+"/"+link.substring(10));
theLocation="http://"+url.getHost()+"/"+link.substring(10);
window.top.location.href=theLocation;
window.parent.location.href=theLocation;
window.top.location.replace(theLocation);
 </script>
[[image test.jpg link="http://snippets.wdfiles.com/local--code/code:url-title-with-slash-redirect/1#http://cyclods.wikidot.com/post:new-post-84/edit/true/parentPage/thread:84/tags/_open/title/Re: Show/Hide content from specific users"]]

If you want to make sure there's no session leakage in HTTPS, make sure you supply both URLs in HTTPS:

[[image test.jpg link="https://snippets.wdfiles.com/local--code/code:url-title-with-slash-redirect/1#https://cyclods.wikidot.com/post:new-post-84/edit/true/parentPage/thread:84/tags/_open/title/Re: Show/Hide content from specific users"]]

Code tested for Firefox and IE.


Thanks to tsangk for this great snippet: conditional-blocks


text above inserted with:

[[include :snippets:if START |unique=1|type=equal|var1=%%name%%|var2=conditional-blocks]]
**##red|Thanks to tsangk for this great snippet:##** [[[code:conditional-blocks]]]
[[include :snippets:if END]]



Other snippets posted by tsangk

404 (Page does not exist) Redirect - 20 Nov 2012 11:40
Custom HTML Blocks - 25 Oct 2012 11:30
Syntax Highlighter - 16 Feb 2012 01:45
Conditional Blocks - 15 Feb 2012 00:36


Rate this solution

If you think this solution is useful — rate it up!

rating: +1
Doesn't correctly quote "%" characters.
soronlin soronlin 03 Jan 2018 16:04

I have used this to create comment pages in a forum build:

[[span class="forum-button"]][[[http://snippets.wdfiles.com/local--code/code:url-title-with-slash-redirect/1#http://srhdev.wikidot.com/comment:new_comment/edit/true/parentPage/%%fullname%%/title/Re:%20%%title%% | Reply]]][[/span]]

It has worked fine for years, until someone just created a thread called "Expect a 15% performance loss coming soon". On that page, the button produces a 400 error. The URL it produces is:
http://snippets.wdfiles.com/local--code/code:url-title-with-slash-redirect/1#http://srhdev.wikidot.com/comment:new_comment/edit/true/parentPage/thread:834/title/Re:%20Expect%20a%2015%%20performance%20loss%20coming%20soon

The "%" not quoted, which it should be.
Last edited on 03 Jan 2018 16:12 by soronlin
by soronlin soronlin , 03 Jan 2018 16:04
Re: Doesn't correctly quote "%" characters.
tsangk tsangk 04 Jan 2018 02:03

Wow. That was some pretty terrible code 😂

Gone back and made some changes. The modified version shouldn't break any existing code (unless the use case is to generate broken URL encoding).

If anyone else is experiencing new problems as a result of this change, please reply below with an example :)


Kenneth Tsang (@jxeeno)

by tsangk tsangk , 04 Jan 2018 02:03
Re: Doesn't correctly quote "%" characters.
soronlin soronlin 04 Jan 2018 10:18

Sorry, that's worse.

The problem is that there are already bona-fide % characters in the URL replacing space characters in the page title:

…/title/Re:%20Expect%20a%2015%%20performance%20loss%20coming%20soon

The fix to escape the % in "15%" also escapes all the others, with the result that the new page title has all the escapes in.

The only way I can think to fix this is to go back to the original and make a special case of a "%" that is not followed by a hexadecimal digit:

var urlfix=unescape(window.location.hash.substring(1));
urlfix = urlfix.replace(/%[^0-9A-Fa-f]/g, function myfunction(x) {return "%25"+x.slice(1);})

(Warning: I have made it a career ambition never to learn Javascript.)

That will lead to odd behaviour when the % is followed by a digit, but I can't see an obvious solution.

On reflection, I think this is a Wikidot problem. I've filed a bug report there: http://feedback.wikidot.com/bug:1152

Last edited on 04 Jan 2018 11:26 by soronlin
by soronlin soronlin , 04 Jan 2018 10:18
Re: Doesn't correctly quote "%" characters.
tsangk tsangk 04 Jan 2018 11:47

Ah, dang. Didn't test that.

I've just updated it to use escape(unescape()) — let me know if it's working better. Based on that link on feedback.wikidot.com, looks like it's working now.

escape(unescape()): the first unescape will ensure any encoded entities are correctly decoded first, and then encode all special characters except / unless it's after the /title/. I think this solves it.

There's still an edge case where the user might provide a page title with a substring that is in the URI encoding format. For example, if the title was to be: Problem with encoding %20. I don't think it's possible to fix that though.


Kenneth Tsang (@jxeeno)

by tsangk tsangk , 04 Jan 2018 11:47
Re: Doesn't correctly quote "%" characters.
soronlin soronlin 05 Jan 2018 11:42

That's got it. Thanks.

I think it's still a Wikidot bug though; there's no good reason why % should be invalid in titles.

by soronlin soronlin , 05 Jan 2018 11:42
Re: Doesn't correctly quote "%" characters.
tsangk tsangk 05 Jan 2018 12:59

Great!

I think it's still a Wikidot bug though; there's no good reason why % should be invalid in titles.

Hmm, not sure if it's something that Wikidot can do. The error is likely being thrown somewhere lower in the stack (i.e. not in Wikidot's codebase).

Most web servers will trip over trying to parse a URI with % but not followed by two hex characters. What happens when that occurs isn't defined. But when it happens after the hash, you're at the discretion of the browser's implementation.

See recommendations on w3.org:

Sequences which start with a percent sign but are not followed by two hexadecimal characters are reserved for future extension.

The ideal solution is for Wikidot to provide a way of variables with percent encoding — e.g. %%title|urlencode%%.


Kenneth Tsang (@jxeeno)

by tsangk tsangk , 05 Jan 2018 12:59
tsangk tsangk 29 Oct 2010 08:51

The second code block is currently useless. It was used to redirect to parent (:


Kenneth Tsang (@jxeeno)

by tsangk tsangk , 29 Oct 2010 08:51
soronlin soronlin 29 Oct 2010 08:44

Why are there two code blocks here? I notice the second was added recently. Maybe there should be some documentation describing when to use each?

by soronlin soronlin , 29 Oct 2010 08:44
leiger leiger 24 Apr 2010 13:49

Edit: Nevermind… I think you told me exactly what to do xD Will just mess around with it for a bit and if I can't figure it out by then I'll reply back here again.

[ in regards to http://feedback.wikidot.com/bug:115 ]


~ Leiger - Wikidot Community Admin - Volunteer
Wikidot: Official Documentation | Wikidot Discord server

Last edited on 24 Apr 2010 13:53 by leiger
by leiger leiger , 24 Apr 2010 13:49
leiger leiger 24 Apr 2010 14:04

Okay, works perfectly so far! Thanks! :)


~ Leiger - Wikidot Community Admin - Volunteer
Wikidot: Official Documentation | Wikidot Discord server

by leiger leiger , 24 Apr 2010 14:04
page revision: 10, last edited: 04 Jan 2018 11:32
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License
Click here to edit contents of this page.
Click here to toggle editing of individual sections of the page (if possible). Watch headings for an "edit" link when available.
Append content without editing the whole page source.
Check out how this page has evolved in the past.
If you want to discuss contents of this page - this is the easiest way to do it.
View and manage file attachments for this page.
A few useful tools to manage this Site.
Change the name (also URL address, possibly the category) of the page.
View wiki source for this page without editing.
View/set parent page (used for creating breadcrumbs and structured layout).

AltStyle によって変換されたページ (->オリジナル) /