← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 4256: Refactored message functionality. Messages now based on conversations. Added function for replyin...

 

Merge authors:
  Lars Helge Øverland (larshelge)
------------------------------------------------------------
revno: 4256 [merge]
committer: Lars Helge Overland <larshelge@xxxxxxxxx>
branch nick: dhis2
timestamp: Thu 2011-08-04 14:12:49 +0200
message:
  Refactored message functionality. Messages now based on conversations. Added function for replying to a message within a conversation. Improved performance for getting relevant messages.
removed:
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/javascript/readMessage.js
added:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/Message.java
  dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/message/hibernate/MessageConversation.hbm.xml
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/SendReplyAction.java
renamed:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/Message.java => dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/MessageConversation.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/UserMessageStore.java => dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/MessageConversationStore.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/message/hibernate/HibernateUserMessageStore.java => dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/message/hibernate/HibernateMessageConversationStore.java
modified:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/MessageService.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/UserMessage.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/message/DefaultMessageService.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/startup/TableAlteror.java
  dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml
  dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/message/hibernate/Message.hbm.xml
  dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/message/hibernate/UserMessage.hbm.xml
  dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/message/MessageServiceTest.java
  dhis-2/dhis-support/dhis-support-hibernate/src/main/java/org/hisp/dhis/hibernate/HibernateGenericStore.java
  dhis-2/dhis-support/dhis-support-hibernate/src/main/resources/ehcache.xml
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/action/ProvideContentAction.java
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/GetMessagesAction.java
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/ReadMessageAction.java
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/RemoveMessageAction.java
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/SendFeedbackAction.java
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/SendMessageAction.java
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/UnreadMessageAction.java
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/resources/META-INF/dhis/beans.xml
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/resources/org/hisp/dhis/dashboard/i18n_module.properties
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/resources/struts.xml
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/javascript/message.js
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/message.vm
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/readMessage.vm
  dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/style/dashboard.css
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/MessageConversation.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/MessageConversationStore.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/message/hibernate/HibernateMessageConversationStore.java


--
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
=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/Message.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/Message.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/Message.java	2011-08-04 10:32:02 +0000
@@ -0,0 +1,113 @@
+package org.hisp.dhis.message;
+
+/*
+ * Copyright (c) 2004-2010, 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 java.util.UUID;
+
+import org.hisp.dhis.user.User;
+
+/**
+ * @author Lars Helge Overland
+ */
+public class Message
+{
+    private int id;
+
+    private String key;
+    
+    private String text;
+
+    private User sender;
+    
+    private Date sentDate;
+
+    public Message()
+    {
+        this.key = UUID.randomUUID().toString();
+        this.sentDate = new Date();
+    }
+    
+    public Message( String text, User sender )
+    {
+        this.key = UUID.randomUUID().toString();
+        this.text = text;
+        this.sender = sender;
+        this.sentDate = new Date();
+    }
+    
+    public int getId()
+    {
+        return id;
+    }
+
+    public void setId( int id )
+    {
+        this.id = id;
+    }
+
+    public String getKey()
+    {
+        return key;
+    }
+
+    public void setKey( String key )
+    {
+        this.key = key;
+    }
+
+    public String getText()
+    {
+        return text;
+    }
+
+    public void setText( String text )
+    {
+        this.text = text;
+    }
+
+    public User getSender()
+    {
+        return sender;
+    }
+
+    public void setSender( User sender )
+    {
+        this.sender = sender;
+    }
+
+    public Date getSentDate()
+    {
+        return sentDate;
+    }
+
+    public void setSentDate( Date sentDate )
+    {
+        this.sentDate = sentDate;
+    }
+}

=== renamed file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/Message.java' => 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/MessageConversation.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/Message.java	2011-04-01 19:47:09 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/MessageConversation.java	2011-08-04 10:45:31 +0000
@@ -27,7 +27,11 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+import java.util.ArrayList;
+import java.util.Date;
 import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Set;
 import java.util.UUID;
 
@@ -36,31 +40,114 @@
 /**
  * @author Lars Helge Overland
  */
-public class Message
+public class MessageConversation
 {
     private int id;
     
     private String key;
     
     private String subject;
-    
-    private String text;
 
-    private User sender;
-    
     private Set<UserMessage> userMessages = new HashSet<UserMessage>();
     
-    public Message()
+    private List<Message> messages = new ArrayList<Message>();
+
+    private Date lastUpdated;
+    
+    private User lastSender;
+    
+    private transient boolean read;
+    
+    private transient String lastSenderSurname;
+    
+    private transient String lastSenderFirstname;
+    
+    public MessageConversation()
     {
         this.key = UUID.randomUUID().toString();
+        this.lastUpdated = new Date();
     }
     
-    public Message( String subject, String text, User sender )
+    public MessageConversation( String subject, User lastSender )
     {
         this.key = UUID.randomUUID().toString();
         this.subject = subject;
-        this.text = text;
-        this.sender = sender;
+        this.lastUpdated = new Date();
+        this.lastSender = lastSender;
+    }
+    
+    public void addUserMessage( UserMessage userMessage )
+    {
+        this.userMessages.add( userMessage );
+    }
+    
+    public void addMessage( Message message )
+    {
+        this.messages.add( message );
+    }
+    
+    public void markRead( User user )
+    {
+        for ( UserMessage userMessage : userMessages )
+        {
+            if ( userMessage.getUser() != null && userMessage.getUser().equals( user ) )
+            {
+                userMessage.setRead( true );
+                
+                return;
+            }
+        }
+    }
+
+    public void markUnread( User user )
+    {
+        for ( UserMessage userMessage : userMessages )
+        {
+            if ( userMessage.getUser() != null && userMessage.getUser().equals( user ) )
+            {
+                userMessage.setRead( false );
+                
+                return;
+            }
+        }
+    }
+    
+    public void markReplied( User sender, Message message )
+    {   
+        for ( UserMessage userMessage : userMessages )
+        {
+            if ( userMessage.getUser() != null && !userMessage.getUser().equals( sender ) )
+            {
+                userMessage.setRead( false );
+            }
+        }
+        
+        addMessage( message );
+        
+        this.lastUpdated = new Date();
+        this.lastSender = sender;
+    }
+    
+    public void remove( User user )
+    {
+        Iterator<UserMessage> iterator = userMessages.iterator();
+        
+        while ( iterator.hasNext() )
+        {
+            UserMessage userMessage = iterator.next();
+            
+            if ( userMessage.getUser() != null && userMessage.getUser().equals( user ) )
+            {
+                iterator.remove();
+                
+                return;
+            }
+        }
+    }
+    
+    public String getLastSenderName()
+    {
+        return lastSenderFirstname + " " + lastSenderSurname;
     }
         
     public int getId()
@@ -93,26 +180,6 @@
         this.subject = subject;
     }
 
-    public String getText()
-    {
-        return text;
-    }
-
-    public void setText( String text )
-    {
-        this.text = text;
-    }
-
-    public User getSender()
-    {
-        return sender;
-    }
-
-    public void setSender( User sender )
-    {
-        this.sender = sender;
-    }
-
     public Set<UserMessage> getUserMessages()
     {
         return userMessages;
@@ -123,6 +190,66 @@
         this.userMessages = userMessages;
     }
 
