← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 7083: Impl interpretation comments

 

------------------------------------------------------------
revno: 7083
committer: Lars Helge Overland <larshelge@xxxxxxxxx>
branch nick: dhis2
timestamp: Tue 2012-05-29 21:08:34 +0200
message:
  Impl interpretation comments
added:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/interpretation/InterpretationComment.java
  dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/interpretation/hibernate/InterpretationComment.hbm.xml
  dhis-2/dhis-support/dhis-support-test/src/main/java/org/hisp/dhis/mock/MockCurrentUserService.java
  dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/InterpretationController.java
  dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/jQuery/jquery.autogrow.js
modified:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/interpretation/Interpretation.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/interpretation/InterpretationService.java
  dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/interpretation/impl/DefaultInterpretationService.java
  dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/interpretation/hibernate/Interpretation.hbm.xml
  dhis-2/dhis-services/dhis-service-reporting/src/test/java/org/hisp/dhis/interpretation/InterpretationServiceTest.java
  dhis-2/dhis-support/dhis-support-test/src/main/java/org/hisp/dhis/DhisConvenienceTest.java
  dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/main.vm
  dhis-2/dhis-web/dhis-web-commons/src/main/java/org/hisp/dhis/interceptor/CacheInterceptor.java
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/interpretation/action/GetInterpretationsAction.java
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/interpretation.vm
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/javascript/interpretation.js
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/style/dashboard.css


--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/interpretation/Interpretation.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/interpretation/Interpretation.java	2012-05-29 12:04:07 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/interpretation/Interpretation.java	2012-05-29 19:08:34 +0000
@@ -27,7 +27,9 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.List;
 
 import org.hisp.dhis.chart.Chart;
 import org.hisp.dhis.common.BaseIdentifiableObject;
@@ -46,6 +48,8 @@
     private User user;
     
     private Date created;
+    
+    private List<InterpretationComment> comments = new ArrayList<InterpretationComment>();
 
     // -------------------------------------------------------------------------
     // Constructors
@@ -63,14 +67,15 @@
         this.created = new Date();
     }
 
-    public Interpretation( Chart chart, String text, User user )
+    // -------------------------------------------------------------------------
+    // Logic
+    // -------------------------------------------------------------------------
+
+    public void addComment( InterpretationComment comment )
     {
-        this.chart = chart;
-        this.text = text;
-        this.user = user;
-        this.created = new Date();
+        this.comments.add( comment );
     }
-
+    
     // -------------------------------------------------------------------------
     // Get and set methods
     // -------------------------------------------------------------------------
@@ -114,4 +119,14 @@
     {
         this.created = created;
     }
+
+    public List<InterpretationComment> getComments()
+    {
+        return comments;
+    }
+
+    public void setComments( List<InterpretationComment> comments )
+    {
+        this.comments = comments;
+    }
 }

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/interpretation/InterpretationComment.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/interpretation/InterpretationComment.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/interpretation/InterpretationComment.java	2012-05-29 19:08:34 +0000
@@ -0,0 +1,87 @@
+package org.hisp.dhis.interpretation;
+
+/*
+ * Copyright (c) 2004-2012, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the HISP project nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.util.Date;
+
+import org.hisp.dhis.common.BaseIdentifiableObject;
+import org.hisp.dhis.user.User;
+
+/**
+ * @author Lars Helge Overland
+ */
+public class InterpretationComment
+    extends BaseIdentifiableObject
+{
+    private String text;
+    
+    private User user;
+    
+    private Date created;
+    
+    public InterpretationComment()
+    {
+        this.created = new Date();
+    }
+    
+    public InterpretationComment( String text )
+    {
+        this.text = text;
+        this.created = new Date();
+    }
+
+    public String getText()
+    {
+        return text;
+    }
+
+    public void setText( String text )
+    {
+        this.text = text;
+    }
+
+    public User getUser()
+    {
+        return user;
+    }
+
+    public void setUser( User user )
+    {
+        this.user = user;
+    }
+
+    public Date getCreated()
+    {
+        return created;
+    }
+
+    public void setCreated( Date created )
+    {
+        this.created = created;
+    }
+}

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/interpretation/InterpretationService.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/interpretation/InterpretationService.java	2012-05-28 22:32:37 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/interpretation/InterpretationService.java	2012-05-29 19:08:34 +0000
@@ -43,4 +43,6 @@
     void deleteInterpretation( Interpretation interpretation );
     
     List<Interpretation> getInterpretations( int first, int max );
