wissel.net

Usability - Productivity - Business - The web - Singapore & Twins

Attachment URLs in XPages


In classic Domino applications directly addressing attachments is well known and simple. Your URL has the format:
http(s)://[yourserver]/[application.nsf]/[viewname|0]/[UNID| ViewKey]/$File/[AttachmentName]?Open
While this will continue to work on the Domino server for the forseeable future you want to learn about the XPages syntax. The very obvious reason: the classic Domino URL doesn't work in the Notes client (XPiNC) and you want to make your applications work cross-platform don't you? Also we don't know what additional options handling attachments via XPages might bring in the future. the syntax is a litte more complex.:
http(s)://[yourserver]/[application.nsf] /xsp/.ibmmodres/domino/OpenAttachment/ [application.nsf]/[UNID|/$File/[AttachmentName]?Open
A few remarks:
  • The URL contains the application location twice. From my tests it looks like the /__[ReplicaID].nsf/ doesn't work for here. I haven't tested what happens when you specify 2 different applications in the front and the back
  • Since there is no view involved in the URL addressing attachments using an attachment key won't work. You have to use the UNID. This requires extra care when you build applications where the attachment URL is supposed to be shared (like send in a chat or an eMail). Deleting a document and recreating with an updated attachment will alter the URL. So ou want to cater for that case with a specific XPage rather than a raw URL.
  • When you attachment is stored inside a RT field (e.g. using the upload control where you target a field) you can replace "/$File/" with the field name e.g. "/Body/"
To make my life easier I use 2 functions so I get an attachment URL with a single call:
  1. function getAttachmentURL (docID :java. lang. String , attachmentName :java. lang. String ) {
  2.     var base = getBaseURL ( ) ;
  3.     var middle = "/xsp/.ibmmodres/domino/OpenAttachment" ;
  4.     if (base. substr ( 0 , 4 ) == "/xsp" ) {
  5.         middle += base. substr ( 4 ) ;
  6.     } else {
  7.         middle += base ;
  8.     }
  9.     var result = base + middle + "/" + docID + "/$File/" + attachmentName + "?Open" ;
  10.     return result ;
  11. }
  12.  
  13. function getBaseURL ( ) {
  14.     var curURL = context. getUrl ( ) ;
  15.     var curAdr = curURL. getAddress ( ) ;
  16.     var rel = curURL. getSiteRelativeAddress (context ) ;
  17.     var step1 = curAdr. substr ( 0 ,curAdr. indexOf (rel ) ) ;
  18.    
  19.     // Now cut off the http
  20.     var step2 = step1. substr (step1. indexOf ( "//" ) + 2 ) ;
  21.     var result = step2. substr (step2. indexOf ( "/" ) ) ;
  22.     return result ;
  23.    
  24.     }
As usual YMMV

Posted by on 24 June 2010 | Comments (1a) | categories: XPages