+    public List<Message> getMessages()
+    {
+        return messages;
+    }
+
+    public void setMessages( List<Message> messages )
+    {
+        this.messages = messages;
+    }
+
+    public Date getLastUpdated()
+    {
+        return lastUpdated;
+    }
+
+    public void setLastUpdated( Date lastUpdated )
+    {
+        this.lastUpdated = lastUpdated;
+    }
+
+    public User getLastSender()
+    {
+        return lastSender;
+    }
+
+    public void setLastSender( User lastSender )
+    {
+        this.lastSender = lastSender;
+    }
+
+    public boolean isRead()
+    {
+        return read;
+    }
+
+    public void setRead( boolean read )
+    {
+        this.read = read;
+    }
+
+    public String getLastSenderSurname()
+    {
+        return lastSenderSurname;
+    }
+
+    public void setLastSenderSurname( String lastSenderSurname )
+    {
+        this.lastSenderSurname = lastSenderSurname;
+    }
+
+    public String getLastSenderFirstname()
+    {
+        return lastSenderFirstname;
+    }
+
+    public void setLastSenderFirstname( String lastSenderFirstname )
+    {
+        this.lastSenderFirstname = lastSenderFirstname;
+    }
+
     @Override
     public int hashCode()
     {
@@ -147,7 +274,7 @@
             return false;
         }
         
-        final Message other = (Message) object;
+        final MessageConversation other = (MessageConversation) object;
         
         return key.equals( other.key );
     }
@@ -155,6 +282,6 @@
     @Override
     public String toString()
     {
-        return "[Subject: " + subject + ", text: " + text + "]";
+        return "[" + subject + "]";
     }
 }

=== renamed file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/UserMessageStore.java' => 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/MessageConversationStore.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/UserMessageStore.java	2011-03-31 11:45:31 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/MessageConversationStore.java	2011-08-04 07:52:24 +0000
@@ -35,10 +35,10 @@
 /**
  * @author Lars Helge Overland
  */
-public interface UserMessageStore
-    extends GenericStore<UserMessage>
+public interface MessageConversationStore
+    extends GenericStore<MessageConversation>
 {
-    List<UserMessage> getUserMessages( User user, int first, int max );
+    List<MessageConversation> getMessageConversations( User user, int first, int max );
     
-    long getUnreadUserMessageCount( User user );
+    long getUnreadUserMessageConversationCount( User user );
 }

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/MessageService.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/MessageService.java	2011-06-08 22:04:42 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/MessageService.java	2011-08-04 07:52:24 +0000
@@ -39,25 +39,21 @@
 {
     final String ID = MessageService.class.getName();
     
-    int sendMessage( Message message, Set<User> users );
-    
-    int sendFeedback( Message message );
-    
-    int saveMessage( Message message );
-    
-    Message getMessage( int id );
-    
-    UserMessage getUserMessage( int id );
-    
-    void updateUserMessage( UserMessage userMessage );
-    
-    void deleteUserMessage( UserMessage userMessage );
-    
-    List<UserMessage> getUserMessages( int first, int max );
-    
-    List<UserMessage> getUserMessages( User user, int first, int max );
-    
-    long getUnreadMessageCount();
-    
-    long getUnreadMessageCount( User user );
+    int sendMessage( String subject, String text, Set<User> users );
+    
+    int sendFeedback( String subject, String text );
+    
+    void sendReply( MessageConversation conversation, String text );
+    
+    int saveMessageConversation( MessageConversation conversation );
+    
+    void updateMessageConversation( MessageConversation conversation );
+    
+    MessageConversation getMessageConversation( int id );
+    
+    long getUnreadMessageConversationCount();
+    
+    long getUnreadMessageConversationCount( User user );
+    
+    public List<MessageConversation> getMessageConversations( int first, int max );
 }

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/UserMessage.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/UserMessage.java	2011-03-31 15:54:20 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/message/UserMessage.java	2011-08-03 20:53:38 +0000
@@ -27,7 +27,7 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-import java.util.Date;
+import java.util.UUID;
 
 import org.hisp.dhis.user.User;
 
@@ -38,25 +38,22 @@
 {
     private int id;
     
+    private String key;
+    
     private User user;
     
-    private Message message;
-    
     private boolean read;
 
-    private Date messageDate;
-
     public UserMessage()
     {
-        this.messageDate = new Date();
+        this.key = UUID.randomUUID().toString();
     }
     
-    public UserMessage( User user, Message message )
+    public UserMessage( User user )
     {
+        this.key = UUID.randomUUID().toString();
         this.user = user;
-        this.message = message;
         this.read = false;
-        this.messageDate = new Date();
     }
     
     public int getId()
@@ -69,6 +66,16 @@
         this.id = id;
     }
 
+    public String getKey()
+    {
+        return key;
+    }
+
+    public void setKey( String key )
+    {
+        this.key = key;
+    }
+
     public User getUser()
     {
         return user;
@@ -79,16 +86,6 @@
         this.user = user;
     }
 
-    public Message getMessage()
-    {
-        return message;
-    }
-
-    public void setMessage( Message message )
-    {
-        this.message = message;
-    }
-
     public boolean isRead()
     {
         return read;
@@ -98,56 +95,10 @@
     {
         this.read = read;
     }
-
-    public Date getMessageDate()
-    {
-        return messageDate;
-    }
-
-    public void setMessageDate( Date messageDate )
-    {
-        this.messageDate = messageDate;
-    }
-
-    @Override
-    public int hashCode()
-    {
-        final int prime = 31;
-        
-        int result = 1;
-        
-        result = prime * result + message.hashCode();
-        result = prime * result + user.hashCode();
-        
-        return result;
-    }
-
-    @Override
-    public boolean equals( Object object )
-    {
-        if ( this == object )
-        {
-            return true;
-        }
-        
-        if ( object == null )
-        {
-            return false;
-        }
-        
-        if ( getClass() != object.getClass() )
-        {
-            return false;
-        }
-        
-        final UserMessage other = (UserMessage) object;
-        
-        return message.equals( other.message ) && user.equals( other.user );
-    }
     
     @Override
     public String toString()
     {
-        return "[User: " + user + ", message: " + message + ", read: " + read + "]";
+        return "[User: " + user + ", read: " + read + "]";
     }
 }

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/message/DefaultMessageService.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/message/DefaultMessageService.java	2011-08-03 10:05:08 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/message/DefaultMessageService.java	2011-08-04 10:45:31 +0000
@@ -31,7 +31,6 @@
 import java.util.List;
 import java.util.Set;
 
-import org.hisp.dhis.common.GenericStore;
 import org.hisp.dhis.configuration.ConfigurationService;
 import org.hisp.dhis.user.CurrentUserService;
 import org.hisp.dhis.user.User;
@@ -49,18 +48,11 @@
     // Dependencies
     // -------------------------------------------------------------------------
 