+    
+    void addInterpretationComment( String uid, String text );
 }

=== modified file 'dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/interpretation/impl/DefaultInterpretationService.java'
--- dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/interpretation/impl/DefaultInterpretationService.java	2012-05-29 12:04:07 +0000
+++ dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/interpretation/impl/DefaultInterpretationService.java	2012-05-29 19:08:34 +0000
@@ -27,17 +27,22 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+import java.util.Date;
 import java.util.List;
 
+import org.hisp.dhis.common.CodeGenerator;
 import org.hisp.dhis.common.GenericIdentifiableObjectStore;
 import org.hisp.dhis.interpretation.Interpretation;
+import org.hisp.dhis.interpretation.InterpretationComment;
 import org.hisp.dhis.interpretation.InterpretationService;
 import org.hisp.dhis.user.CurrentUserService;
 import org.hisp.dhis.user.User;
+import org.springframework.transaction.annotation.Transactional;
 
 /**
  * @author Lars Helge Overland
  */
+@Transactional
 public class DefaultInterpretationService
     implements InterpretationService
 {
@@ -94,4 +99,24 @@
     {
         return interpretationStore.getBetweenOrderderByLastUpdated( first, max );
     }
+    
+    public void addInterpretationComment( String uid, String text )
+    {
+        Interpretation interpretation = getInterpretation( uid );
+
+        User user = currentUserService.getCurrentUser();
+        
+        InterpretationComment comment = new InterpretationComment( text );
+        comment.setLastUpdated( new Date() );
+        comment.setUid( CodeGenerator.generateCode() );
+        
+        if ( user != null )
+        {
+            comment.setUser( user );
+        }
+        
+        interpretation.addComment( comment );
+        
+        interpretationStore.update( interpretation );
+    }
 }

=== modified file 'dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/interpretation/hibernate/Interpretation.hbm.xml'
--- dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/interpretation/hibernate/Interpretation.hbm.xml	2012-05-29 12:04:07 +0000
+++ dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/interpretation/hibernate/Interpretation.hbm.xml	2012-05-29 19:08:34 +0000
@@ -22,8 +22,15 @@
 
     <many-to-one name="user" class="org.hisp.dhis.user.User" column="userid"
 		foreign-key="fk_interpretation_userid" not-null="true" />
-		
+	
 	<property name="created" not-null="true" />
 
+	<list name="comments" table="interpretation_comments" cascade="all,delete-orphan">
+		<key column="interpretationid" foreign-key="fk_interpretation_comments_interpretationid" />
+		<list-index column="sort_order" base="1" />
+		<many-to-many class="org.hisp.dhis.interpretation.InterpretationComment" column="interpretationcommentid"
+			unique="true" foreign-key="fk_interpretation_comments_interpretationcommentid" />
+	</list>
+
   </class>
 </hibernate-mapping>
\ No newline at end of file

=== added file 'dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/interpretation/hibernate/InterpretationComment.hbm.xml'
--- dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/interpretation/hibernate/InterpretationComment.hbm.xml	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/interpretation/hibernate/InterpretationComment.hbm.xml	2012-05-29 19:08:34 +0000
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd";>
+
+<hibernate-mapping>
+  <class name="org.hisp.dhis.interpretation.InterpretationComment" table="interpretationcomment">
+
+    <cache usage="read-write" />
+
+    <id name="id" column="interpretationcommentid">
+      <generator class="native" />
+    </id>
+	
+    <property name="uid" column="uid" length="11" />
+    <property name="lastUpdated" type="timestamp"/>
+	
+    <property name="text" column="commenttext" type="text" />
+
+    <many-to-one name="user" class="org.hisp.dhis.user.User" column="userid"
+		foreign-key="fk_interpretation_userid" not-null="true" />
+		
+	<property name="created" not-null="true" />
+
+  </class>
+</hibernate-mapping>
\ No newline at end of file

