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 & I.e. returns 'value' if the key is 'key' in the url 'http://server/?Open&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('&'); 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 & I.e. returns 'value' if the key is 'key' in the url 'http://server/?Open&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('&'); 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!
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.
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)
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
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)
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)
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
Okay, works perfectly so far! Thanks! :)
~ Leiger - Wikidot Community Admin - Volunteer
Wikidot: Official Documentation | Wikidot Discord server