Comments

  1. posted by Peter Presnell on Friday 25 June 2010 AD:
    Thx for taking the time to point this out Stephan. I don't recall my XPage Kindergarten teacher ever covering this!
  2. posted by Glen Mitchell on Saturday 26 June 2010 AD:
    That's cool, if only there was a way to get the Notes client to open a file automatically, rather than only asking to save it.
  3. posted by Fredrik Sjöström on Saturday 26 June 2010 AD:
    Great post, Stephan!
  4. posted by Mark Leusink on Monday 30 August 2010 AD:
    Note that with the XPage syntax for attachments, the server adds a Content-Disposition header that will force the browser to offer the user the choice of directly opening or saving the attachment.

    In the classic format the server doesn't add the header which causes the attachment to be "launched": if you have Adobe Reader installed and click a link to a PDF, the PDF will be shown directly in the browser window (in IE)

    I wonder if it's possible to override this behaviour in the new syntax. Perhaps by adding an extra parameter?

    Mark
  5. posted by ljubisa on Wednesday 15 February 2012 AD:
    Excuse me for this dumb question, but how do i make use of this in my project?
  6. posted by Stephan H. Wissel on Wednesday 15 February 2012 AD:
    Stuff it into a SSJS lib and call it from your XPage where you e.g. use a view or a repeat control to compute the content. Like this:
    <?xml version="1.0" encoding="UTF-8"?>
    <xp:view xmlns:xp="http://www.ibm.com/xsp/core">
        <xp:this.resources>
            <xp:script src="/AttachmentTools.jss" clientSide="false"></xp:script>
        </xp:this.resources>
        <xp:viewPanel rows="30" id="viewPanel1" viewStyle="width:40.0%"
            var="curRow">
            <xp:this.facets>
                <xp:pager partialRefresh="true" layout="Previous Group Next"
                    xp:key="headerPager" id="pager1">
                </xp:pager>
            </xp:this.facets>
            <xp:this.data>
                <xp:dominoView var="view1" viewName="withAttachments"></xp:dominoView>
            </xp:this.data>
            <xp:viewColumn columnName="$120" id="viewColumn1">
                <xp:viewColumnHeader value="Topic" id="viewColumnHeader1"></xp:viewColumnHeader>
            </xp:viewColumn>
            <xp:viewColumn id="viewColumn2" contentType="HTML">
                <xp:this.facets>
                    <xp:viewColumnHeader xp:key="header"
                        id="viewColumnHeader2" value="Attachments">
                    </xp:viewColumnHeader>
                </xp:this.facets>
            <xp:this.value> <![CDATA[#{javascript:var id:string = curRow.getUniversalID();
    var attNames = curRow.getColumnValue("AttachmentNames");
    var u = getAttachmentURL(id,attNames);
    return "<a href=\""+u+"\">"+attNames+"</a>"}]]> </xp:this.value></xp:viewColumn>
        </xp:viewPanel>
    </xp:view>
     
    You need to tweak the function if you expect more than one attachment in a document. The trick is the column "AttachmentNames" in the view is computed as @AttachmentNames
  7. posted by ljubisa on Thursday 16 February 2012 AD:
    Why, thank you good sir! :)
  8. posted by Ian on Monday 26 March 2012 AD:
    Many thanks.
  9. posted by Randal Oulton on Tuesday 04 September 2012 AD:
    Geez, they couldn't if they'd tried have picked a syntax that Google's gonna hate more for page ranking scores, could they? I suppose they didn't even *think* of that.
  10. posted by Stephan H. Wissel on Tuesday 04 September 2012 AD:
    @Randal: the web sites configuration documents are your friends. You can mask the URL to something leaner easily
    Emoticon biggrin.gif
  11. posted by Frank van der Linden on Friday 21 September 2012 AD:
    Thanks for the functions. Saves me lots of time.
    I transformed the SSJS functions to Java methods in my JSFUtil class, so I can use it in my beans to calculate the attachment url's
  12. posted by Ryan Buening on Friday 02 November 2012 AD:
    Thanks for this, Stephan.
  13. posted by Arne Menting on Friday 09 November 2012 AD:
    Thanks, that nudged me in the right direction. If you want to open an attachment in another database you should pay attention which database is which, and that you can be local, and have to specify the current app as the starting point:

    http(s)://127.0.0.1:[theRandomXPINCPort]/xsp/[currentServer]!![yourCurrentApplication.nsf] /xsp/.ibmmodres/domino/OpenAttachment/[yourTargetServer]!![yourTargetapplication.nsf]/[UNID]/$File/[AttachmentName]?Open
  14. posted by Randal Oulton on Friday 30 November 2012 AD:
    "@Randal: the web sites configuration documents are your friends. You can mask the URL to something leaner easily"

    Oh I get that, believe me I know substitutions and redirections up the whazoo and all their failings. I'm just saying, someone at the very start shoulda thunk and went, this is so not gonna fly with anyone who cares about SEO (which would be everyone lol.)

    SEO and look is what people care about now on websites, SEO and look. If you can't sell that, you ain't gonna be programing a website in *any* language.
  15. posted by Rakesh Lakshmanan on Thursday 17 January 2013 AD:
    Hi,

    I have tried the above method its opening the attachment URL but its not prompting for the attachment download.I am using IE8.I enabled alllow the pop-up still not working Emoticon sad.gif .. any idea why its not working..??
  16. posted by Stephan H. Wissel on Thursday 17 January 2013 AD:
    @Rakesh,
    post that question on stackoverflow, include your sample. Test with wget or curl to exclude a network issue. Make sure that you state the error message you get. If the download contol works, this works in all cases when done correctly.
  17. posted by Rakesh Lakshmanan on Friday 18 January 2013 AD:
    @Stephan,
    Thanks for the reply.my code sample is:
    view.postScript("window.open('http://localhost/learning.nsf/0/"+ID+"/$FILE/export"+sessID+".xls')");

    link generated : http://localhost/learning.nsf/3acc399c5fa49b8065257ae300646bc2/73c6511b3295fb0365257af60047977b/$FILE/exportDDIT2L2F58.xls

    I can download the attachment from notes client not from browser..
  18. posted by Stephan H. Wissel on Friday 18 January 2013 AD:
    @Rakesh: This is not Stackoverflow. Also who produces popups, distributes popups or makes popups available to others shall be punished with the requirement to code for IE6 in HTML5. Emoticon biggrin.gif

    1) Do not use popups, don't, never!!
    2) Do not use popups, don't, never!!

    In case you ignore 1 or 2, the popup blocker will teach you.

    Also the link generated can't be from your formula since it doesn't contain the /0/ and it isn't XPages related - see article.
  19. posted by andy on Thursday 31 January 2013 AD:
    I've got it to work when I hard code the UNID, but I don't know how to get the UNID in a Repeat Control? My Collection name is: reportData.

    How to I get the backend doc unid where the attachment is saved?

    I'm using a link to launch my attachment with this client side javascript:

    return location.href = url;

    Please help!

    p.s I hate XPages


  20. posted by Stephan H. Wissel on Thursday 31 January 2013 AD:
    @Andy: there are nice books about PHP, go and have fun. Just explain the logic to me: you have a problem with a technology, failed to RTFM to find the getUniversalId() function, state that you hate the technology and still expect an expert (who probably became an expert since he likes the technology) to bail you out?
    Emoticon angry.gif
  21. posted by andy on Friday 01 February 2013 AD:
    I have a repeat control that displays response documents. That's working. There is one attachment per response document that has been created by an agent. There's no file upload control involved. I want to display a link called 'Open' for each response, and I want to give users the option of opening/saving the attachment when they click on it.

    I got this working by hard coding a UNID in the Client side OnClick event of the link Control.

    var url = "{ Link }
    return location.href = url;

    I have the above urls in a column in a view, but I just can't get it to work? I doubt getUniversalId() works in client side? Emoticon huh.gif
  22. posted by andy on Friday 01 February 2013 AD:
    I have a repeat control that displays response documents. That's working. There is one attachment per response document that has been created by an agent. There's no file upload control involved. I want to display a link called 'Open' for each response, and I want to give users the option of opening/saving the attachment when they click on it.<br /><br />I got this working by hard coding a UNID in the Client side OnClick event of the link Control.<br /><br />var url = "{ <a href="{ Link } rel="nofollow" target ="blank">Link</a> }<br />return location.href = url;<br /><br />I have the above urls in a column in a view, but I just can't get it to work? I doubt getUniversalId() works in client side? Emoticon huh.gif
  23. posted by Jairo on Thursday 11 July 2013 AD:
    found this very short code to get the same link for the attachment:

    doc.getAttachmentList("fieldName").get(0).getHref()

    doc : NotesDocument
    "fieldName" : field name

    this is from
    { Link } , answer by Grant Lindsay

  24. posted by Bill Fox on Saturday 27 September 2014 AD:
    I have a situation where the database where the code is run from is different than the database where the data resides. In your example I believe the first reference is to the DB where the code is run and the second refers to the database where the data resides. I copied the link for the save and it looks like this:
    { Link }

    So I suspect that I will need to modify your code to cover where the code and the data reside in different DBs. I have not got this working yet but believe that is why the database is stated twice.
  25. posted by alan harris on Wednesday 14 October 2015 AD:
    truly exceptional. no need for the dumba$$ download control EVER again. Emoticon biggrin.gif
  26. posted by Patrick Kwinten on Tuesday 06 September 2016 AD:
    how do calculate the path in java classes?