=== modified file 'dhis-2/dhis-services/dhis-service-reporting/src/test/java/org/hisp/dhis/interpretation/InterpretationServiceTest.java'
--- dhis-2/dhis-services/dhis-service-reporting/src/test/java/org/hisp/dhis/interpretation/InterpretationServiceTest.java	2012-05-29 12:04:07 +0000
+++ dhis-2/dhis-services/dhis-service-reporting/src/test/java/org/hisp/dhis/interpretation/InterpretationServiceTest.java	2012-05-29 19:08:34 +0000
@@ -37,6 +37,8 @@
 import org.hisp.dhis.DhisSpringTest;
 import org.hisp.dhis.chart.Chart;
 import org.hisp.dhis.chart.ChartService;
+import org.hisp.dhis.mock.MockCurrentUserService;
+import org.hisp.dhis.user.CurrentUserService;
 import org.hisp.dhis.user.User;
 import org.hisp.dhis.user.UserService;
 import org.junit.Before;
@@ -72,12 +74,14 @@
         userA = createUser( 'A' );
         userService.addUser( userA );
         
+        setDependency( interpretationService, "currentUserService", new MockCurrentUserService( userA ), CurrentUserService.class );
+        
         chartA = new Chart( "ChartA" );
         chartService.saveChart( chartA );
         
-        interpretationA = new Interpretation( chartA, "Interpration of chart A", userA );
-        interpretationB = new Interpretation( chartA, "Interpration of chart B", userA );
-        interpretationC = new Interpretation( chartA, "Interpration of chart C", userA );
+        interpretationA = new Interpretation( chartA, "Interpration of chart A" );
+        interpretationB = new Interpretation( chartA, "Interpration of chart B" );
+        interpretationC = new Interpretation( chartA, "Interpration of chart C" );
     }
     
     @Test
@@ -136,4 +140,19 @@
         assertTrue( interpretations.contains( interpretationB ) );
         assertTrue( interpretations.contains( interpretationC ) );
     }
+    
+    @Test
+    public void testAddComment()
+    {
+        interpretationService.saveInterpretation( interpretationA );
+        String uid = interpretationA.getUid();        
+        assertNotNull( uid );
+        
+        interpretationService.addInterpretationComment( uid, "This interpretation is good" );
+        interpretationService.addInterpretationComment( uid, "This interpretation is bad" );
+        
+        interpretationA = interpretationService.getInterpretation( uid );
+        assertNotNull( interpretationA.getComments() );
+        assertEquals( 2, interpretationA.getComments().size() );
+    }
 }

=== modified file 'dhis-2/dhis-support/dhis-support-test/src/main/java/org/hisp/dhis/DhisConvenienceTest.java'
--- dhis-2/dhis-support/dhis-support-test/src/main/java/org/hisp/dhis/DhisConvenienceTest.java	2012-05-29 15:37:40 +0000
+++ dhis-2/dhis-support/dhis-support-test/src/main/java/org/hisp/dhis/DhisConvenienceTest.java	2012-05-29 19:08:34 +0000
@@ -98,6 +98,8 @@
 import org.hisp.dhis.validation.ValidationRule;
 import org.hisp.dhis.validation.ValidationRuleGroup;
 import org.hisp.dhis.validation.ValidationRuleService;
+import org.springframework.aop.framework.Advised;
+import org.springframework.aop.support.AopUtils;
 
 /**
  * @author Lars Helge Overland
@@ -314,7 +316,9 @@
     protected void setDependency( Object targetService, String fieldName, Object dependency, Class<?> clazz )
     {
         try
-        {
+        {            
+            targetService = getRealObject( targetService );
+            
             String setMethodName = "set" + fieldName.substring( 0, 1 ).toUpperCase()
                 + fieldName.substring( 1, fieldName.length() );
 
@@ -330,6 +334,24 @@
         }
     }
 
+    /**
+     * If the given class is advised by Spring AOP it will return the target class,
+     * i.e. the advised class. If not the given class is returned unchanged.
+     * 
+     * @param object the object.
+     */
+    @SuppressWarnings("unchecked")
+    private <T> T getRealObject( T object )
+        throws Exception
+    {
+        if ( AopUtils.isAopProxy( object ) )
+        {
+            return (T) ((Advised) object).getTargetSource().getTarget();
+        }
+        
+        return object;
+    }
+    
     // -------------------------------------------------------------------------
     // Create object methods
     // -------------------------------------------------------------------------

