Hi,
I've seen this question: Is there a way to get a Confluence page's tiny link programmatically? and I have one in a similar vein.
What is the algorithm used to create the tiny links?
I'm assuming it's some kind of base 64/62 encoder of some sort? If anyone knows where in the source to look for the code, a path would be greatly appreciated.
Steve
Check out the source of com.atlassian.confluence.pages.TinyUrl...
Base64.encodeBase64(longToByteArray(id));
id is the pageId.
I recently ported this to python:
def page_id_to_tiny(page_id):
return base64.b64encode((page_id).to_bytes(4, byteorder='little')).decode().replace('/','-').replace('+','_').rstrip('A=')
def tiny_to_page_id(tiny):
return int.from_bytes(base64.b64decode(tiny.ljust(8,'A').replace('_','+').replace('-','/').encode()),byteorder='little')
Updated as per feedback below from @Sebastian Schmachtel and @Fabian Sturm that noticed that Atlassian do not actually implement url safe encoding from rfc4648 correctly.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
This would have been a perfect answer but I think Atlassian screwed up and + is replaced by _ and / by - instead of the other way round as it is defined for the url safe encoding rfc4648.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I used this functions and started to see weird effects. So I returned here et voila a hint just in time :-)
My (working) code looks now like this
def page_id_to_tiny(page_ids: List[int]) -> List[str]:
"""converts a list of pageids (int) to a list of last part of shortlink (/x/$PART)"""
shortlinks: List[str] = []
for page_id in page_ids:
encoded = base64.b64encode(page_id.to_bytes(4, byteorder='little')).decode()
encoded_fixed = encoded.replace("/", "-").replace("+", "_").rstrip('A=')
shortlinks.append(encoded_fixed)
return shortlinks
def tiny_to_page_id(tiny_links: List[str]) -> List[int]:
"""converts a list of last part of shortlink (/x/$PART) to a list of pageids (int)"""
page_ids: List[int] = []
for tiny_link in tiny_links:
tiny_link = tiny_link.replace("-", "/").replace("_", "+")
page_ids.append(int.from_bytes(base64.b64decode(tiny_link.ljust(8, 'A').encode()), byteorder='little'))
return page_ids
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Here a solution in JS. Please check if the browsers are compatible with btoa and atob (https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/btoa).
// The long to byte array conversion is taken from this source:
// https://stackoverflow.com/questions/8482309/converting-javascript-integer-to-byte-array-and-back
function longToByteArray(long) {
var byteArray = [0, 0, 0, 0];
for ( var index = 0; index < byteArray.length; index ++ ) {
var byte = long & 0xff;
byteArray [ index ] = byte;
long = (long - byte) / 256 ;
}
return byteArray;
}
function pageIdToShortUrl(pageId) {
return btoa(longToByteArray(pageId)
.map(String.fromCharCode)
.map(function (s) { return s[0] })
.join(""))
.replace(/=/g,"");
}
function byteArrayToLong(byteArray) {
var value = 0;
for ( var i = byteArray.length - 1; i >= 0; i--) {
value = (value * 256) + byteArray[i];
}
return value;
};
function shortUrlToPageId(shortUrl) {
return byteArrayToLong(atob(shortUrl)
.split("")
.map(function (b) { return b.charCodeAt()}));
}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I used the following functions in PHP based on the Steve's code:
function id2tinylink($id) { $bytes = pack("L", (int)$id); // convert the long to bytes $base64string=base64_encode($bytes); $base64string_escaped=strtr($base64string,'+/','_-'); return rtrim($base64string_escaped, "A="); } function tinylink2id($tinylink){ $base64string_encoded = str_pad($tinylink, 8, "A", STR_PAD_RIGHT); $base64string = strtr($base64string_encoded, '_-', '+/'); $bytes = base64_decode($base64string); return unpack("L",$bytes)[1]; }
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I used your examples to write a PostgreSQL equivalent. Probably not as efficient, but could be useful.
-- Encode to short URL
SELECT TRIM(
TRAILING 'A=' FROM
translate(encode(('\x' || regexp_replace(
lpad(to_hex(content.contentid), 16, '0'),
'(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)',
'\8\7\6\5\4\3\2\1'))::bytea, 'base64'), '-_', '/+')) "short_url"
FROM (SELECT 200999755 "contentid") "content"
An example from the Confluence table directly:
SELECT contentid, TRIM(
TRAILING 'A=' FROM
translate(encode(('\x' || regexp_replace(
lpad(to_hex(content.contentid), 16, '0'),
'(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)',
'\8\7\6\5\4\3\2\1'))::bytea, 'base64'), '-_', '/+')) "short_url",
title
FROM content
LIMIT 10
To decode a short URL to it's content ID:
-- Decode to content ID
SELECT ('x' || regexp_replace(
rpad(encode(decode(translate(short.url || repeat('A', 8 - length(short.url)), '_-', '+/'), 'base64'), 'hex'), 16, '0'),
'(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)',
'\8\7\6\5\4\3\2\1'))::bit(64)::bigint "content_id"
FROM (SELECT 'SwP7Cw' "url") "short"
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Here is a rough bash implementation. I don't bother chopping a trailing A because this official implementation also didn't
confluence-tiny-code() {
local id="${1?}"
local tiny
printf -v tiny "\\\x%02x" $(( id & 0xff )) $(( (id >> 8 ) & 0xff )) $(( (id >> 16 ) & 0xff )) $(( (id >> 24 ) & 0xff ))
tiny=$(printf "$tiny" | base64)
tiny="${tiny//\+/_}"
tiny="${tiny//\//-}"
echo "${tiny%%=*}"
}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
If anyone would like to emulate this functionality in PHP for whatever reason:
function tinylinkid($id) { $bytes = pack("L", $id); // convert the long to bytes $bytes = rtrim($bytes); // trim any null characters $bytestring = ""; $len = strlen($bytes); for($i = 0; $i < $len; $i++) { $bytestring = $bytestring . $bytes[$i]; // build the byte string } $base64string = base64_encode($bytestring); // base64 encode the byte string $base64string = rtrim($base64string, "="); // trim any base64 padding characters return $base64string; }
Thanks for David for the pointer.
Steve
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Online forums and learning are now in one easy-to-use experience.
By continuing, you accept the updated Community Terms of Use and acknowledge the Privacy Policy. Your public name, photo, and achievements may be publicly visible and available in search engines.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.