-    private GenericStore<Message> messageStore;
-    
-    public void setMessageStore( GenericStore<Message> messageStore )
-    {
-        this.messageStore = messageStore;
-    }
-
-    private UserMessageStore userMessageStore;
-
-    public void setUserMessageStore( UserMessageStore userMessageStore )
-    {
-        this.userMessageStore = userMessageStore;
+    private MessageConversationStore messageConversationStore;
+
+    public void setMessageConversationStore( MessageConversationStore messageConversationStore )
+    {
+        this.messageConversationStore = messageConversationStore;
     }
 
     private CurrentUserService currentUserService;
@@ -81,78 +73,80 @@
     // MessageService implementation
     // -------------------------------------------------------------------------
 
-    public int sendMessage( Message message, Set<User> users )
+    public int sendMessage( String subject, String text, Set<User> users )
     {
-        Set<UserMessage> userMessages = new HashSet<UserMessage>();
-        
-        for ( User user : users )
-        {
-            userMessages.add( new UserMessage( user, message ) );        
-        }
-        
-        message.setUserMessages( userMessages );
-        
-        return saveMessage( message );
-    }
+        // ---------------------------------------------------------------------
+        // Add feedback recipients to users if they are not there
+        // ---------------------------------------------------------------------
 
-    public int sendFeedback( Message message )
-    {
         UserGroup userGroup = configurationService.getConfiguration().getFeedbackRecipients();
-        
-        int r = 0;
-        
+
         if ( userGroup != null && userGroup.getMembers().size() > 0 )
         {
-            r = sendMessage( message, userGroup.getMembers() );
-        }
-        
-        return r;
-    }
-        
-    public int saveMessage( Message message )
-    {
-        return messageStore.save( message );
-    }
-    
-    public Message getMessage( int id )
-    {
-        return messageStore.get( id );
-    }
-    
-    public UserMessage getUserMessage( int id )
-    {
-        return userMessageStore.get( id );
-    }
-    
-    public void updateUserMessage( UserMessage userMessage )
-    {
-        userMessageStore.update( userMessage );
-    }
-    
-    public void deleteUserMessage( UserMessage userMessage )
-    {
-        Message message = userMessage.getMessage();
-        message.getUserMessages().remove( userMessage );
-        userMessageStore.delete( userMessage );
-    }
-    
-    public List<UserMessage> getUserMessages( int first, int max )
-    {
-        return userMessageStore.getUserMessages( currentUserService.getCurrentUser(), first, max );
-    }
-
-    public List<UserMessage> getUserMessages( User user, int first, int max )
-    {
-        return userMessageStore.getUserMessages( user, first, max );
-    }
-    
-    public long getUnreadMessageCount()
-    {
-        return userMessageStore.getUnreadUserMessageCount( currentUserService.getCurrentUser() );
-    }
-    
-    public long getUnreadMessageCount( User user )
-    {
-        return userMessageStore.getUnreadUserMessageCount( user );
+            users.addAll( userGroup.getMembers() );
+        }
+
+        // ---------------------------------------------------------------------
+        // Instantiate message, content and user messages
+        // ---------------------------------------------------------------------
+
+        User sender = currentUserService.getCurrentUser();
+        
+        MessageConversation conversation = new MessageConversation( subject, sender );
+        
+        conversation.addMessage( new Message( text, sender ) );
+        
+        for ( User user : users )
+        {
+            conversation.addUserMessage( new UserMessage( user ) );        
+        }
+        
+        return saveMessageConversation( conversation );
+    }
+
+    public int sendFeedback( String subject, String text )
+    {
+        return sendMessage( subject, text, new HashSet<User>() );
+    }
+    
+    public void sendReply( MessageConversation conversation, String text )
+    {
+        User sender = currentUserService.getCurrentUser();
+        
+        Message message = new Message( text, sender );
+        
+        conversation.markReplied( sender, message );
+        
+        updateMessageConversation( conversation );        
+    }
+        
+    public int saveMessageConversation( MessageConversation conversation )
+    {
+        return messageConversationStore.save( conversation );
+    }
+    
+    public void updateMessageConversation( MessageConversation conversation )
+    {
+        messageConversationStore.update( conversation );
+    }
+    
+    public MessageConversation getMessageConversation( int id )
+    {
+        return messageConversationStore.get( id );
+    }
+        
+    public long getUnreadMessageConversationCount()
+    {
+        return messageConversationStore.getUnreadUserMessageConversationCount( currentUserService.getCurrentUser() );
+    }
+    
+    public long getUnreadMessageConversationCount( User user )
+    {
+        return messageConversationStore.getUnreadUserMessageConversationCount( user );
+    }
+    
+    public List<MessageConversation> getMessageConversations( int first, int max )
+    {
+        return messageConversationStore.getMessageConversations( currentUserService.getCurrentUser(), first, max );
     }
 }

=== renamed file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/message/hibernate/HibernateUserMessageStore.java' => 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/message/hibernate/HibernateMessageConversationStore.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/message/hibernate/HibernateUserMessageStore.java	2011-03-31 15:54:20 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/message/hibernate/HibernateMessageConversationStore.java	2011-08-04 10:06:15 +0000
@@ -27,39 +27,62 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+import java.sql.ResultSet;
+import java.sql.SQLException;
 import java.util.List;
 
-import org.hibernate.Criteria;
 import org.hibernate.Query;
-import org.hibernate.criterion.Order;
-import org.hibernate.criterion.Restrictions;
 import org.hisp.dhis.hibernate.HibernateGenericStore;
-import org.hisp.dhis.message.UserMessage;
-import org.hisp.dhis.message.UserMessageStore;
+import org.hisp.dhis.message.MessageConversation;
+import org.hisp.dhis.message.MessageConversationStore;
 import org.hisp.dhis.user.User;
+import org.springframework.jdbc.core.RowMapper;
 
 /**
  * @author Lars Helge Overland
  */
-public class HibernateUserMessageStore
-    extends HibernateGenericStore<UserMessage> implements UserMessageStore 
+public class HibernateMessageConversationStore
+    extends HibernateGenericStore<MessageConversation> implements MessageConversationStore
 {
-    @SuppressWarnings("unchecked")
-    public List<UserMessage> getUserMessages( User user, int first, int max )
+    public List<MessageConversation> getMessageConversations( User user, int first, int max )
     {
-        Criteria criteria = getCriteria( Restrictions.eq( "user", user ) );
-        
-        criteria.setFirstResult( first );
-        criteria.setMaxResults( max );
-        criteria.addOrder( Order.desc( "messageDate" ) );
-        //TODO eager-fetch message
-        
-        return criteria.list();
+        final String sql = 
+            "select mc.messageconversationid, mc.messageconversationkey, mc.subject, mc.lastupdated, ui.surname, ui.firstname, ( " +
+                "select isread from usermessage " +
+                "where usermessage.usermessageid=mu.usermessageid " +
+                "and mu.messageconversationid=mc.messageconversationid ) as isread " +
+            "from messageconversation mc " +
+            "left join messageconversation_usermessages mu on mc.messageconversationid=mu.messageconversationid " +
+            "left join usermessage um on mu.usermessageid=um.usermessageid " +
+            "left join userinfo ui on mc.lastsenderid=ui.userinfoid " +
+            "where um.userid=" + user.getId() + " " +
+            "order by mc.lastupdated desc " +
+            "limit " + max;
+        
+        final List<MessageConversation> conversations = jdbcTemplate.query( sql, new RowMapper<MessageConversation>()
+        {
+            public MessageConversation mapRow( ResultSet resultSet, int count ) throws SQLException
+            {
+                MessageConversation conversation = new MessageConversation();
+                
+                conversation.setId( resultSet.getInt( 1 ) );
+                conversation.setKey( resultSet.getString( 2 ) );
+                conversation.setSubject( resultSet.getString( 3 ) );
+                conversation.setLastUpdated( resultSet.getDate( 4 ) );
+                conversation.setLastSenderSurname( resultSet.getString( 5 ) );
+                conversation.setLastSenderFirstname( resultSet.getString( 6 ) );                
+                conversation.setRead( resultSet.getBoolean( 7 ) );
+                
+                return conversation;
+            }            
+        } );
+        
+        return conversations;
     }
     
-    public long getUnreadUserMessageCount( User user )
+    public long getUnreadUserMessageConversationCount( User user )
     {
-        String hql = "select count(*) from UserMessage where user = :user and read = false";
+        String hql = "select count(*) from MessageConversation m join m.userMessages u where u.user = :user and u.read = false";
         
         Query query = getQuery( hql );
         query.setEntity( "user", user );
@@ -67,4 +90,4 @@
         
         return (Long) query.uniqueResult();
     }
-}
+}
\ No newline at end of file

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/startup/TableAlteror.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/startup/TableAlteror.java	2011-06-02 08:51:07 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/startup/TableAlteror.java	2011-08-04 10:06:15 +0000
@@ -280,6 +280,12 @@
         executeSql( "ALTER TABLE organisationunit DROP CONSTRAINT fke509dd5ef1c932ed" );
         executeSql( "DROP TABLE source CASCADE" );        
 
+        // message
+
+        executeSql( "ALTER TABLE message DROP COLUMN messagesubject" );
+        executeSql( "ALTER TABLE usermessage DROP COLUMN messagedate" );
+        executeSql( "DROP TABLE message_usermessages" );
+        
         log.info( "Tables updated" );
     }
 

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml'
--- dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml	2011-07-19 06:11:23 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml	2011-08-04 10:06:15 +0000
@@ -201,15 +201,10 @@
     <property name="sessionFactory" ref="sessionFactory" />
   </bean>
 