=== added file 'dhis-2/dhis-support/dhis-support-test/src/main/java/org/hisp/dhis/mock/MockCurrentUserService.java'
--- dhis-2/dhis-support/dhis-support-test/src/main/java/org/hisp/dhis/mock/MockCurrentUserService.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-support/dhis-support-test/src/main/java/org/hisp/dhis/mock/MockCurrentUserService.java	2012-05-29 19:08:34 +0000
@@ -0,0 +1,66 @@
+package org.hisp.dhis.mock;
+
+/*
+ * Copyright (c) 2004-2012, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the HISP project nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.hisp.dhis.user.CurrentUserService;
+import org.hisp.dhis.user.User;
+
+public class MockCurrentUserService
+    implements CurrentUserService
+{
+    private User currentUser;
+    
+    public MockCurrentUserService( User currentUser )
+    {
+        this.currentUser = currentUser;
+    }
+    
+    @Override
+    public String getCurrentUsername()
+    {
+        return currentUser.getUsername();
+    }
+
+    @Override
+    public User getCurrentUser()
+    {
+        return currentUser;
+    }
+
+    @Override
+    public boolean currentUserIsSuper()
+    {
+        return true;
+    }
+
+    @Override
+    public void clearCurrentUser()
+    {
+        currentUser = null;
+    }
+}

=== added file 'dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/InterpretationController.java'
--- dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/InterpretationController.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/InterpretationController.java	2012-05-29 19:08:34 +0000
@@ -0,0 +1,62 @@
+package org.hisp.dhis.api.controller;
+
+/*
+ * Copyright (c) 2004-2012, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the HISP project nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.hisp.dhis.api.utils.ContextUtils;
+import org.hisp.dhis.interpretation.InterpretationService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+/**
+ * @author Lars Helge Overland
+ */
+@Controller
+@RequestMapping( value = InterpretationController.RESOURCE_PATH )
+public class InterpretationController
+{
+    public static final String RESOURCE_PATH = "/interpretations";
+    
+    @Autowired
+    private InterpretationService interpretationService;
+    
+    @RequestMapping( value = "/{uid}/comment", method = RequestMethod.POST )
+    public void postComment( @PathVariable( "uid" ) String uid, @RequestBody String text, HttpServletResponse response ) throws IOException
+    {
+        interpretationService.addInterpretationComment( uid, text );
+        
+        ContextUtils.okResponse( response, "Comment created" );        
+    }
+}

=== added file 'dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/jQuery/jquery.autogrow.js'
--- dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/jQuery/jquery.autogrow.js	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/jQuery/jquery.autogrow.js	2012-05-29 19:08:34 +0000
@@ -0,0 +1,46 @@
+(function($) {
+
+    /*
+     * Auto-growing textareas; technique ripped from Facebook
+     */
+    $.fn.autogrow = function(options) {
+        
+        this.filter('textarea').each(function() {
+            
+            var $this       = $(this),
+                minHeight   = $this.height(),
+                lineHeight  = $this.css('lineHeight');
+            
+            var shadow = $('<div></div>').css({
+                position:   'absolute',
+                top:        -10000,
+                left:       -10000,
+                width:      $(this).width(),
+                fontSize:   $this.css('fontSize'),
+                fontFamily: $this.css('fontFamily'),
+                lineHeight: $this.css('lineHeight'),
+                resize:     'none'
+            }).appendTo(document.body);
+            
+            var update = function() {
+                
+                var val = this.value.replace(/</g, '&lt;')
+                                    .replace(/>/g, '&gt;')
+                                    .replace(/&/g, '&amp;')
+                                    .replace(/\n/g, '<br/>');
+                
+                shadow.html(val);
+                $(this).css('height', Math.max(shadow.height() + 20, minHeight));
+            }
+            
+            $(this).change(update).keyup(update).keydown(update);
+            
+            update.apply(this);
+            
+        });
+        
+        return this;
+        
+    }
+    
+})(jQuery);
\ No newline at end of file

