Computing : Cloning a confluence page tree

Cloning a page confluence page tree (without content) is not too difficult using the confluence rest api. A simple bash script is pasted below. It requires jq to be installed (e.g. with pip https://pypi.org/project/jq/), and a few confluence functions to be present.

The sample script creates a copy of Services in Maxwell space to Services in a space named poio. The script surely is buggy and incomplete but might give an idea how to. Have a look at https://developer.atlassian.com/server/confluence/confluence-rest-api-examples/ for more and better examples. The python API (https://atlassian-python-api.readthedocs.io/confluence.html) would be an interesting alternative.

The sample script

#!/bin/bash
# confluence.functions can be found below
source $HOME/home-dir/confluence/confluence.functions
export CF_COOKIE=$HOME/.ssh/confluence/confluence.cookie

cf_authorize

#
#  recursively walk down a page tree and create the pages
#
treewalk() {
    local childs
    local pageID
    local child
    local n
    local parent
    local page
    local parentID

    page="$1"
    parent="$2"

    pageID=$( cf_get_pageID $spaceKey "$(echo "$page" | tr -d '"')" )
    parentID=$( cf_get_pageID $newSpace "$(echo "$parent" | tr -d '"')" )

    childs=$(curl -s -b $CF_COOKIE -X GET "https://confluence.desy.de/rest/api/content/$pageID/child/page?expand=children.page" | python -mjson.tool )

    n=0
    child="x"
    while [[ "$child" != "null" && "x$child" != "x" ]]; do
       child=$(echo "$childs" | jq ".results[$n].title")
       c="$(echo $child | tr -d '"')"
       if [[ "$child" != "null" ]] ; then
	     if [[ $parentID -eq 0 ]]; then
	       echo " creating top-level page $child"
	       cf_create_page $newSpace "$c" > /dev/null 2>&1
	     elif [[ $parentID -gt 0 ]]; then
	       echo " creating $child as child of $parent"
	       cf_create_child_page $newSpace $parentID "$c" > /dev/null 2>&1
	     fi
	     sleep 3 # might not be necessary ... 
         treewalk "$child" "$child"
       fi
       n=$(($n+1))
    done
}

#
#  original space: spaceKey
#  original page: oldParent
#
spaceKey=MXW
oldParent=Services
pageID=$( cf_get_pageID $spaceKey "$oldParent" )

if [[ "$pageID" == "null" || "$pageID" == "0" ]]; then
    echo " Page $oldParent does not exist in space $spaceKey"
    echo " Check space https://confluence.desy.de/display/$spaceKey"
    echo " abort"
    exit
fi

#
#  new space: newSpace
#  new toplevel page: newParent
#
newSpace=POIO
newParent=Services
spacePageID=$(cf_get_pageID $newSpace "" | awk '{print $1}')
newPageID=$( cf_get_pageID $newSpace "$newParent" )

if [[ "$newPageID" == "null" || "$newPageID" == "0" ]]; then # page doesn't exist yet, create it nested under toplevel page
    cf_create_child_page $newSpace $spacePageID "$newParent" > /dev/null 2>&1
    sleep 3 # just in case
    checkID=$( cf_get_pageID $newSpace "$newParent" )
    
    if [[ $checkID -eq 0 ]]; then
	  echo " Page creation for $newParent in $newSpace failed "
	  echo " abort"
    else
	  echo " Created page $newParent "
    fi
else
    echo " Page $newParent exists. PageID: $newPageID"
fi

treewalk "$oldParent" "$newParent"


exit



The confluence.functions simple utilities:

# simple confluence functions
#
#  check if jq exists
# 
which jq > /dev/null 2>&1
if [[ $? -gt 0 ]]; then
   echo "nothing done - install jq !"
   exit
fi

#
#  check if cookie is still valid; get new one if not
#  private_url must be an(y) existing page which requires a login to access!
#
private_url="https://confluence.desy.de/display/~schluenz/Infiniband"

function cf_authorize {
    test=$( curl -b $CF_COOKIE --silent "$private_url" | wc -l )
    
    if [[ $test -gt 20 ]]; then
	echo " Cookie still valid"
    else 
	echo " Cookie expired! "
	curl -u $USER -c  $CF_COOKIE "https://confluence.desy.de/dologin.action" -s --output /dev/null
    fi
}

#
#  get version of page, set to 0 if page doesn't exist
#
function cf_get_pageV {
    cfspaceKey=$1
    title=$(echo "$2" | sed 's| |%20|g')
    #title="$2"
    current=$(curl -s -b $CF_COOKIE -X GET "https://confluence.desy.de/rest/api/content?title=${title}&spaceKey=${cfspaceKey}&expand=version" | python -mjson.tool | jq '.results[].version.number')
    [ "x$current" == "x" ] && current=0
    echo $current

}

#
#  create new page
#
function cf_create_page {
    cfspaceKey=$1
    #title=$(echo "$2" | sed 's| |%20|g')
    title="$2"
    cat <<EOF>upload.tmp
   {"type":"page","title":"${title}","space":{"key":"${cfspaceKey}"},"body":{"storage":{"value":"","representation":"storage"}}}
EOF
    curl -s -b $CF_COOKIE -X POST -H 'Content-Type: application/json'  -d @upload.tmp "https://confluence.desy.de/rest/api/content" | python -mjson.tool

    }

#
#  create new child page  -not tested once!
#
function cf_create_child_page {
    cfspaceKey=$1
    parentID="$2"
    #title=$(echo "$3" | sed 's| |%20|g')
    title="$3"
    cat <<EOF>upload.tmp
   {"type":"page","title":"${title}","ancestors":[{"id":${parentID}}],"space":{"key":"${cfspaceKey}"},"body":{"storage":{"value":"","representation":"storage"}}}
EOF
    curl -s -b $CF_COOKIE -X POST -H 'Content-Type: application/json'  -d @upload.tmp "https://confluence.desy.de/rest/api/content" | python -mjson.tool

    }

#
#  get pageID, return 0 if page doesn't exist
#
function cf_get_pageID {
    cfspaceKey=$1
    #title="$2"
    title=$(echo "$2" | sed 's| |%20|g')	
    cfpageID=$(curl -s -b $CF_COOKIE -X GET "https://confluence.desy.de/rest/api/content?title=${title}&spaceKey=${cfspaceKey}&expand=version" | python -mjson.tool | jq '.results[].id' | tr -d '"' )
    [ "x$cfpageID" == "x" ] && cfpageID=0
    echo $cfpageID
}

#
#  upload a json-file
#
function cf_uploadF {
    cfpageID=$1
    file=$2
    curl -s -b $CF_COOKIE -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -d @${file} "https://confluence.desy.de/rest/api/content/${cfpageID}" | python -mjson.tool
}

#
#  convert html to json and upload
#
function cf_uploadFHTML {
    cfspaceKey=$1
    title="$2"
    cfpageID=$3
    newV=$4
    file=$5
    #  >&2 echo "$title $cfspaceKey $cfpageID $newV $file"
   
    cat <<EOF>upload.tmp
{"id":"${cfpageID}","type":"page", "title":"${title}","space":{"key":"${cfspaceKey}"},"body":{"storage":{"value":"
EOF
    cat $file >> upload.tmp
    cat <<EOF>>upload.tmp
","representation":"storage"}}, "version":{"number":${newV}}}
EOF
    cat upload.tmp  | tr -d '\n' > upload.tmp3

    cf_uploadF $cfpageID upload.tmp3
#    rm -f upload.tmp*

}

The output of the script:

 Cookie still valid
 Created page Services 
 creating "JupyterHub on Maxwell" as child of Services
 creating "JupyterHub development instance" as child of "JupyterHub on Maxwell"
 creating "Old JupyterHub on Maxwell" as child of "JupyterHub on Maxwell"
 creating "Standalone jupyter-notebooks" as child of "JupyterHub on Maxwell"
 creating "SLURM Reservations" as child of Services
 creating "SLURM REST API" as child of Services

The two page trees after running the script

Attachments:

page-clone.png (image/png)