-  <bean id="org.hisp.dhis.message.MessageStore" class="org.hisp.dhis.hibernate.HibernateGenericStore">
-    <property name="clazz" value="org.hisp.dhis.message.Message" />
-    <property name="sessionFactory" ref="sessionFactory" />
-    <property name="cacheable" value="true" />
-  </bean>
-
-  <bean id="org.hisp.dhis.message.UserMessageStore" class="org.hisp.dhis.message.hibernate.HibernateUserMessageStore">
-    <property name="clazz" value="org.hisp.dhis.message.UserMessage" />
-    <property name="sessionFactory" ref="sessionFactory" />
+  <bean id="org.hisp.dhis.message.MessageConversationStore" class="org.hisp.dhis.message.hibernate.HibernateMessageConversationStore">
+    <property name="clazz" value="org.hisp.dhis.message.MessageConversation" />
+    <property name="sessionFactory" ref="sessionFactory" />
+	<property name="jdbcTemplate" ref="jdbcTemplate" />
     <property name="cacheable" value="true" />
   </bean>
 
@@ -420,8 +415,7 @@
   </bean>
 
   <bean id="org.hisp.dhis.message.MessageService" class="org.hisp.dhis.message.DefaultMessageService">
-    <property name="messageStore" ref="org.hisp.dhis.message.MessageStore" />
-    <property name="userMessageStore" ref="org.hisp.dhis.message.UserMessageStore" />
+    <property name="messageConversationStore" ref="org.hisp.dhis.message.MessageConversationStore" />
     <property name="currentUserService" ref="org.hisp.dhis.user.CurrentUserService" />
     <property name="configurationService" ref="org.hisp.dhis.configuration.ConfigurationService" />
   </bean>

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/message/hibernate/Message.hbm.xml'
--- dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/message/hibernate/Message.hbm.xml	2011-05-28 21:25:46 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/message/hibernate/Message.hbm.xml	2011-08-04 10:32:02 +0000
@@ -11,19 +11,15 @@
     <id name="id" column="messageid">
       <generator class="native" />
     </id>
-
-    <property name="key" column="messagekey" not-null="true" />
-    <property name="subject" column="messagesubject" not-null="true" />
-    <property name="text" column="messagetext" type="text" />
-
-    <many-to-one name="sender" class="org.hisp.dhis.user.User" column="userid" foreign-key="fk_message_user"
-      not-null="true" />
-
-    <set name="userMessages" table="message_usermessages" cascade="save-update">
-      <key column="messageid" foreign-key="fk_message_usermessages_messageid" />
-      <many-to-many class="org.hisp.dhis.message.UserMessage" column="usermessageid" unique="true"
-        foreign-key="fk_message_usermessages" />
-    </set>
-
+	
+	<property name="key" column="messagekey" not-null="true"/>
+	<property name="text" column="messagetext"/>
+	
+    <many-to-one name="sender" class="org.hisp.dhis.user.User" column="userid" 
+	  foreign-key="fk_message_userid" />
+	
+	<property name="sentDate"/>
+	
   </class>
 </hibernate-mapping>
+	

=== added file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/message/hibernate/MessageConversation.hbm.xml'
--- dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/message/hibernate/MessageConversation.hbm.xml	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/message/hibernate/MessageConversation.hbm.xml	2011-08-04 10:32:02 +0000
@@ -0,0 +1,37 @@
+<?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.message.MessageConversation" table="messageconversation">
+
+    <cache usage="read-write" />
+
+    <id name="id" column="messageconversationid">
+      <generator class="native" />
+    </id>
+
+    <property name="key" column="messageconversationkey" not-null="true" />
+    <property name="subject" column="subject" not-null="true" />
+
+    <set name="userMessages" table="messageconversation_usermessages" cascade="all,delete-orphan">
+      <key column="messageconversationid" foreign-key="fk_messageconversation_usermessages_messageconversationid" />
+      <many-to-many class="org.hisp.dhis.message.UserMessage" column="usermessageid" 
+		unique="true" foreign-key="fk_messageconversation_usermessages_usermessageid" />
+    </set>
+	
+	<list name="messages" table="messageconversation_messages" cascade="all,delete-orphan">
+	  <key column="messageconversationid" foreign-key="fk_messageconversation_messages_messageconversationid" />
+	  <list-index column="sort_order" base="1" />
+	  <many-to-many class="org.hisp.dhis.message.Message" column="messageid"
+		unique="true" foreign-key="fk_messageconversation_messages_messageid" />
+	</list>
+	
+	<property name="lastUpdated"/>
+	
+    <many-to-one name="lastSender" class="org.hisp.dhis.user.User" column="lastsenderid" 
+	  foreign-key="fk_messageconversation_userid" />
+	
+  </class>
+</hibernate-mapping>

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/message/hibernate/UserMessage.hbm.xml'
--- dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/message/hibernate/UserMessage.hbm.xml	2011-05-28 21:25:46 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/message/hibernate/UserMessage.hbm.xml	2011-08-04 07:52:24 +0000
@@ -12,16 +12,12 @@
       <generator class="native" />
     </id>
 
-    <many-to-one name="user" class="org.hisp.dhis.user.User" column="userid" foreign-key="fk_usermessage_user"
-      not-null="true" />
+    <property name="key" column="usermessagekey"/>
+
+    <many-to-one name="user" class="org.hisp.dhis.user.User" column="userid" 
+	  foreign-key="fk_usermessage_user" not-null="true" />
 
     <property name="read" column="isread" not-null="true" />
-    <property name="messageDate" column="messagedate" not-null="true" />
-
-    <join table="message_usermessages" inverse="true">
-      <key column="usermessageid" />
-      <many-to-one column="messageid" name="message" not-null="true" />
-    </join>
 
   </class>
 </hibernate-mapping>

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/message/MessageServiceTest.java'
--- dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/message/MessageServiceTest.java	2011-04-01 15:44:51 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/message/MessageServiceTest.java	2011-08-04 10:06:15 +0000
@@ -28,12 +28,10 @@
  */
 
 import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
 import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
 
 import org.hisp.dhis.DhisSpringTest;
@@ -51,13 +49,7 @@
     private User userA;
     private User userB;
 
-    private Message messageA;
-    private Message messageB;
-    
-    private UserMessage userMessageA;
-    private UserMessage userMessageB;
-    private UserMessage userMessageC;
-    private UserMessage userMessageD;
+    private Set<User> users;
 
     // -------------------------------------------------------------------------
     // Fixture
@@ -69,131 +61,109 @@
         userService = (UserService) getBean( UserService.ID );
         messageService = (MessageService) getBean( MessageService.ID );
         
-        sender = createUser( 'S');
+        sender = createUser( 'S' );
         userA = createUser( 'A' );
         userB = createUser( 'B' );
 
         userService.addUser( sender );
         userService.addUser( userA );
         userService.addUser( userB );