=== modified file 'dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/main.vm'
--- dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/main.vm	2012-04-27 17:03:51 +0000
+++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/main.vm	2012-05-29 19:08:34 +0000
@@ -32,6 +32,7 @@
     <script type="text/javascript" src="../dhis-web-commons/javascripts/jQuery/jquery.glob.js"></script>
     <script type="text/javascript" src="../dhis-web-commons/javascripts/jQuery/jquery.date.js"></script>
     <script type="text/javascript" src="../dhis-web-commons/javascripts/jQuery/jquery.tmpl.js"></script>
+    <script type="text/javascript" src="../dhis-web-commons/javascripts/jQuery/jquery.autogrow.js"></script>
     <script type="text/javascript" src="../dhis-web-commons/i18nJavaScript.action"></script>
     <script type="text/javascript" src="../dhis-web-commons/messagesJavaScript.action"></script>
     <script type="text/javascript" src="../dhis-web-commons/javascripts/commons.js"></script>

=== modified file 'dhis-2/dhis-web/dhis-web-commons/src/main/java/org/hisp/dhis/interceptor/CacheInterceptor.java'
--- dhis-2/dhis-web/dhis-web-commons/src/main/java/org/hisp/dhis/interceptor/CacheInterceptor.java	2012-03-06 09:28:52 +0000
+++ dhis-2/dhis-web/dhis-web-commons/src/main/java/org/hisp/dhis/interceptor/CacheInterceptor.java	2012-05-29 19:08:34 +0000
@@ -53,7 +53,7 @@
     {
         HttpServletResponse response = ServletActionContext.getResponse();
         
-        response.setHeader( "Cache-Control", "max-age=" + seconds );
+        response.setHeader( "Cache-Control", "public, max-age=" + seconds );
         
         return invocation.invoke();
     }

=== modified file 'dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/interpretation/action/GetInterpretationsAction.java'
--- dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/interpretation/action/GetInterpretationsAction.java	2012-05-29 12:04:07 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/interpretation/action/GetInterpretationsAction.java	2012-05-29 19:08:34 +0000
@@ -1,5 +1,32 @@
 package org.hisp.dhis.dashboard.interpretation.action;
 
