Showing All Versions of “Append Changes to Existing Text” in a Data View Web Part (DVWP)

While flying across the country to SPTechCon SFO 2011, I was going through some of my old “blog posts I should do sometime” lists and came across this cool trick. Brendan Horner (@hornerit) shared it with me months ago via Twitter and I socked it away for one of those extra moments. Well, I found that moment. It’s not something which hasn’t been written about before, but it’s something I’m asked about from time to time, and I never remember how to do it, so here you go.

imageYou’ve probably used the “Append Changes to Existing Text” trick in a list at least once. If not, here’s how it works. You have to have versioning turned on for the list for this to work. Then if you create a Multiple lines of text column, at the bottom of the settings you’ll see the option.

image

Setting the option to “Yes” lets you keep a sort of running log in that column. I’ve used it many times in tasks lists so that each person can add their $.02 during the process. Here’s what it might look like (with some silly test data):

One problem with this cool functionality is that in views, you don’t get to see the strings of text, just a link that takes you to the DispForm for the item. It’s a cumbersome UI choice, for sure.

So naturally, we’d like to be able to turn to a Data View Web Part (DVWP) to improve the user experience. To make this work, you need to add the following line into your XSL:

<SharePoint:AppendOnlyHistory FieldName="Comments" runat="server" ControlMode="Display" ItemId="{@ID}"/>

When you do this, you get a nice listing of the history, like this:

Much nicer! Thanks, Brendan.

Similar Posts

106 Comments

  1. I think your column has to reference:
    Fieldname=”@V3Comments”
    or at least that’s what seems to work for me. I’d hate to have told everyone the wrong thing to try during my #SPSEMEA demo. :P

  2. What amazing timing, thank you Marc! I needed to know how to do this today – I was trying to find other elements in the XML to get this data and was failing miserably.

    I had to make a couple of extra adjustments to my page to get this to work which I thought I’d share with you… My DVWP is in a page that’s not in the context of the List, and so returned an error “No item exists at [url]. It may have been deleted or renamed by another user”. The immediate fix for this is to add a query string parameter “List” with the List’s GUID to the URL such as
    List=%7B0E5228F9%2D6180%2D4EBA%2D996F%2D5A47B6BE0869%7D

    It’s also possible to add the ListId attribute to the node and put the list GUID in there. But my problem was that this page forms part of a site template and so I can’t hard-code GUIDs like this as they will fail when deployed.
    The solution is to get the List GUID dynamically. For me, this was to add another Data Source to the Aggregate Data Source:

    <SharePoint:SPDataSource runat="server" DataSourceMode="ListOfLists" ><SelectParameters>
        </SelectParameters>
    </SharePoint:SPDataSource>
    

    And

    <datasource name="AllLists" id="x" Type="SPList"/> 
    

    in the

    <Aggregate><concat>
    

    section.

    This will return all lists on the site it’s being run against – it’s not possible to give a CAML query to filter the returned values. Then extract the List GUID into an XSL variable and use this in the

    <SharePoint:AppendOnlyHistory>
    

    node:

    <xsl:variable name="MyListGUID" select="/dsQueryResponse/AllLists/Rows/Row[@__spTitle='MyListName']/@__spID"/>
    <SharePoint:AppendOnlyHistory ListId="{$MyListGUID}" FieldName="Comments" runat="server" ControlMode="Display" ItemId="{@ID}"/>
    

    Job done!

    1. I also used the following jQuery code to disable the hyperlinks on the comment author and dates:

      // for the append-only comments, disable the click-to-view author and date
      $("a[href*='_layouts/userdisp.aspx?ID=']").each(function(){
      	$(this).closest("span").replaceWith($(this).html());
      });
      $("a[href*='/Lists/MyListName/DispForm.aspx?ID=']").each(function(){
      	$(this).replaceWith($(this).html());
      });
      
  3. I have tested this code on my XSL

    <SharePoint:AppendOnlyHistory FieldName="Comments" runat="server" ControlMode="Display" ItemId="{@ID}"/>

    and I get the following error:

    Cannot create an object of type ‘System.Int32′ fromt its string representation ’6df9bd522-550-4a30-bc31-a4366832a87e’ for the ‘ItemId’ property.

    Is it right where I have inserted these code from Matt:

    <xsl:template name="FieldRef_Note_body.V3Comments" ddwrt:dvt_mode="body" match="FieldRef[@Name='V3Comments']" mode="Note_body" ddwrt:ghost="hide">
        			<xsl:param name="thisNode" select="."/>
        <div dir="{@Direction}" class="ms-rtestate-field">
          <SharePoint:AppendOnlyHistory FieldName="Comments" runat="server" ControlMode="Display" ItemId="{@ID}"/>
          <xsl:value-of select="$thisNode/@*[name()=current()/@Name]" disable-output-escaping="yes"/>
        </div>
      </xsl:template>

    Could anyone tell me what I do wrong?

    1. Steiner:

      Hmm. It’s hard to tell what might be going wrong here. How about posting more of the surrounding code over at Stump The Panel so that you can get some more eyes on it? One question: Is your column called “Comments”? Because that’s the FieldName you are using in the SharePoint:AppendOnlyHistory. If that’s not right, it’ll throw an error.

      M.

  4. I spent over an hour trying to figure out why this wasn’t working for me… Then I figured it out.

    The xls uses _x0020_ when there is a space in the column name – my column name is “Project Notes” which in the default xls looks like this {@Project_x0020_Notes} – so I copied and pasted that into your line (replacing FieldName=”Comments” with FieldName=”{@Project_x0020_Notes}”).

    When that didn’t work – I took off the brackets – then I removed the @ sign – same error (referencing an object not part of the set or something like that). Then as a last ditch effort I removed the _x0020_ and replaced it with the space – BAM! it worked!

    The moral of the story – use the friendly sharepoint name of the column.

  5. I know this is kind of an old post but you guys seem to have some answers. I am able to get that view to work if I am using only one data source. How do you accomplish this in a joined dataview? When I attempt this using the I get the following error. Error Rendering Control – Unamed1 An unhandled exception has occured. Operation is not valid due to the current state of the object.

    1. Scott:

      I don’t think I’ve ever tried this with an AggregateDataSource, so I don’t have an easy answer for you. i’d like to think it’s possible, but it may take some fiddling.

      M.

    2. You may want to use:

      /dsQueryResponse/YourDataSource/Rows/Row[@ID = $dvt_ParentRow]/@ID

      instead of the normal {@ID} . Obviously, I don’t know how you set up your connection between your data sources, but that should be close to the OOTB version. It might work better if you stored the above as a variable in the row iteration and then did

      Haven’t tested this, but it may work. Let me know if it does :)

    3. I got this working for a DVWP in MOSS, with a linked (aggregate) data source, by using the following:

      <td class="ms-vb">
      <SharePoint:AppendOnlyHistory
      ListId="56001AD3-0580-45D6-9830-81A260BB6090" FieldName="Comments"
      runat="server" ControlMode="Display"  ItemId="{@ID}"
      id="AppendOnlyHistory1{generate-id()}"/>
      </td>
      

      When you are trying to render in a DVWP with a linked datasource, the ListID parameter lets it know which list the column is in. I am going to test in SP 2010 next.

  6. Well that idea didn’t work. I am noticing though that that field doesn’t show the last entry in an AggregateDataSource as it does in a stand alone data source. Not sure as to why though. That may be the reason why. If you get a chance to test something out on it let me know. I will keep messing my self and will post here if I find an answer. Thanks for the idea.

Leave a Reply to Larry Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.