Simple approvals

A simple script to mark a version of a page as approved
This groovy script is designed to be put into a user macro. It stores an approved version number for the page as a page property. If the displayed version is not the approved version, it renders a warning, and a link to the approved version. If the user is in a certain group it renders a link to allow the approval of the current page.
This is all to comply with ISO 9001 document control, which requires formal approval of all QMS doc and changes.

Why a script and not a plugin? Couldn't be bothered with the maven awfulness! If anyone wants to turn it into a proper plugin, be my guest.

Copy this code into a user macro, assuming the script plugin & groovy is installed.

{groovy:output=wiki}

def user = com.atlassian.confluence.user.AuthenticatedUserThreadLocal.user
def page = context.entity
def ua = bucket.container.ContainerManager.getComponent("userAccessor")
def group = ua.getGroup("QMSApprovers")
def cpm = bucket.container.ContainerManager.getComponent("contentPropertyManager")
def pm = bucket.container.ContainerManager.getComponent("pageManager")

def approveVersion = request.getParameter("approveVersion")

def propPage = page
if (!page.isLatestVersion())
  propPage = page.getLatestVersion()

String approvedVersion = cpm.getStringProperty(propPage,"approved.version")

// is the current user in the appropriate group?
boolean approver = ua.hasMembership(group,user)


// has the approve link been clicked?
if (approveVersion!=null && approver)
{
  approvedVersion = approveVersion
  cpm.setStringProperty(page,"approved.version",approveVersion)
}

//is this the approved version?
if (approvedVersion==null || !approvedVersion.equals( context.entity.version.toString() ) )
{
  // render a warning
  if (approvedVersion==null)
  {
    print "{warning:title=Warning}This document has not been formally approved."
  }
  else
  {
    print "{warning:title=Warning}This is not the currently approved version of this document."

    if (page.isLatestVersion() )
    {
      print " The approved version is *"
      def oldVersion = pm.getPageByVersion(page, new Integer(approvedVersion) )
      print "{html}<a href='"
      print contextPath
      print oldVersion.urlPath
      print "'>"
      print approvedVersion
      print "</a>.{html}* "
    }
  }

  // if user in the right group, render a link to allow approval
  if (approver)
  {
    println ""
    print "   {html}<a href='"
    print contextPath
    print page.urlPath
    print "?approveVersion="
    print page.version
    print "'>Approve this version<a>{html}"
  }

  println "{warning}"
}
else
{
  // approved version
  println "{tip}This is the formally approved version of this document.{tip}"
}

{groovy}
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Nov 05

    Justin Hickman says:

    It should be noted that you're using security by obscurity when it comes to appr...

    It should be noted that you're using security by obscurity when it comes to approving the page.   All it takes is someone to add:

    ?approveVersion=x
    
    
    

    to their URL, and it's "approved"

    This could be avoided by checking "hasMembership()" in the section where it's setting the page properties.

    1. Nov 05

      Jon Nermut says:

      feel free to fix it

      feel free to fix it

      1. Nov 05

        Alain Moran says:

        Gotta love open-source

        Gotta love open-source

  2. Nov 05

    Justin Hickman says:

    Have you thought of any way to make this global to a space?  You could add ...

    Have you thought of any way to make this global to a space?  You could add it to a template, but that requires the person to remember and use the template in order to get that ability.  It also is up to the editor to keep the macro in the page or not.   Having it outside of the page content can also help when it comes to exporting to PDF, et al.  You may not want the banner displayed in an export.

    I tried modifying my layout to do:

    #includePage($helper.spaceKey ".approvalInclude")
    

    That doesn't work as the approval will end up being on .approvalInclude rather than the page it's included on. This would require the macro to know about the page it's included in, rather than where the macro exists.

    1. Nov 06

      Jon Nermut says:

      It's a nasty hack, but you could add another line before your #includePage to pu...

      It's a nasty hack, but you could add another line before your #includePage to put the current page in a request attribute (its probably $page or $pageContext or something), then get it out again in the script.

    2. Nov 06

      Alain Moran says:

      try this #includePage("${helper.spaceKey}.approvalInclude")

      try this

      #includePage("${helper.spaceKey}.approvalInclude")
      
      1. Nov 06

        Justin Hickman says:

        From going through the Confluence Javadoc, I was able to figure out an easier so...

        From going through the Confluence Javadoc, I was able to figure out an easier solution:

        $helper.renderConfluenceMacro("{approval}")
        

        From initial tests, it does appear to be working correctly and against the current page.