+/*
+ * Copyright (c) 2004-2012, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the HISP project nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
 import java.util.List;
 
 import org.hisp.dhis.interpretation.Interpretation;
@@ -7,6 +34,9 @@
 
 import com.opensymphony.xwork2.Action;
 
+/**
+ * @author Lars Helge Overland
+ */
 public class GetInterpretationsAction
     implements Action
 {

=== modified file 'dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/interpretation.vm'
--- dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/interpretation.vm	2012-05-29 12:04:07 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/interpretation.vm	2012-05-29 19:08:34 +0000
@@ -6,15 +6,49 @@
 <div id="interpretationFeed">
 #foreach( $ip in $interpretations )
 <div class="interpretation">
-	<div class="interpretationName"><span class="bold" style="cursor:pointer" onclick="showUserInfo( '${ip.user.id}' )">${ip.user.name}</span>&nbsp; <span class="grey">${format.formatDate( $ip.created )}</span></div>
-	<div class="interpretationText">${ip.text}</div>
+
+	<div class="interpretationName">
+		<span class="bold" style="cursor:pointer" onclick="showUserInfo( '${ip.user.id}' )">${ip.user.name}</span>&nbsp;
+		<span class="grey">${format.formatDate( $ip.created )}</span>
+	</div>
+	<div class="interpretationText">
+		${ip.text}
+	</div>
 	<div class="interpretationChart">
 		<img style="cursor:pointer" 
 		     src="../api/charts/${ip.chart.uid}/data?date=${format.formatDate( $ip.created )}&width=530&height=300" 
-		     onclick="javascript:window.location.href='../dhis-web-visualizer/app/index.html?id=${ip.chart.uid}&date=${format.formatDate( $ip.created )}'">
-	</div>
+		     onclick="javascript:window.location.href='../dhis-web-visualizer/app/index.html?id=${ip.chart.uid}&date=${format.formatDate( $ip.created )}'"
+		     title="Click to view in Data Visualizer">
+	</div>
+	
+	#set( $comments = $ip.comments )
+	<div class="interpretationCommentArea">
+		<div id="comments${ip.uid}">
+		#foreach( $comment in $comments )
+		<div>
+			<div class="interpretationName">
+				<span class="bold pointer" onclick="showUserInfo( '${comment.user.id}' )">${comment.user.name}</span>&nbsp;
+				<span class="grey">${format.formatDate( $comment.created )}</span>
+			</div>
+		</div>
+		<div class="interpretationText">
+			${comment.text}
+		</div>
+		#end
+		</div>
+		<textarea id="commentArea${ip.uid}" class="commentArea" placeholder="Add a comment..."></textarea>
+		<input type="button" class="commentButton" value="Post comment" onclick="postComment( '${ip.uid}' )">
+	</div>
+	
 </div>
 #end
 </div>
 
-<div id="userInfo" class="page"></div>
\ No newline at end of file
+<div id="userInfo" class="page"></div>
+
+<script type="text/javascript">
+var currentUser = {
+	id: "${currentUser.id}",
+	name: "${currentUser.name}"
+};	
+</script>

=== modified file 'dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/javascript/interpretation.js'
--- dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/javascript/interpretation.js	2012-05-29 12:04:07 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/javascript/interpretation.js	2012-05-29 19:08:34 +0000
@@ -1,11 +1,39 @@
 
+$( document ).ready( function() {
+	$( '.commentArea' ).autogrow();
+} );
+
 function showUserInfo( id )
 {
 	$( "#userInfo" ).load( "../dhis-web-commons-ajax-html/getUser.action?id=" + id );
 	$( "#userInfo" ).dialog( {
-	        modal : true,
-	        width : 350,
-	        height : 350,
-	        title : "User"
-	    } );
+		modal : true,
+		width : 350,
+		height : 350,
+		title : "User"
+	} );
+}
+
+function postComment( uid )
+{	
+	var text = $( "#commentArea" + uid ).val();
+	
+	var url = "../api/interpretations/" + uid + "/comment";
+	
+	var created = getCurrentDate();
+	
+	$.ajax( url, {
+		type: "POST",
+		contentType: "text/html",
+		data: text,
+		success: function() {			
+			var template = 
+				"<div><div class=\"interpretationName\"><span class=\"bold pointer\" " +
+				"onclick=\"showUserInfo( \'${userId}\' )\">${userName}<\/span>&nbsp; " +
+				"<span class=\"grey\">${created}<\/span><\/div><\/div>" +
+				"<div class=\"interpretationText\">${text}<\/div>";
+			
+			$.tmpl( template, { "userId": currentUser.id, "userName": currentUser.name, created: created, text: text } ).appendTo( "#comments" + uid );
+		}		
+	} );
 }
\ No newline at end of file

=== modified file 'dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/style/dashboard.css'
--- dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/style/dashboard.css	2012-05-29 12:04:07 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/style/dashboard.css	2012-05-29 19:08:34 +0000
@@ -144,13 +144,14 @@
 
 .interpretation
 {
+  width: 550px;
   border-bottom: 1px solid #ccc;
-  padding: 22px;
+  padding: 22px 10px 16px 10px;
 }
 
 .interpretationName
 {
-  margin-bottom: 8px;
+  margin-bottom: 7px;
 }
 
 .interpretationText
@@ -160,5 +161,38 @@
 
 .interpretationChart
 {
+  margin-bottom: 12px;
+}
+
+.interpretationCommentArea
+{
+  margin-top: 10px;
+  padding: 18px 20px 14px 20px;
+  background-color: #f2f3f4;
+  border: 1px solid #d5d5d5;
+  border-radius: 3px;
+}
+
+.commentArea
+{
+  height: 18px;
+  width: 370px;
+  border: 1px solid #ccc;
+  resize: none;
+  overflow: auto;
+  padding: 4px 0px 0px 6px;
+}
+
+.commentButton
+{
+  width: 120px;
+  height: 24px;
+  margin: 1px 0 0 5px;
+  float: right;
+}
+
+.pointer
+{
+  cursor:pointer;
 }