-
-        messageA = new Message( "SubjectA", "TextA", sender );
-        messageB = new Message( "SubjectB", "TextB", sender );
-        
-        userMessageA = new UserMessage( userA, messageA );
-        userMessageB = new UserMessage( userB, messageA );
-        userMessageC = new UserMessage( userA, messageB );
-        userMessageD = new UserMessage( userB, messageB );
-        
-        messageA.getUserMessages().add( userMessageA );
-        messageA.getUserMessages().add( userMessageB );
-        messageB.getUserMessages().add( userMessageC );
-        messageB.getUserMessages().add( userMessageD );
-    }
-    
+        
+        users = new HashSet<User>();
+        users.add( userA );
+        users.add( userB );        
+    }
+
+    @Test
+    public void testSaveMessageConversationA()
+    {
+        MessageConversation conversationA = new MessageConversation( "SubjectA", sender );
+        MessageConversation conversationB = new MessageConversation( "SubjectB", sender );
+        
+        int idA = messageService.saveMessageConversation( conversationA );
+        int idB = messageService.saveMessageConversation( conversationB );
+        
+        conversationA = messageService.getMessageConversation( idA );
+        conversationB = messageService.getMessageConversation( idB );
+        
+        assertNotNull( conversationA );
+        assertEquals( "SubjectA", conversationA.getSubject() );
+
+        assertNotNull( conversationB );
+        assertEquals( "SubjectB", conversationB.getSubject() );
+    }
+
+    @Test
+    public void testSaveMessageB()
+    {
+        MessageConversation conversation = new MessageConversation( "Subject", sender );
+        
+        UserMessage userMessageA = new UserMessage( userA );
+        UserMessage userMessageB = new UserMessage( userB );
+        
+        conversation.addUserMessage( userMessageA );
+        conversation.addUserMessage( userMessageB );
+        
+        Message contentA = new Message( "TextA", sender );
+        Message contentB = new Message( "TextB", sender );
+        
+        conversation.addMessage( contentA );
+        conversation.addMessage( contentB );
+        
+        int id = messageService.saveMessageConversation( conversation );
+        
+        conversation = messageService.getMessageConversation( id );
+        
+        assertNotNull( conversation );
+        assertEquals( "Subject", conversation.getSubject() );
+        assertEquals( 2, conversation.getUserMessages().size() );
+        assertTrue( conversation.getUserMessages().contains( userMessageA ) );
+        assertTrue( conversation.getUserMessages().contains( userMessageB ) );
+        assertEquals( 2, conversation.getMessages().size() );
+        assertTrue( conversation.getMessages().contains( contentA ) );
+        assertTrue( conversation.getMessages().contains( contentB ) );
+    }
+
     @Test
     public void testSendMessage()
     {
-        messageA = new Message( "SubjectA", "TextA", sender );
-        
-        Set<User> users = new HashSet<User>();
-        users.add( userA );
-        users.add( userB );
-        
-        int idA = messageService.sendMessage( messageA, users );
-
-        messageA = messageService.getMessage( idA );
-        
-        assertNotNull( messageA );
-        assertEquals( "SubjectA", messageA.getSubject() );
-        assertEquals( "TextA", messageA.getText() );
-        assertEquals( 2, messageA.getUserMessages().size() );        
-    }
-    
-    @Test
-    public void testSaveMessage()
-    {
-        int idA = messageService.saveMessage( messageA );
-        
-        messageA = messageService.getMessage( idA );
-        
-        assertNotNull( messageA );
-        assertEquals( "SubjectA", messageA.getSubject() );
-        assertEquals( "TextA", messageA.getText() );
-        assertEquals( 2, messageA.getUserMessages().size() );
-        assertTrue( messageA.getUserMessages().contains( userMessageA ) );
-        assertTrue( messageA.getUserMessages().contains( userMessageB ) );
-    }
-
-    @Test
-    public void testGetUserMessages()
-    {
-        messageService.saveMessage( messageA );
-        messageService.saveMessage( messageB );
-        
-        List<UserMessage> userMessages = messageService.getUserMessages( userA, 0, 10 );
-        
-        assertNotNull( userMessages );
-        assertEquals( 2, userMessages.size() );
-        assertTrue( userMessages.contains( userMessageA ) );
-        assertTrue( userMessages.contains( userMessageC ) );
-    }
-
-    @Test
-    public void testUpdateUserMessage()
-    {
-        messageService.saveMessage( messageA );
-        
-        assertNotNull( userMessageA );
-        assertFalse( userMessageA.isRead() );
-        
-        userMessageA.setRead( true );
-        
-        int idA = userMessageA.getId();
-        
-        messageService.updateUserMessage( userMessageA );
-        
-        userMessageA = messageService.getUserMessage( idA );
-        
-        assertNotNull( userMessageA );
-        assertTrue( userMessageA.isRead() );
-    }
-    
-    @Test
-    public void testDeleteUserMessage()
-    {
-        messageService.saveMessage( messageA );
-
-        assertEquals( 2, messageA.getUserMessages().size() );
-        assertTrue( messageA.getUserMessages().contains( userMessageA ) );
-        assertTrue( messageA.getUserMessages().contains( userMessageB ) );
-        
-        messageService.deleteUserMessage( userMessageB );
-
-        assertEquals( 1, messageA.getUserMessages().size() );
-        assertTrue( messageA.getUserMessages().contains( userMessageA ) );
-        
-        messageService.deleteUserMessage( userMessageA );
-
-        assertEquals( 0, messageA.getUserMessages().size() );
-    }
-    
-    @Test
-    public void testGetUserMessagesCount()
-    {
-        messageService.saveMessage( messageA );
-        messageService.saveMessage( messageB );
-     
-        long count = messageService.getUnreadMessageCount( userA );
-        
-        assertEquals( 2, count );
-        
-        userMessageA.setRead( true );
-        
-        messageService.updateUserMessage( userMessageA );
-        
-        count = messageService.getUnreadMessageCount( userA );
-        
-        assertEquals( 1, count );
+        int id = messageService.sendMessage( "Subject", "Text", users );
+        
+        MessageConversation conversation = messageService.getMessageConversation( id );
+        
+        assertNotNull( conversation );
+        assertEquals( "Subject", conversation.getSubject() );
+        assertEquals( 2, conversation.getUserMessages().size() );
+        assertEquals( 1, conversation.getMessages().size() );
+        assertTrue( conversation.getMessages().iterator().next().getText().equals( "Text" ) );
+    }
+    
+    @Test
+    public void testSendFeedback()
+    {
+        int id = messageService.sendFeedback( "Subject", "Text" );
+        
+        MessageConversation conversation = messageService.getMessageConversation( id );
+        
+        assertNotNull( conversation );
+        assertEquals( "Subject", conversation.getSubject() );
+        assertEquals( 1, conversation.getMessages().size() );
+        assertTrue( conversation.getMessages().iterator().next().getText().equals( "Text" ) );
+    }
+    
+    @Test
+    public void testSendReply()
+    {
+        MessageConversation message = new MessageConversation( "Subject", sender );
+        message.addMessage( new Message( "TextA", sender ) );
+        int id = messageService.saveMessageConversation( message );
+        
+        messageService.sendReply( message, "TextB" );
+        
+        message = messageService.getMessageConversation( id );
+        
+        assertNotNull( message );
+        assertEquals( "Subject", message.getSubject() );
+        assertEquals( 2, message.getMessages().size() );       
     }
 }

=== modified file 'dhis-2/dhis-support/dhis-support-hibernate/src/main/java/org/hisp/dhis/hibernate/HibernateGenericStore.java'
--- dhis-2/dhis-support/dhis-support-hibernate/src/main/java/org/hisp/dhis/hibernate/HibernateGenericStore.java	2011-04-23 18:52:44 +0000
+++ dhis-2/dhis-support/dhis-support-hibernate/src/main/java/org/hisp/dhis/hibernate/HibernateGenericStore.java	2011-08-04 10:06:15 +0000
@@ -39,6 +39,7 @@
 import org.hibernate.criterion.Projections;
 import org.hibernate.criterion.Restrictions;
 import org.hisp.dhis.common.GenericIdentifiableObjectStore;
