What is the algorithm used to create the "Tiny links"

danielss
I'm New Here
I'm New Here
Those new to the Atlassian Community have posted less than three times. Give them a warm welcome!
September 17, 2012

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

8 answers

1 accepted

3 votes
Answer accepted
David at David Simpson Apps
Marketplace Partner
Marketplace Partners provide apps and integrations available on the Atlassian Marketplace that extend the power of Atlassian products.
September 17, 2012

Check out the source of com.atlassian.confluence.pages.TinyUrl...

Base64.encodeBase64(longToByteArray(id));

id is the pageId.

2 votes
cloudbolt_sam
I'm New Here
I'm New Here
Those new to the Atlassian Community have posted less than three times. Give them a warm welcome!
August 7, 2019

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.

Fabian Sturm
I'm New Here
I'm New Here
Those new to the Atlassian Community have posted less than three times. Give them a warm welcome!
October 24, 2019

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.

Like cloudbolt_sam likes this
Sebastian Schmachtel
I'm New Here
I'm New Here
Those new to the Atlassian Community have posted less than three times. Give them a warm welcome!
October 25, 2019

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
Like cloudbolt_sam likes this
1 vote
Tilman Schweitzer
I'm New Here
I'm New Here
Those new to the Atlassian Community have posted less than three times. Give them a warm welcome!
March 27, 2019

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()}));
}

1 vote
Mircea Vutcovici December 13, 2013

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];
}

0 votes
Maxime Boyer March 8, 2024

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"
0 votes
Sam Liddicott
I'm New Here
I'm New Here
Those new to the Atlassian Community have posted less than three times. Give them a warm welcome!
June 16, 2023

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%%=*}"
}
0 votes
N-A okuda August 11, 2013

Could you describe a method of decoding?

0 votes
danielss
I'm New Here
I'm New Here
Those new to the Atlassian Community have posted less than three times. Give them a warm welcome!
September 17, 2012

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 &lt; $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

Suggest an answer

Log in or Sign up to answer
TAGS
AUG Leaders

Atlassian Community Events