+import org.springframework.jdbc.core.JdbcTemplate;
 
 /**
  * @author Lars Helge Overland
@@ -54,6 +55,13 @@
         this.sessionFactory = sessionFactory;
     }
     
+    protected JdbcTemplate jdbcTemplate;
+    
+    public void setJdbcTemplate( JdbcTemplate jdbcTemplate )
+    {
+        this.jdbcTemplate = jdbcTemplate;
+    }
+
     private Class<T> clazz;
 
     /**

=== modified file 'dhis-2/dhis-support/dhis-support-hibernate/src/main/resources/ehcache.xml'
--- dhis-2/dhis-support/dhis-support-hibernate/src/main/resources/ehcache.xml	2011-07-21 03:29:35 +0000
+++ dhis-2/dhis-support/dhis-support-hibernate/src/main/resources/ehcache.xml	2011-08-04 10:06:15 +0000
@@ -102,8 +102,11 @@
   <cache name="org.hisp.dhis.translation.Translation"
     maxElementsInMemory="8000"/>
   
+  <cache name="org.hisp.dhis.message.MessageConversation"
+    maxElementsInMemory="500"/>
+  
   <cache name="org.hisp.dhis.message.Message"
-    maxElementsInMemory="500"/>
+    maxElementsInMemory="1000"/>
   
   <cache name="org.hisp.dhis.message.UserMessage"
     maxElementsInMemory="50000"/>

=== modified file 'dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/action/ProvideContentAction.java'
--- dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/action/ProvideContentAction.java	2011-06-09 19:07:37 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/action/ProvideContentAction.java	2011-08-04 10:06:15 +0000
@@ -137,7 +137,7 @@
             chartAreas.add( content.get( DashboardManager.CHART_AREA_PREFIX + i ) );
         }
 
-        messageCount = messageService.getUnreadMessageCount();
+        messageCount = messageService.getUnreadMessageConversationCount();
 
         return SUCCESS;
     }

=== modified file 'dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/GetMessagesAction.java'
--- dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/GetMessagesAction.java	2011-04-01 19:47:09 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/GetMessagesAction.java	2011-08-04 10:06:15 +0000
@@ -29,8 +29,8 @@
 
 import java.util.List;
 
+import org.hisp.dhis.message.MessageConversation;
 import org.hisp.dhis.message.MessageService;
-import org.hisp.dhis.message.UserMessage;
 
 import com.opensymphony.xwork2.Action;
 
@@ -55,11 +55,11 @@
     // Output
     // -------------------------------------------------------------------------
 
-    private List<UserMessage> messages;
+    private List<MessageConversation> conversations;
 
-    public List<UserMessage> getMessages()
+    public List<MessageConversation> getConversations()
     {
-        return messages;
+        return conversations;
     }
 
     // -------------------------------------------------------------------------
@@ -68,7 +68,7 @@
 
     public String execute()
     {
-        messages = messageService.getUserMessages( 0, 200 );
+        conversations = messageService.getMessageConversations( 0, 300 );
         
         return SUCCESS;
     }

=== modified file 'dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/ReadMessageAction.java'
--- dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/ReadMessageAction.java	2011-08-03 10:05:08 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/ReadMessageAction.java	2011-08-04 10:06:15 +0000
@@ -27,8 +27,9 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+import org.hisp.dhis.message.MessageConversation;
 import org.hisp.dhis.message.MessageService;
-import org.hisp.dhis.message.UserMessage;
+import org.hisp.dhis.user.CurrentUserService;
 
 import com.opensymphony.xwork2.Action;
 
@@ -48,6 +49,13 @@
     {
         this.messageService = messageService;
     }
+    
+    private CurrentUserService currentUserService;
+
+    public void setCurrentUserService( CurrentUserService currentUserService )
+    {
+        this.currentUserService = currentUserService;
+    }
 
     // -------------------------------------------------------------------------
     // Input
@@ -64,11 +72,11 @@
     // Output
     // -------------------------------------------------------------------------
 
-    private UserMessage message;
-    
-    public UserMessage getMessage()
+    private MessageConversation conversation;
+
+    public MessageConversation getConversation()
     {
-        return message;
+        return conversation;
     }
 
     // -------------------------------------------------------------------------
@@ -79,11 +87,11 @@
     public String execute()
         throws Exception
     {
-        message = messageService.getUserMessage( id );
-        
-        message.setRead( true );
-        
-        messageService.updateUserMessage( message );
+        conversation = messageService.getMessageConversation( id );
+        
+        conversation.markRead( currentUserService.getCurrentUser() );
+        
+        messageService.updateMessageConversation( conversation );
         
         return SUCCESS;
     }

=== modified file 'dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/RemoveMessageAction.java'
--- dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/RemoveMessageAction.java	2011-08-03 10:05:08 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/RemoveMessageAction.java	2011-08-04 10:06:15 +0000
@@ -27,8 +27,9 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+import org.hisp.dhis.message.MessageConversation;
 import org.hisp.dhis.message.MessageService;
-import org.hisp.dhis.message.UserMessage;
+import org.hisp.dhis.user.CurrentUserService;
 
 import com.opensymphony.xwork2.Action;
 
@@ -48,6 +49,13 @@
     {
         this.messageService = messageService;
     }
+    
+    private CurrentUserService currentUserService;
+
+    public void setCurrentUserService( CurrentUserService currentUserService )
+    {
+        this.currentUserService = currentUserService;
+    }
 
     // -------------------------------------------------------------------------
     // Input
@@ -67,9 +75,11 @@
     public String execute()
         throws Exception
     {
-        UserMessage message = messageService.getUserMessage( id );
-        
-        messageService.deleteUserMessage( message );
+        MessageConversation conversation = messageService.getMessageConversation( id );
+        
+        conversation.remove( currentUserService.getCurrentUser() );
+        
+        messageService.updateMessageConversation( conversation );
         
         return SUCCESS;
     }    

=== modified file 'dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/SendFeedbackAction.java'
--- dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/SendFeedbackAction.java	2011-06-16 08:08:02 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/SendFeedbackAction.java	2011-08-04 10:06:15 +0000
@@ -27,10 +27,7 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-import org.hisp.dhis.message.Message;
 import org.hisp.dhis.message.MessageService;
-import org.hisp.dhis.user.CurrentUserService;
-import org.hisp.dhis.user.User;
 
 import com.opensymphony.xwork2.Action;
 
@@ -51,13 +48,6 @@
         this.messageService = messageService;
     }
     
-    private CurrentUserService currentUserService;
-
-    public void setCurrentUserService( CurrentUserService currentUserService )
-    {
-        this.currentUserService = currentUserService;
-    }
-
     // -------------------------------------------------------------------------
     // Input
     // -------------------------------------------------------------------------
@@ -82,11 +72,7 @@
 
     public String execute()
     {
-        User sender = currentUserService.getCurrentUser();
-
-        Message message = new Message( subject, text, sender );
-        
-        messageService.sendFeedback( message );
+        messageService.sendFeedback( subject, text );
         
         return SUCCESS;
     }

=== modified file 'dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/SendMessageAction.java'
--- dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/SendMessageAction.java	2011-04-01 15:44:51 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/SendMessageAction.java	2011-08-04 10:06:15 +0000
@@ -30,11 +30,9 @@
 import java.util.HashSet;
 import java.util.Set;
 
-import org.hisp.dhis.message.Message;
 import org.hisp.dhis.message.MessageService;
 import org.hisp.dhis.organisationunit.OrganisationUnit;
 import org.hisp.dhis.oust.manager.SelectionTreeManager;
-import org.hisp.dhis.user.CurrentUserService;
 import org.hisp.dhis.user.User;
 
 import com.opensymphony.xwork2.Action;
@@ -63,13 +61,6 @@
         this.selectionTreeManager = selectionTreeManager;
     }
     
-    private CurrentUserService currentUserService;
-
-    public void setCurrentUserService( CurrentUserService currentUserService )
-    {
-        this.currentUserService = currentUserService;
-    }
-
     // -------------------------------------------------------------------------
     // Input
     // -------------------------------------------------------------------------
@@ -94,10 +85,6 @@
 
     public String execute()
     {
-        User sender = currentUserService.getCurrentUser();
-
-        Message message = new Message( subject, text, sender );
-        
         Set<User> users = new HashSet<User>();
         
         for ( OrganisationUnit unit : selectionTreeManager.getReloadedSelectedOrganisationUnits() )
@@ -105,7 +92,7 @@
             users.addAll( unit.getUsers() );
         }
         
-        messageService.sendMessage( message, users );
+        messageService.sendMessage( subject, text, users );
         
         return SUCCESS;
     }

=== added file 'dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/SendReplyAction.java'
--- dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/SendReplyAction.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/SendReplyAction.java	2011-08-04 10:06:15 +0000
@@ -0,0 +1,82 @@
+package org.hisp.dhis.dashboard.message.action;
+
+/*
+ * Copyright (c) 2004-2010, 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.message.MessageConversation;
+import org.hisp.dhis.message.MessageService;
+
+import com.opensymphony.xwork2.Action;
+
+/**
+ * @author Lars Helge Overland
+ */
+public class SendReplyAction
+    implements Action
+{
+    // -------------------------------------------------------------------------
+    // Dependencies
+    // -------------------------------------------------------------------------
+
+    private MessageService messageService;
+
+    public void setMessageService( MessageService messageService )
+    {
+        this.messageService = messageService;
+    }
+
+    // -------------------------------------------------------------------------
+    // Input
+    // -------------------------------------------------------------------------
+
+    private Integer id;
+    
+    public void setId( Integer id )
+    {
+        this.id = id;
+    }
+
+    private String text;
+
+    public void setText( String text )
+    {
+        this.text = text;
+    }
+
+    // -------------------------------------------------------------------------
+    // Action implementation
+    // -------------------------------------------------------------------------
+
+    public String execute()
+    {
+        MessageConversation conversation = messageService.getMessageConversation( id );
+        
+        messageService.sendReply( conversation, text );
+        
+        return SUCCESS;
+    }
+}

=== modified file 'dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/UnreadMessageAction.java'
--- dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/UnreadMessageAction.java	2011-08-03 10:05:08 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/java/org/hisp/dhis/dashboard/message/action/UnreadMessageAction.java	2011-08-04 10:06:15 +0000
@@ -27,8 +27,9 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+import org.hisp.dhis.message.MessageConversation;
 import org.hisp.dhis.message.MessageService;
-import org.hisp.dhis.message.UserMessage;
+import org.hisp.dhis.user.CurrentUserService;
 
 import com.opensymphony.xwork2.Action;
 
@@ -49,6 +50,13 @@
         this.messageService = messageService;
     }
 
+    private CurrentUserService currentUserService;
+
+    public void setCurrentUserService( CurrentUserService currentUserService )
+    {
+        this.currentUserService = currentUserService;
+    }
+
     // -------------------------------------------------------------------------
     // Input
     // -------------------------------------------------------------------------
@@ -67,11 +75,11 @@
     public String execute()
         throws Exception
     {
-        UserMessage message = messageService.getUserMessage( id );
-        
-        message.setRead( false );
-        
-        messageService.updateUserMessage( message );
+        MessageConversation conversation = messageService.getMessageConversation( id );
+        
+        conversation.markUnread( currentUserService.getCurrentUser() );
+        
+        messageService.updateMessageConversation( conversation );
         
         return SUCCESS;
     }

=== modified file 'dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/resources/META-INF/dhis/beans.xml'
--- dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/resources/META-INF/dhis/beans.xml	2011-06-23 14:44:17 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/resources/META-INF/dhis/beans.xml	2011-08-04 10:06:15 +0000
@@ -67,28 +67,34 @@
     scope="prototype">
     <property name="messageService" ref="org.hisp.dhis.message.MessageService" />
     <property name="selectionTreeManager" ref="org.hisp.dhis.oust.manager.SelectionTreeManager" />
-    <property name="currentUserService" ref="org.hisp.dhis.user.CurrentUserService" />
   </bean>
 
   <bean id="org.hisp.dhis.dashboard.message.action.SendFeedbackAction" class="org.hisp.dhis.dashboard.message.action.SendFeedbackAction"
     scope="prototype">
     <property name="messageService" ref="org.hisp.dhis.message.MessageService" />
-    <property name="currentUserService" ref="org.hisp.dhis.user.CurrentUserService" />
+  </bean>
+
+  <bean id="org.hisp.dhis.dashboard.message.action.SendReplyAction" class="org.hisp.dhis.dashboard.message.action.SendReplyAction"
+    scope="prototype">
+    <property name="messageService" ref="org.hisp.dhis.message.MessageService" />
   </bean>
 
   <bean id="org.hisp.dhis.dashboard.message.action.ReadMessageAction" class="org.hisp.dhis.dashboard.message.action.ReadMessageAction"
     scope="prototype">
     <property name="messageService" ref="org.hisp.dhis.message.MessageService" />
+	<property name="currentUserService" ref="org.hisp.dhis.user.CurrentUserService" />
   </bean>
 
   <bean id="org.hisp.dhis.dashboard.message.action.UnreadMessageAction" class="org.hisp.dhis.dashboard.message.action.UnreadMessageAction"
     scope="prototype">
     <property name="messageService" ref="org.hisp.dhis.message.MessageService" />
+	<property name="currentUserService" ref="org.hisp.dhis.user.CurrentUserService" />
   </bean>
 
   <bean id="org.hisp.dhis.dashboard.message.action.RemoveMessageAction" class="org.hisp.dhis.dashboard.message.action.RemoveMessageAction"
     scope="prototype">
     <property name="messageService" ref="org.hisp.dhis.message.MessageService" />
+    <property name="currentUserService" ref="org.hisp.dhis.user.CurrentUserService" />
   </bean>
 
 </beans>

=== modified file 'dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/resources/org/hisp/dhis/dashboard/i18n_module.properties'
--- dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/resources/org/hisp/dhis/dashboard/i18n_module.properties	2011-08-03 12:14:53 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/resources/org/hisp/dhis/dashboard/i18n_module.properties	2011-08-04 11:22:31 +0000
@@ -34,4 +34,5 @@
 unread_message = unread message
 discard = Discard
 enter_subject = Please enter a subject
-enter_text = Please enter text
\ No newline at end of file
+enter_text = Please enter text
+reply = Reply
\ No newline at end of file

=== modified file 'dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/resources/struts.xml'
--- dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/resources/struts.xml	2011-06-23 14:44:17 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/resources/struts.xml	2011-08-04 11:10:11 +0000
@@ -69,6 +69,11 @@
     <action name="sendFeedback" class="org.hisp.dhis.dashboard.message.action.SendFeedbackAction">
       <result name="success" type="redirect">message.action</result>
     </action>
+	
+	<action name="sendReply" class="org.hisp.dhis.dashboard.message.action.SendReplyAction">
+	  <result name="success" type="velocity-xml">/dhis-web-dashboard-integration/responseSuccess.vm</result>
+      <param name="onExceptionReturn">plainTextError</param>
+	</action>
 
     <action name="showSendMessage" class="org.hisp.dhis.dashboard.action.NoAction">
       <result name="success" type="velocity">/main.vm</result>
@@ -91,7 +96,7 @@
       <result name="success" type="velocity">/main.vm</result>
       <param name="page">/dhis-web-dashboard-integration/readMessage.vm</param>
       <param name="menu">/dhis-web-dashboard-integration/menu.vm</param>
-      <param name="javascripts">javascript/readMessage.js</param>
+      <param name="javascripts">javascript/message.js</param>
       <param name="stylesheets">style/dashboard.css</param>
     </action>
 

=== modified file 'dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/javascript/message.js'
--- dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/javascript/message.js	2011-08-03 12:14:53 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/javascript/message.js	2011-08-04 11:22:31 +0000
@@ -18,8 +18,8 @@
 
 function validateMessage()
 {
-	var subject = $( '#subject' ).val();
-	var text = $( '#text' ).val();
+	var subject = $( "#subject" ).val();
+	var text = $( "#text" ).val();
 	
 	if ( subject == null || subject.trim() == '' )
 	{
@@ -35,3 +35,36 @@
 	
 	return true;
 }
+
+function showSenderInfo( id )
+{
+	$.getJSON( "../dhis-web-commons-ajax-json/getUser.action", { id:id }, function( json ) {
+		$( "#senderName" ).html( json.user.firstName + " " + json.user.surname );
+		$( "#senderEmail" ).html( json.user.email );
+		$( "#senderPhoneNumber" ).html( json.user.phoneNumber );
+		$( "#senderOrganisationUnits" ).html( json.user.organisationUnits );
+		
+		$( "#senderInfo" ).dialog( {
+	        modal : true,
+	        width : 300,
+	        height : 250,
+	        title : "Sender"
+	    } );
+	} );
+}
+
+function sendReply()
+{
+	var id = $( "#conversationId" ).val();
+	var text = $( "#text" ).val();
+	
+	if ( text == null || text.trim() == '' )
+	{
+		setHeaderMessage( i18n_enter_text );
+		return false;
+	}
+	
+	$.postUTF8( "sendReply.action", { id:id, text:text }, function() {
+		window.location.href = "readMessage.action?id=" + id;
+	} );
+}

=== removed file 'dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/javascript/readMessage.js'
--- dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/javascript/readMessage.js	2011-07-28 07:15:09 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/javascript/readMessage.js	1970-01-01 00:00:00 +0000
@@ -1,17 +0,0 @@
-var dialog = null;
-
-$( document ).ready( function()
-{
-    dialog = $( "#senderInfo" ).dialog( {
-        modal : true,
-        autoOpen : false,
-        width : 300,
-        height : 250,
-        title : "Sender"
-    } );
-} );
-
-function showSenderInfo( id )
-{
-    dialog.dialog( "open" );
-}

=== modified file 'dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/message.vm'
--- dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/message.vm	2011-06-14 19:13:54 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/message.vm	2011-08-04 10:06:15 +0000
@@ -17,14 +17,14 @@
 		<th>$i18n.getString( "date" )</th>
 		<th>$i18n.getString( "operations" )</th>
 	</tr>
-	#foreach( $message in $messages )
-	<tr id="tr${message.id}" #if( !$message.read )class="unread bold"#end>
-		<td style="width:200px" onclick="read( '${message.id}' )">$encoder.htmlEncode( $message.message.sender.name )</td>
-		<td onclick="read( '${message.id}' )">$encoder.htmlEncode( $message.message.subject )</td>
-		<td onclick="read( '${message.id}' )" style="width:80px">$format.formatDate( $message.messageDate )</td>
+	#foreach( $conversation in $conversations )
+	<tr id="tr${conversation.id}" #if( !$conversation.read )class="unread bold"#end>
+		<td style="width:200px" onclick="read( '${conversation.id}' )">$encoder.htmlEncode( $conversation.lastSenderName )</td>
+		<td onclick="read( '${conversation.id}' )">$encoder.htmlEncode( $conversation.subject )</td>
+		<td onclick="read( '${conversation.id}' )" style="width:80px">$format.formatDate( $conversation.lastUpdated )</td>
 		<td style="width:70px; text-align:center;">
-			<a href="readMessage.action?id=${message.id}"><img src="../images/read.png" title="$i18n.getString( 'read' )"></a>
-			<a href="javascript:removeMessage( '${message.id}' )"><img src="../images/delete.png" title="$i18n.getString( 'delete' )"></a>
+			<a href="readMessage.action?id=${conversation.id}"><img src="../images/read.png" title="$i18n.getString( 'read' )"></a>
+			<a href="javascript:removeMessage( '${conversation.id}' )"><img src="../images/delete.png" title="$i18n.getString( 'delete' )"></a>
 		</td>
 	</tr>
 	#end

=== modified file 'dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/readMessage.vm'
--- dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/readMessage.vm	2011-08-03 11:03:01 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/readMessage.vm	2011-08-04 11:22:31 +0000
@@ -1,32 +1,48 @@
+<script type="text/javascript">
+var i18n_sender = '$encoder.jsEscape( $i18n.getString( "sender" ), "'")';
+var i18n_enter_text = '$encoder.jsEscape( $i18n.getString( "enter_text" ), "'")';
+</script>
 
 <div id="senderInfo" style="display:none" class="page">
-<h3>$message.message.sender.name</h3>
+<h3><span id="senderName"></span></h3>
 <table>
 <tr>
 <td><label>$i18n.getString( "email" ):</label></td>
-<td>$!encoder.htmlEncode( ${message.message.sender.email} )</td>
+<td><span id="senderEmail"></span></td>
 </tr>
 <tr>
 <td><label>$i18n.getString( "phone_number" ):</label></td>
-<td>$!encoder.htmlEncode( ${message.message.sender.phoneNumber} )</td>
+<td><span id="senderPhoneNumber"></span></td>
 </tr>
 <tr>
 <td><label>$i18n.getString( "organisation_units" ):</label></td>
-<td>$!encoder.htmlEncode( ${message.message.sender.organisationUnitsName} )</td>
+<td><span id="senderOrganisationUnits"></span></td>
 </tr>
 </table>
 </div>
 
-<div style="border-bottom: 1px solid #cccccc; margin-bottom:15px; padding-bottom:15px; width:50%;">
-<input type="button" value="$i18n.getString( 'mark_unread' )" onclick="window.location.href='unreadMessage.action?id=${message.id}'" style="width:120px">
-<input type="button" value="$i18n.getString( 'remove' )" onclick="window.location.href='removeMessageRedirect.action?id=${message.id}'" style="width:120px">
+<input type="hidden" id="conversationId" value="${conversation.id}"/>
+
+<div class="messageDiv">
+<input type="button" value="$i18n.getString( 'mark_unread' )" onclick="window.location.href='unreadMessage.action?id=${conversation.id}'" style="width:120px">
+<input type="button" value="$i18n.getString( 'remove' )" onclick="window.location.href='removeMessageRedirect.action?id=${conversation.id}'" style="width:120px">
 <input type="button" value="$i18n.getString( 'back' )" onclick="window.location.href='message.action'" style="width:120px">
 </div>
 
-<h3>$encoder.htmlEncode( $message.message.subject )</h3>
-
-<div class="messageDiv"><span class="bold" style="cursor:pointer" onclick="showSenderInfo( ${message.message.sender.id} )">$encoder.htmlEncode( $message.message.sender.name )</span>&nbsp;
-<span style="color:#606060">$format.formatDate( $message.messageDate )</span></div>
-
-<div style="border-bottom: 1px solid #cccccc; margin-bottom:15px; padding-bottom:15px; width:50%;">$encoder.htmlEncode( $message.message.text )</div>
-
+<div>
+<h3>$encoder.htmlEncode( $conversation.subject )</h3>
+</div>
+
+#foreach( $message in $conversation.messages )
+<div class="messageDiv">
+<span class="bold" style="cursor:pointer" onclick="showSenderInfo( ${message.sender.id} )">$encoder.htmlEncode( $message.sender.name )</span>&nbsp;
+<span style="color:#606060">$format.formatDate( $message.sentDate )</span>
+
+<p>$encoder.htmlEncode( $message.text )</p>
+</div>
+#end
+
+<div>
+<textarea id="text" name="text" style="width:495px;height:125px"></textarea><br>
+<input type="button" value="$i18n.getString( 'reply' )" onclick="sendReply()" style="width:120px">
+</div>
\ 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	2011-06-14 19:13:54 +0000
+++ dhis-2/dhis-web/dhis-web-dashboard-integration/src/main/webapp/dhis-web-dashboard-integration/style/dashboard.css	2011-08-04 10:32:02 +0000
@@ -77,8 +77,10 @@
 
 .messageDiv
 {
-  margin-bottom:20px;
   width:50%;
+  border-bottom:1px solid #cccccc; 
+  margin-bottom:15px;
+  padding-bottom:15px;
 }
 
 a