You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rave.apache.org by ra...@apache.org on 2012/08/13 21:04:13 UTC

svn commit: r1372553 - in /rave/trunk: rave-components/rave-core/src/main/java/org/apache/rave/portal/model/ rave-components/rave-core/src/main/java/org/apache/rave/portal/model/impl/ rave-components/rave-core/src/main/java/org/apache/rave/portal/repos...

Author: raminder
Date: Mon Aug 13 19:04:12 2012
New Revision: 1372553

URL: http://svn.apache.org/viewvc?rev=1372553&view=rev
Log:
Thanks Viknes for the add friend patch. Applied the patch for RAVE-690.

Added:
    rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/FriendRequestStatus.java
    rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/api/rpc/PersonApi.java
Modified:
    rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/Person.java
    rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/impl/PersonImpl.java
    rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/impl/UserImpl.java
    rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/repository/PersonRepository.java
    rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/UserService.java
    rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultUserService.java
    rave/trunk/rave-components/rave-core/src/test/java/org/apache/rave/portal/service/impl/DefaultUserServiceTest.java
    rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/JpaPerson.java
    rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/JpaPersonAssociation.java
    rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/JpaUser.java
    rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/conversion/JpaPersonConverter.java
    rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/conversion/JpaUserConverter.java
    rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/repository/impl/JpaPersonRepository.java
    rave/trunk/rave-components/rave-jpa/src/test/java/org/apache/rave/portal/model/conversion/JpaPersonConverterTest.java
    rave/trunk/rave-components/rave-jpa/src/test/java/org/apache/rave/portal/model/conversion/JpaUserConverterTest.java
    rave/trunk/rave-components/rave-jpa/src/test/resources/test_data.sql
    rave/trunk/rave-portal-resources/src/main/resources/messages.properties
    rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/db/initial_data.sql
    rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/jsp/views/personProfile.jsp
    rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_api.js
    rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_layout.js
    rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_person_profile.js
    rave/trunk/rave-providers/rave-opensocial-provider/rave-opensocial-core/src/main/java/org/apache/rave/opensocial/repository/impl/DecoratingOpenSocialPersonRepository.java

Added: rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/FriendRequestStatus.java
URL: http://svn.apache.org/viewvc/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/FriendRequestStatus.java?rev=1372553&view=auto
==============================================================================
--- rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/FriendRequestStatus.java (added)
+++ rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/FriendRequestStatus.java Mon Aug 13 19:04:12 2012
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.rave.portal.model;
+
+import java.util.HashMap;
+import java.util.Map;
+/**
+ * The friend request status of a user - which is pending (not yet accepted) or accepted by the friend
+ */
+public enum FriendRequestStatus {
+    PENDING("pending"),
+    ACCEPTED("accepted");
+
+    private String requestStatus;
+    private static final Map<String, FriendRequestStatus> lookup = new HashMap<String, FriendRequestStatus>();
+
+    static {
+        for (FriendRequestStatus pt : FriendRequestStatus.values()) {
+            lookup.put(pt.toString(), pt);
+        }
+    }
+
+    private FriendRequestStatus(String requestStatus) {
+        this.requestStatus = requestStatus;
+    }
+
+    public String getRequestStatus() {
+        return requestStatus;
+    }
+
+    @Override
+    public String toString() {
+        return requestStatus;
+    }
+
+    public static FriendRequestStatus get(String requestStatus) {
+        return lookup.get(requestStatus);
+    }
+}

Modified: rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/Person.java
URL: http://svn.apache.org/viewvc/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/Person.java?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/Person.java (original)
+++ rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/Person.java Mon Aug 13 19:04:12 2012
@@ -77,10 +77,6 @@ public interface Person {
 
     void setProperties(List<PersonProperty> properties);
 
-    List<Person> getFriends();
-
-    void setFriends(List<Person> friends);
-
     List<Organization> getOrganizations();
 
     void setOrganizations(List<Organization> organizations);

Modified: rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/impl/PersonImpl.java
URL: http://svn.apache.org/viewvc/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/impl/PersonImpl.java?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/impl/PersonImpl.java (original)
+++ rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/impl/PersonImpl.java Mon Aug 13 19:04:12 2012
@@ -38,7 +38,6 @@ public class PersonImpl implements Perso
     protected List<Address> addresses;
     protected List<Organization> organizations;
     protected List<PersonProperty> properties;
-    protected List<Person> friends;
 
     public String getUsername() {
         return username;
@@ -152,13 +151,6 @@ public class PersonImpl implements Perso
         this.properties = properties;
     }
 
-    public List<Person> getFriends() {
-        return friends;
-    }
-
-    public void setFriends(List<Person> friends) {
-        this.friends = friends;
-    }
 
     @Override
     public boolean equals(Object o) {
@@ -174,7 +166,6 @@ public class PersonImpl implements Perso
         if (displayName != null ? !displayName.equals(person.displayName) : person.displayName != null) return false;
         if (email != null ? !email.equals(person.email) : person.email != null) return false;
         if (familyName != null ? !familyName.equals(person.familyName) : person.familyName != null) return false;
-        if (friends != null ? !friends.equals(person.friends) : person.friends != null) return false;
         if (givenName != null ? !givenName.equals(person.givenName) : person.givenName != null) return false;
         if (honorificPrefix != null ? !honorificPrefix.equals(person.honorificPrefix) : person.honorificPrefix != null)
             return false;
@@ -207,7 +198,6 @@ public class PersonImpl implements Perso
         result = 31 * result + (addresses != null ? addresses.hashCode() : 0);
         result = 31 * result + (organizations != null ? organizations.hashCode() : 0);
         result = 31 * result + (properties != null ? properties.hashCode() : 0);
-        result = 31 * result + (friends != null ? friends.hashCode() : 0);
         return result;
     }
 }
\ No newline at end of file

Modified: rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/impl/UserImpl.java
URL: http://svn.apache.org/viewvc/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/impl/UserImpl.java?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/impl/UserImpl.java (original)
+++ rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/impl/UserImpl.java Mon Aug 13 19:04:12 2012
@@ -151,7 +151,6 @@ public class UserImpl extends PersonImpl
         p.setDisplayName(this.getDisplayName());
         p.setEmail(this.getEmail());
         p.setFamilyName(this.getFamilyName());
-        p.setFriends(this.getFriends());
         p.setGivenName(this.getGivenName());
         p.setHonorificPrefix(this.getHonorificPrefix());
         p.setHonorificSuffix(this.getHonorificSuffix());

Modified: rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/repository/PersonRepository.java
URL: http://svn.apache.org/viewvc/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/repository/PersonRepository.java?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/repository/PersonRepository.java (original)
+++ rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/repository/PersonRepository.java Mon Aug 13 19:04:12 2012
@@ -19,11 +19,12 @@
 
 package org.apache.rave.portal.repository;
 
+import java.util.HashMap;
+import java.util.List;
+
 import org.apache.rave.persistence.Repository;
 import org.apache.rave.portal.model.Person;
 
-import java.util.List;
-
 
 public interface PersonRepository extends Repository<Person> {
     /**
@@ -107,5 +108,32 @@ public interface PersonRepository extend
      * @return a list of people in the group who have the specified friend
      */
     List<Person> findByGroupWithFriend(String groupId, String friendUsername);
+
+    /**
+     * Registers a relationship between two user objects.
+     *
+     * @param user is the user who sends to friend request
+     * @param friend is the user who receives the friend request
+     * @return true is the relationship request was successful
+     */
+	boolean addFriend(Person friend, Person user);
+
+    /**
+     * Removes the relationship between two user objects.
+     *
+     * @param user is the user who wants to remove a friend
+     * @param friend is the user who is removed from the friends list of user
+     */
+	void removeFriend(Person friend, Person user);
+
+    /**
+     * Get the friends and friends requests of a particular user.
+     *
+     * @param user is the user whose friends and requests are to be found
+     * @return A hashmap which contains 2 lists of peeple
+     * 			1 containing friends
+     * 			2 containing friend requests
+     */
+	HashMap<String, List<Person>> findFriendsAndRequests(String username);
 }
 

Modified: rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/UserService.java
URL: http://svn.apache.org/viewvc/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/UserService.java?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/UserService.java (original)
+++ rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/UserService.java Mon Aug 13 19:04:12 2012
@@ -24,6 +24,7 @@ import org.apache.rave.portal.model.User
 import org.apache.rave.portal.model.util.SearchResult;
 import org.springframework.security.core.userdetails.UserDetailsService;
 
+import java.util.HashMap;
 import java.util.List;
 
 public interface UserService extends UserDetailsService {
@@ -150,5 +151,31 @@ public interface UserService extends Use
      */
     boolean isValidReminderRequest(String forgotPasswordHash, int nrOfMinutesValid);
 
+    /**
+     * Registers a relationship between two user objects.
+     *
+     * @param user is the user who sends to friend request
+     * @param friend is the user who receives the friend request
+     * @return true is the relationship request was successful
+     */
+    boolean addFriend(String user, String friend);
+
+    /**
+     * Removes the relationship between two user objects.
+     *
+     * @param user is the user who wants to remove a friend
+     * @param friend is the user who is removed from the friends list of user
+     */
+    void removeFriend(String user, String friend);
+
+    /**
+     * Get the friends and friends requests of a particular user.
+     *
+     * @param user is the user whose friends and requests are to be found
+     * @return A hashmap which contains 2 lists of peeple
+     * 			1 containing friends
+     * 			2 containing friend requests
+     */
+	HashMap<String, List<Person>> getFriends(String username);
 
 }
\ No newline at end of file

Modified: rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultUserService.java
URL: http://svn.apache.org/viewvc/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultUserService.java?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultUserService.java (original)
+++ rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultUserService.java Mon Aug 13 19:04:12 2012
@@ -20,12 +20,26 @@
 package org.apache.rave.portal.service.impl;
 
 
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.commons.lang.StringUtils;
 import org.apache.rave.portal.model.PageType;
 import org.apache.rave.portal.model.Person;
 import org.apache.rave.portal.model.User;
 import org.apache.rave.portal.model.util.SearchResult;
-import org.apache.rave.portal.repository.*;
+import org.apache.rave.portal.repository.CategoryRepository;
+import org.apache.rave.portal.repository.PageRepository;
+import org.apache.rave.portal.repository.PageTemplateRepository;
+import org.apache.rave.portal.repository.PersonRepository;
+import org.apache.rave.portal.repository.UserRepository;
+import org.apache.rave.portal.repository.WidgetCommentRepository;
+import org.apache.rave.portal.repository.WidgetRatingRepository;
+import org.apache.rave.portal.repository.WidgetRepository;
 import org.apache.rave.portal.service.EmailService;
 import org.apache.rave.portal.service.UserService;
 import org.slf4j.Logger;
@@ -45,8 +59,6 @@ import org.springframework.security.cryp
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.util.*;
-
 /**
  *
  */
@@ -61,6 +73,7 @@ public class DefaultUserService implemen
     private final WidgetCommentRepository widgetCommentRepository;
     private final WidgetRepository widgetRepository;
     private final CategoryRepository categoryRepository;
+    private final PersonRepository personRepository;
 
     @Autowired
     private PasswordEncoder passwordEncoder;
@@ -105,7 +118,8 @@ public class DefaultUserService implemen
                               WidgetCommentRepository widgetCommentRepository,
                               WidgetRepository widgetRepository,
                               PageTemplateRepository pageTemplateRepository,
-                              CategoryRepository categoryRepository) {
+                              CategoryRepository categoryRepository,
+                              PersonRepository personRepository) {
         this.userRepository = userRepository;
         this.pageRepository = pageRepository;
         this.widgetRatingRepository = widgetRatingRepository;
@@ -113,6 +127,7 @@ public class DefaultUserService implemen
         this.widgetRepository = widgetRepository;
         this.pageTemplateRepository = pageTemplateRepository;
         this.categoryRepository = categoryRepository;
+        this.personRepository = personRepository;
     }
 
     @Override
@@ -352,4 +367,25 @@ public class DefaultUserService implemen
         }
         return true;
     }
-}
\ No newline at end of file
+
+    @Override
+    @Transactional
+    public boolean addFriend(String friendId, String userId) {
+    	User user = getUserById((long) Integer.parseInt(userId));
+    	User friend = getUserById((long) Integer.parseInt(friendId));
+    	return personRepository.addFriend(friend.toPerson(),user.toPerson());
+    }
+
+    @Override
+    @Transactional
+    public void removeFriend(String friendId, String userId) {
+    	User user = getUserById((long) Integer.parseInt(userId));
+    	User friend = getUserById((long) Integer.parseInt(friendId));
+    	personRepository.removeFriend(friend.toPerson(),user.toPerson());
+    }
+
+    @Override
+    public HashMap<String, List<Person>> getFriends(String username) {
+    	return personRepository.findFriendsAndRequests(username);
+    }
+}

Modified: rave/trunk/rave-components/rave-core/src/test/java/org/apache/rave/portal/service/impl/DefaultUserServiceTest.java
URL: http://svn.apache.org/viewvc/rave/trunk/rave-components/rave-core/src/test/java/org/apache/rave/portal/service/impl/DefaultUserServiceTest.java?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-components/rave-core/src/test/java/org/apache/rave/portal/service/impl/DefaultUserServiceTest.java (original)
+++ rave/trunk/rave-components/rave-core/src/test/java/org/apache/rave/portal/service/impl/DefaultUserServiceTest.java Mon Aug 13 19:04:12 2012
@@ -19,13 +19,43 @@
 
 package org.apache.rave.portal.service.impl;
 
-import org.apache.rave.portal.model.*;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.isA;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.rave.portal.model.Authority;
+import org.apache.rave.portal.model.Page;
+import org.apache.rave.portal.model.PageTemplate;
+import org.apache.rave.portal.model.PageType;
+import org.apache.rave.portal.model.Person;
+import org.apache.rave.portal.model.User;
 import org.apache.rave.portal.model.impl.AuthorityImpl;
 import org.apache.rave.portal.model.impl.PageImpl;
 import org.apache.rave.portal.model.impl.PageTemplateImpl;
 import org.apache.rave.portal.model.impl.UserImpl;
 import org.apache.rave.portal.model.util.SearchResult;
-import org.apache.rave.portal.repository.*;
+import org.apache.rave.portal.repository.CategoryRepository;
+import org.apache.rave.portal.repository.PageRepository;
+import org.apache.rave.portal.repository.PageTemplateRepository;
+import org.apache.rave.portal.repository.PersonRepository;
+import org.apache.rave.portal.repository.UserRepository;
+import org.apache.rave.portal.repository.WidgetCommentRepository;
+import org.apache.rave.portal.repository.WidgetRatingRepository;
+import org.apache.rave.portal.repository.WidgetRepository;
 import org.apache.rave.portal.service.UserService;
 import org.junit.After;
 import org.junit.Before;
@@ -38,14 +68,6 @@ import org.springframework.security.core
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.easymock.EasyMock.*;
-import static org.hamcrest.CoreMatchers.*;
-import static org.hamcrest.CoreMatchers.not;
-import static org.junit.Assert.*;
-
 public class DefaultUserServiceTest {
 
     private static final Long USER_ID = 1234L;
@@ -57,6 +79,7 @@ public class DefaultUserServiceTest {
     private WidgetRatingRepository widgetRatingRepository;
     private WidgetRepository widgetRepository;
     private CategoryRepository categoryRepository;
+    private PersonRepository personRepository;
 
     private static final String USER_NAME = "1234";
     private static final String USER_EMAIL = "test@test.com";
@@ -72,9 +95,10 @@ public class DefaultUserServiceTest {
         widgetRatingRepository = createMock(WidgetRatingRepository.class);
         widgetRepository = createMock(WidgetRepository.class);
         categoryRepository = createMock(CategoryRepository.class);
+        personRepository = createMock(PersonRepository.class);
 
         service = new DefaultUserService(pageRepository, userRepository, widgetRatingRepository, widgetCommentRepository,
-                                         widgetRepository, pageTemplateRepository, categoryRepository);
+                                         widgetRepository, pageTemplateRepository, categoryRepository, personRepository);
     }
 
     @After

Modified: rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/JpaPerson.java
URL: http://svn.apache.org/viewvc/rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/JpaPerson.java?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/JpaPerson.java (original)
+++ rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/JpaPerson.java Mon Aug 13 19:04:12 2012
@@ -52,7 +52,7 @@ import java.util.List;
 @Access(AccessType.FIELD)
 @NamedQueries(value = {
     @NamedQuery(name = JpaPerson.FIND_BY_USERNAME, query = "select p from JpaPerson p where p.username like :username"),
-    @NamedQuery(name = JpaPerson.FIND_FRIENDS_BY_USERNAME, query = "select a.followed from JpaPersonAssociation a where a.follower.username = :username"),
+    @NamedQuery(name = JpaPerson.FIND_FRIENDS_BY_USERNAME, query = "select a.followedby from JpaPersonAssociation a where a.follower.username = :username and a.status = :status"),
     @NamedQuery(name = JpaPerson.FIND_BY_GROUP_MEMBERSHIP, query = "select m from JpaGroup g join g.members m where exists " +
             "(select 'found' from g.members b where b.username = :username) and m.username <> :username")
 })
@@ -63,6 +63,7 @@ public class JpaPerson implements BasicE
     public static final String FIND_FRIENDS_BY_USERNAME = "Person.findFriendsByUsername";
     public static final String FIND_BY_GROUP_MEMBERSHIP = "Person.findByGroupMembership";
     public static final String USERNAME_PARAM = "username";
+    public static final String STATUS_PARAM = "status";
 
     @Id
     @Column(name = "entity_id")
@@ -130,12 +131,6 @@ public class JpaPerson implements BasicE
     protected List<JpaPersonProperty> properties;
 
     @ManyToMany(fetch = FetchType.LAZY)
-    @JoinTable(name = "person_association",
-            joinColumns = @JoinColumn(name = "follower_id", referencedColumnName = "entity_id"),
-            inverseJoinColumns = @JoinColumn(name = "followed_id", referencedColumnName = "entity_id"))
-    protected List<JpaPerson> friends;
-
-    @ManyToMany(fetch = FetchType.LAZY)
     @JoinTable(name = "group_members",
             joinColumns = @JoinColumn(name = "person_id", referencedColumnName = "entity_id"),
             inverseJoinColumns = @JoinColumn(name = "group_id", referencedColumnName = "entity_id"))
@@ -292,23 +287,6 @@ public class JpaPerson implements BasicE
     }
 
     @Override
-    public List<Person> getFriends() {
-        return ConvertingListProxyFactory.createProxyList(Person.class, friends);
-    }
-
-    @Override
-    public void setFriends(List<Person> friends) {
-        if(this.friends == null) {
-            this.friends = new ArrayList<JpaPerson>();
-        }
-        //Ensure that all operations go through the conversion proxy
-        this.getFriends().clear();
-        if(friends != null) {
-            this.getFriends().addAll(friends);
-        }
-    }
-
-    @Override
     public List<Organization> getOrganizations() {
         return ConvertingListProxyFactory.createProxyList(Organization.class, organizations);
     }

Modified: rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/JpaPersonAssociation.java
URL: http://svn.apache.org/viewvc/rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/JpaPersonAssociation.java?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/JpaPersonAssociation.java (original)
+++ rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/JpaPersonAssociation.java Mon Aug 13 19:04:12 2012
@@ -18,9 +18,25 @@
  */
 package org.apache.rave.portal.model;
 
-import org.apache.rave.persistence.BasicEntity;
+import javax.persistence.Access;
+import javax.persistence.AccessType;
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import javax.persistence.TableGenerator;
+import javax.persistence.UniqueConstraint;
 
-import javax.persistence.*;
+import org.apache.rave.persistence.BasicEntity;
 
 /**
  * Represents an association between people
@@ -28,10 +44,18 @@ import javax.persistence.*;
 @Entity
 @Access(AccessType.FIELD)
 @Table(name = "person_association",
-        uniqueConstraints = @UniqueConstraint(columnNames = {"follower_id", "followed_id"})
-)
+        uniqueConstraints = @UniqueConstraint(columnNames = {"follower_id", "followedby_id"}))
+@NamedQueries(value = {
+		@NamedQuery(name = JpaPersonAssociation.REMOVE_FRIEND_BY_USERNAME, query = "select a from JpaPersonAssociation a where a.follower.username = :follower_username and a.followedby.username = :followedby_username"),
+		@NamedQuery(name = JpaPersonAssociation.FIND_FRIENDS_AND_REQUESTS_BY_USERNAME, query = "select a from JpaPersonAssociation a where a.follower.username = :follower_username")
+})
 public class JpaPersonAssociation implements BasicEntity {
 
+    public static final String REMOVE_FRIEND_BY_USERNAME = "PersonAssociation.removeFriendByUsername";
+    public static final String FOLLOWER_USERNAME = "follower_username";
+    public static final String FOLLOWEDBY_USERNAME = "followedby_username";
+    public static final String FIND_FRIENDS_AND_REQUESTS_BY_USERNAME = "PersonAssociation.findFriendsAndRequestsByUsername";
+
     @Id
     @Column(name = "entity_id")
     @GeneratedValue(strategy = GenerationType.TABLE, generator = "personAssociationIdGenerator")
@@ -39,13 +63,18 @@ public class JpaPersonAssociation implem
             valueColumnName = "SEQ_COUNT", pkColumnValue = "person_association", allocationSize = 1, initialValue = 1)
     private Long entityId;
 
-    @OneToOne
+    @ManyToOne
     @JoinColumn(name="follower_id", referencedColumnName = "entity_id")
     JpaPerson follower;
 
-    @OneToOne
-    @JoinColumn(name="followed_id", referencedColumnName = "entity_id")
-    JpaPerson followed;
+    @ManyToOne
+    @JoinColumn(name="followedby_id", referencedColumnName = "entity_id")
+    JpaPerson followedby;
+
+    @Basic
+    @Column(name="status")
+    @Enumerated(EnumType.STRING)
+    private FriendRequestStatus status;
 
     @Override
     public Long getEntityId() {
@@ -57,7 +86,7 @@ public class JpaPersonAssociation implem
         this.entityId = entityId;
     }
 
-    public Person getFollower() {
+    public JpaPerson getFollower() {
         return follower;
     }
 
@@ -65,15 +94,23 @@ public class JpaPersonAssociation implem
         this.follower = follower;
     }
 
-    public JpaPerson getFollowed() {
-        return followed;
+    public JpaPerson getFollowedby() {
+        return followedby;
     }
 
-    public void setFollowed(JpaPerson followed) {
-        this.followed = followed;
+    public void setFollowedby(JpaPerson followedby) {
+        this.followedby = followedby;
     }
 
-    @Override
+    public FriendRequestStatus getStatus() {
+		return status;
+	}
+
+	public void setStatus(FriendRequestStatus status) {
+		this.status = status;
+	}
+
+	@Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;

Modified: rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/JpaUser.java
URL: http://svn.apache.org/viewvc/rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/JpaUser.java?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/JpaUser.java (original)
+++ rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/JpaUser.java Mon Aug 13 19:04:12 2012
@@ -118,7 +118,7 @@ public class JpaUser extends JpaPerson i
 
     @OneToMany(targetEntity=JpaPageUser.class, fetch = FetchType.LAZY, mappedBy="user", orphanRemoval=true)
     private List<JpaPageUser> pageUsers;
-    
+
     @OneToMany(targetEntity=JpaWidgetTag.class, fetch = FetchType.LAZY, mappedBy="user", orphanRemoval=true)
     private List<JpaWidgetTag> widgetTags;
 
@@ -401,7 +401,6 @@ public class JpaUser extends JpaPerson i
         p.setDisplayName(this.getDisplayName());
         p.setEmail(this.getEmail());
         p.setFamilyName(this.getFamilyName());
-        p.setFriends(this.getFriends());
         p.setGivenName(this.getGivenName());
         p.setHonorificPrefix(this.getHonorificPrefix());
         p.setHonorificSuffix(this.getHonorificSuffix());

Modified: rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/conversion/JpaPersonConverter.java
URL: http://svn.apache.org/viewvc/rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/conversion/JpaPersonConverter.java?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/conversion/JpaPersonConverter.java (original)
+++ rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/conversion/JpaPersonConverter.java Mon Aug 13 19:04:12 2012
@@ -77,6 +77,5 @@ public class JpaPersonConverter implemen
         converted.setAddresses(source.getAddresses());
         converted.setOrganizations(source.getOrganizations());
         converted.setProperties(source.getProperties());
-        converted.setFriends(source.getFriends());
     }
 }

Modified: rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/conversion/JpaUserConverter.java
URL: http://svn.apache.org/viewvc/rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/conversion/JpaUserConverter.java?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/conversion/JpaUserConverter.java (original)
+++ rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/model/conversion/JpaUserConverter.java Mon Aug 13 19:04:12 2012
@@ -72,7 +72,6 @@ public class JpaUserConverter implements
         converted.setAddresses(source.getAddresses());
         converted.setOrganizations(source.getOrganizations());
         converted.setProperties(source.getProperties());
-        converted.setFriends(source.getFriends());
         converted.setPassword(source.getPassword());
         converted.setConfirmPassword(source.getConfirmPassword());
         converted.setDefaultPageLayout(source.getDefaultPageLayout());

Modified: rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/repository/impl/JpaPersonRepository.java
URL: http://svn.apache.org/viewvc/rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/repository/impl/JpaPersonRepository.java?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/repository/impl/JpaPersonRepository.java (original)
+++ rave/trunk/rave-components/rave-jpa/src/main/java/org/apache/rave/portal/repository/impl/JpaPersonRepository.java Mon Aug 13 19:04:12 2012
@@ -23,16 +23,18 @@ import static org.apache.rave.persistenc
 import static org.apache.rave.persistence.jpa.util.JpaUtil.saveOrUpdate;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
 import javax.persistence.TypedQuery;
 
-import org.apache.commons.lang.StringUtils;
 import org.apache.rave.exception.NotSupportedException;
+import org.apache.rave.portal.model.FriendRequestStatus;
 import org.apache.rave.portal.model.JpaGroup;
 import org.apache.rave.portal.model.JpaPerson;
+import org.apache.rave.portal.model.JpaPersonAssociation;
 import org.apache.rave.portal.model.JpaUser;
 import org.apache.rave.portal.model.JpaWidget;
 import org.apache.rave.portal.model.Person;
@@ -87,6 +89,7 @@ public class JpaPersonRepository impleme
     public List<Person> findFriends(String username) {
         TypedQuery<JpaPerson> friends = manager.createNamedQuery(JpaPerson.FIND_FRIENDS_BY_USERNAME, JpaPerson.class);
         friends.setParameter(JpaPerson.USERNAME_PARAM, username);
+        friends.setParameter(JpaPerson.STATUS_PARAM, FriendRequestStatus.ACCEPTED);
         return CollectionUtils.<Person>toBaseTypedList(friends.getResultList());
     }
 
@@ -169,4 +172,54 @@ public class JpaPersonRepository impleme
     public void delete(Person item) {
         manager.remove(item instanceof JpaPerson ? item : findByUsername(item.getUsername()));
     }
+
+	@Override
+	public boolean addFriend(Person friend, Person user) {
+		JpaPersonAssociation item = new JpaPersonAssociation();
+		JpaPersonAssociation inverseItem = new JpaPersonAssociation();
+		item.setFollowedby(personConverter.convert(friend));
+		item.setFollower(personConverter.convert(user));
+		item.setStatus(FriendRequestStatus.PENDING);
+		item = saveOrUpdate(item.getEntityId(), manager, item);
+		inverseItem.setFollowedby(personConverter.convert(user));
+		inverseItem.setFollower(personConverter.convert(friend));
+		inverseItem.setStatus(FriendRequestStatus.PENDING);
+		inverseItem = saveOrUpdate(inverseItem.getEntityId(), manager, inverseItem);
+		if(item.getEntityId()!=null && inverseItem.getEntityId()!=null)
+			return true;
+		else
+			return false;
+	}
+
+	@Override
+	public void removeFriend(Person friend, Person user) {
+		TypedQuery<JpaPersonAssociation> query = manager.createNamedQuery(JpaPersonAssociation.REMOVE_FRIEND_BY_USERNAME, JpaPersonAssociation.class);
+        query.setParameter(JpaPersonAssociation.FOLLOWER_USERNAME, user.getUsername());
+        query.setParameter(JpaPersonAssociation.FOLLOWEDBY_USERNAME, friend.getUsername());
+        JpaPersonAssociation item = getSingleResult(query.getResultList());
+        manager.remove(item);
+		query = manager.createNamedQuery(JpaPersonAssociation.REMOVE_FRIEND_BY_USERNAME, JpaPersonAssociation.class);
+        query.setParameter(JpaPersonAssociation.FOLLOWER_USERNAME, friend.getUsername());
+        query.setParameter(JpaPersonAssociation.FOLLOWEDBY_USERNAME, user.getUsername());
+        JpaPersonAssociation inverseItem = getSingleResult(query.getResultList());
+        manager.remove(inverseItem);
+	}
+
+	@Override
+	public HashMap<String, List<Person>> findFriendsAndRequests(String username) {
+		List<Person> friends = new ArrayList<Person>();
+		List<Person> friendRequests = new ArrayList<Person>();
+		HashMap<String, List<Person>> friendsAndRequests = new HashMap<String, List<Person>>();
+		TypedQuery<JpaPersonAssociation> associationItems = manager.createNamedQuery(JpaPersonAssociation.FIND_FRIENDS_AND_REQUESTS_BY_USERNAME, JpaPersonAssociation.class);
+		associationItems.setParameter(JpaPersonAssociation.FOLLOWER_USERNAME, username);
+		for(JpaPersonAssociation item : associationItems.getResultList()) {
+			if(item.getStatus().equals(FriendRequestStatus.ACCEPTED))
+				friends.add(item.getFollowedby());
+			else if(item.getStatus().equals(FriendRequestStatus.PENDING))
+				friendRequests.add(item.getFollowedby());
+		}
+		friendsAndRequests.put(FriendRequestStatus.ACCEPTED.toString(), friends);
+		friendsAndRequests.put(FriendRequestStatus.PENDING.toString(), friendRequests);
+        return friendsAndRequests;
+	}
 }

Modified: rave/trunk/rave-components/rave-jpa/src/test/java/org/apache/rave/portal/model/conversion/JpaPersonConverterTest.java
URL: http://svn.apache.org/viewvc/rave/trunk/rave-components/rave-jpa/src/test/java/org/apache/rave/portal/model/conversion/JpaPersonConverterTest.java?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-components/rave-jpa/src/test/java/org/apache/rave/portal/model/conversion/JpaPersonConverterTest.java (original)
+++ rave/trunk/rave-components/rave-jpa/src/test/java/org/apache/rave/portal/model/conversion/JpaPersonConverterTest.java Mon Aug 13 19:04:12 2012
@@ -74,7 +74,6 @@ public class JpaPersonConverterTest {
         template.setAddresses(new ArrayList<Address>());
         template.setOrganizations(new ArrayList<Organization>());
         template.setProperties(new ArrayList<PersonProperty>());
-        template.setFriends(new ArrayList<Person>());
 
         JpaPerson jpaTemplate = converter.convert(template);
 
@@ -94,7 +93,6 @@ public class JpaPersonConverterTest {
         assertThat(jpaTemplate.getAddresses(), is(equalTo(template.getAddresses())));
         assertThat(jpaTemplate.getOrganizations(), is(equalTo(template.getOrganizations())));
         assertThat(jpaTemplate.getProperties(), is(equalTo(template.getProperties())));
-        assertThat(jpaTemplate.getFriends(), is(equalTo(template.getFriends())));
     }
 
 }

Modified: rave/trunk/rave-components/rave-jpa/src/test/java/org/apache/rave/portal/model/conversion/JpaUserConverterTest.java
URL: http://svn.apache.org/viewvc/rave/trunk/rave-components/rave-jpa/src/test/java/org/apache/rave/portal/model/conversion/JpaUserConverterTest.java?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-components/rave-jpa/src/test/java/org/apache/rave/portal/model/conversion/JpaUserConverterTest.java (original)
+++ rave/trunk/rave-components/rave-jpa/src/test/java/org/apache/rave/portal/model/conversion/JpaUserConverterTest.java Mon Aug 13 19:04:12 2012
@@ -78,7 +78,6 @@ public class JpaUserConverterTest {
         template.setAddresses(new ArrayList<Address>());
         template.setOrganizations(new ArrayList<Organization>());
         template.setProperties(new ArrayList<PersonProperty>());
-        template.setFriends(new ArrayList<Person>());
         template.setPassword("TEST_L");
         template.setConfirmPassword("TEST_M");
         template.setDefaultPageLayout(new PageLayoutImpl("CODE"));
@@ -111,7 +110,6 @@ public class JpaUserConverterTest {
         assertThat(jpaTemplate.getAddresses(), is(equalTo(template.getAddresses())));
         assertThat(jpaTemplate.getOrganizations(), is(equalTo(template.getOrganizations())));
         assertThat(jpaTemplate.getProperties(), is(equalTo(template.getProperties())));
-        assertThat(jpaTemplate.getFriends(), is(equalTo(template.getFriends())));
         assertThat(jpaTemplate.getPassword(), is(equalTo(template.getPassword())));
         assertThat(jpaTemplate.getConfirmPassword(), is(equalTo(template.getConfirmPassword())));
         assertThat(jpaTemplate.getDefaultPageLayout().getCode(), is(equalTo(template.getDefaultPageLayout().getCode())));

Modified: rave/trunk/rave-components/rave-jpa/src/test/resources/test_data.sql
URL: http://svn.apache.org/viewvc/rave/trunk/rave-components/rave-jpa/src/test/resources/test_data.sql?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-components/rave-jpa/src/test/resources/test_data.sql (original)
+++ rave/trunk/rave-components/rave-jpa/src/test/resources/test_data.sql Mon Aug 13 19:04:12 2012
@@ -1217,33 +1217,63 @@ VALUES (@application_data_id_1, '12345',
 UPDATE RAVE_SHINDIG_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @application_data_seq;
 
 set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
-INSERT INTO person_association(entity_id, follower_id, followed_id)
-VALUES (@next_person_association, @user_id_1, @user_id_2);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_1, @user_id_2, 'ACCEPTED');
 UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
 
 set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
-INSERT INTO person_association(entity_id, follower_id, followed_id)
-VALUES (@next_person_association, @user_id_1, @user_id_3);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_1, @user_id_3, 'ACCEPTED');
 UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
 
 set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
-INSERT INTO person_association(entity_id, follower_id, followed_id)
-VALUES (@next_person_association, @user_id_1, @user_id_4);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_1, @user_id_4, 'ACCEPTED');
 UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
 
 set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
-INSERT INTO person_association(entity_id, follower_id, followed_id)
-VALUES (@next_person_association, @user_id_2, @user_id_3);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_2, @user_id_1, 'ACCEPTED');
 UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
 
 set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
-INSERT INTO person_association(entity_id, follower_id, followed_id)
-VALUES (@next_person_association, @user_id_2, @user_id_4);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_2, @user_id_3, 'ACCEPTED');
 UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
 
 set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
-INSERT INTO person_association(entity_id, follower_id, followed_id)
-VALUES (@next_person_association, @user_id_2, @user_id_5);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_2, @user_id_4, 'ACCEPTED');
+UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
+
+set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_2, @user_id_5, 'ACCEPTED');
+UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
+
+set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_3, @user_id_1, 'ACCEPTED');
+UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
+
+set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_3, @user_id_2, 'ACCEPTED');
+UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
+
+set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_4, @user_id_1, 'ACCEPTED');
+UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
+
+set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_4, @user_id_2, 'ACCEPTED');
+UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
+
+set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_5, @user_id_2, 'ACCEPTED');
 UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
 
 set @group_id_1 = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @groups_seq);

Added: rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/api/rpc/PersonApi.java
URL: http://svn.apache.org/viewvc/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/api/rpc/PersonApi.java?rev=1372553&view=auto
==============================================================================
--- rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/api/rpc/PersonApi.java (added)
+++ rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/api/rpc/PersonApi.java Mon Aug 13 19:04:12 2012
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.rave.portal.web.api.rpc;
+
+import java.util.HashMap;
+import java.util.List;
+
+import org.apache.rave.portal.model.Person;
+import org.apache.rave.portal.service.UserService;
+import org.apache.rave.portal.web.api.rpc.model.RpcOperation;
+import org.apache.rave.portal.web.api.rpc.model.RpcResult;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+/**
+ * Defines RPC operations for adding and removing friends
+ */
+@Controller(value="rpcPersonApi")
+@RequestMapping(value = "/api/rpc/person/*")
+public class PersonApi {
+
+    private final UserService userService;
+
+    @Autowired
+    public PersonApi(UserService userService) {
+        this.userService = userService;
+    }
+
+    @ResponseBody
+    @RequestMapping(value = "getFriends", method = RequestMethod.POST)
+    public RpcResult<HashMap<String, List<Person>>> getFriends() {
+        return new RpcOperation<HashMap<String, List<Person>>>() {
+            @Override
+            public HashMap<String, List<Person>> execute() {
+                return userService.getFriends(userService.getAuthenticatedUser().getUsername());
+            }
+        }.getResult();
+    }
+
+    /**
+     * Adds a friend to the current user
+     *
+     * @param friendId
+     *            the userID of the user to add as a friend of the current user
+     * @return true if adding friend was successful or any errors
+     *         encountered while performing the RPC operation
+     */
+    @ResponseBody
+    @RequestMapping(value = "{friendId}/addfriend", method = RequestMethod.POST)
+    public RpcResult<Boolean> addFriend(@PathVariable final String friendId) {
+    	return new RpcOperation<Boolean>() {
+    		@Override
+    		public Boolean execute() {
+    			boolean result = userService.addFriend(friendId, userService.getAuthenticatedUser().getId().toString());
+    			return result;
+    		}
+    	}.getResult();
+    }
+
+    /**
+     * Removes a user from the friends list of a user
+     *
+     * @param friendId
+     *            the userID of the user to remove from the friend list of current user
+     * @return true if removing friend was successful or any errors
+     *         encountered while performing the RPC operation
+     */
+    @ResponseBody
+    @RequestMapping(value = "{friendId}/removefriend", method = RequestMethod.POST)
+    public RpcResult<Boolean> removeFriend(@PathVariable final String friendId) {
+    	return new RpcOperation<Boolean>() {
+    		@Override
+    		public Boolean execute() {
+    			userService.removeFriend(friendId, userService.getAuthenticatedUser().getId().toString());
+    			return true;
+    		}
+    	}.getResult();
+    }
+}
\ No newline at end of file

Modified: rave/trunk/rave-portal-resources/src/main/resources/messages.properties
URL: http://svn.apache.org/viewvc/rave/trunk/rave-portal-resources/src/main/resources/messages.properties?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-portal-resources/src/main/resources/messages.properties (original)
+++ rave/trunk/rave-portal-resources/src/main/resources/messages.properties Mon Aug 13 19:04:12 2012
@@ -364,6 +364,7 @@ _rave_client.api.rpc.empty.search.term=S
 _rave_client.common.add=Add
 _rave_client.common.remove=Remove
 _rave_client.common.cancel=Cancel
+_rave_client.common.cancel.request=Cancel Request
 _rave_client.common.move=Move
 _rave_client.common.save=Save
 _rave_client.common.update=Update
@@ -392,6 +393,8 @@ _rave_client.revoke.share.current.user=Y
 _rave_client.revoke.share.current.user.confirm=Are you sure you wish to remove this shared page?
 _rave_client.grant.editing.user.confirm=Are you sure you wish to give editing rights to the following user?
 _rave_client.revoke.editing.user.confirm=Are you sure you wish to remove editing rights from the following user?
+_rave_client.remove.friend.confirm=Are you sure you wish to remove {0} from your friends list?
+_rave_client.remove.friend.request.confirm=Are you sure you wish to cancel the friend request you sent to {0}?
 _rave_client.no.results.found=No results found
 _rave_client.get.metadata=Get Metadata
 page.general.status=Relationship Status\:

Modified: rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/db/initial_data.sql
URL: http://svn.apache.org/viewvc/rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/db/initial_data.sql?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/db/initial_data.sql (original)
+++ rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/db/initial_data.sql Mon Aug 13 19:04:12 2012
@@ -189,33 +189,63 @@ UPDATE RAVE_PORTAL_SEQUENCES SET seq_cou
 
 -- user association data --
 set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
-INSERT INTO person_association(entity_id, follower_id, followed_id)
-VALUES (@next_person_association, @user_id_1, @user_id_2);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_1, @user_id_2, 'ACCEPTED');
 UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
 
 set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
-INSERT INTO person_association(entity_id, follower_id, followed_id)
-VALUES (@next_person_association, @user_id_1, @user_id_3);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_1, @user_id_3, 'ACCEPTED');
 UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
 
 set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
-INSERT INTO person_association(entity_id, follower_id, followed_id)
-VALUES (@next_person_association, @user_id_1, @user_id_4);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_1, @user_id_4, 'ACCEPTED');
 UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
 
 set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
-INSERT INTO person_association(entity_id, follower_id, followed_id)
-VALUES (@next_person_association, @user_id_2, @user_id_3);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_2, @user_id_1, 'ACCEPTED');
 UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
 
 set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
-INSERT INTO person_association(entity_id, follower_id, followed_id)
-VALUES (@next_person_association, @user_id_2, @user_id_4);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_2, @user_id_3, 'ACCEPTED');
 UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
 
 set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
-INSERT INTO person_association(entity_id, follower_id, followed_id)
-VALUES (@next_person_association, @user_id_2, @user_id_5);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_2, @user_id_4, 'ACCEPTED');
+UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
+
+set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_2, @user_id_5, 'ACCEPTED');
+UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
+
+set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_3, @user_id_1, 'ACCEPTED');
+UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
+
+set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_3, @user_id_2, 'ACCEPTED');
+UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
+
+set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_4, @user_id_1, 'ACCEPTED');
+UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
+
+set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_4, @user_id_2, 'ACCEPTED');
+UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
+
+set @next_person_association = (SELECT seq_count FROM RAVE_PORTAL_SEQUENCES WHERE seq_name = @person_association_seq);
+INSERT INTO person_association(entity_id, follower_id, followedby_id, status)
+VALUES (@next_person_association, @user_id_5, @user_id_2, 'ACCEPTED');
 UPDATE RAVE_PORTAL_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @person_association_seq;
 -- end user association data --
 

Modified: rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/jsp/views/personProfile.jsp
URL: http://svn.apache.org/viewvc/rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/jsp/views/personProfile.jsp?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/jsp/views/personProfile.jsp (original)
+++ rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/jsp/views/personProfile.jsp Mon Aug 13 19:04:12 2012
@@ -165,6 +165,9 @@
             </div>
         </div>
         <div class="span3">
+        	<button type="button" id="addRemoveFriend" value="${page.owner.username}" class="btn btn-primary profile-info-visible">Add/Remove Friends</button>
+        </div>
+        <div class="span3">
             <div>
                 <%-- render the person profile parent page region/widgets --%>
                 <c:forEach var="region" items="${page.regions}" varStatus="status">
@@ -175,6 +178,28 @@
     <div class="clear-float">&nbsp;</div>
     </div>
 </div>
+    <div id="userDialog" class="modal hide" data-backdrop="static">
+        <div class="modal-header">
+            <a href="#" class="close" data-dismiss="modal">&times;</a>
+            <h3><fmt:message key="page.general.search.title"/></h3>
+        </div>
+        <div class="modal-body">
+            <div id="userDialogContent" >
+                <div id="userContent">
+                    <div id="searchControls"><input id="searchTerm" name="searchTerm" type="text"/>
+                        <input id="userSearchButton" value="<fmt:message key="page.store.search.button"/>" type="submit"/>
+                        <input id="clearSearchButton" value="<fmt:message key="admin.clearsearch"/>" type="submit" class="hide"/>
+                    </div>
+                    <div id="userSearchListHeader"></div>
+                    <div id="userSearchListPaging"></div>
+                    <div id="userSearchResults"></div>
+                </div>
+            </div>
+        </div>
+        <div class="modal-footer">
+            <a href="#" class="btn" onclick="$('#userDialog').modal('hide');">Close</a>
+        </div>
+    </div>
 <div class="clear-float">&nbsp;</div>
 
 <portal:register-init-script location="${'AFTER_RAVE'}">

Modified: rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_api.js
URL: http://svn.apache.org/viewvc/rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_api.js?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_api.js (original)
+++ rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_api.js Mon Aug 13 19:04:12 2012
@@ -525,6 +525,46 @@ rave.api = rave.api || (function() {
             }).error(handleError);
         }
 
+        function addFriend(args) {
+        	$.post(rave.getContext() + path + "person/" + args.friendId + "/addfriend",
+                function(result) {
+                    if (result.error) {
+                        handleRpcError(result);
+                    }
+                    else {
+                        if (typeof args.successCallback == 'function') {
+                             args.successCallback(result);
+                        }
+                    }
+               }).error(handleError);
+        }
+        function removeFriend(args) {
+        	$.post(rave.getContext() + path + "person/" + args.friendId + "/removefriend",
+                function(result) {
+                    if (result.error) {
+                        handleRpcError(result);
+                    }
+                    else {
+                        if (typeof args.successCallback == 'function') {
+                             args.successCallback(result);
+                        }
+                    }
+               }).error(handleError);
+        }
+        function getFriends(args){
+            $.post(rave.getContext() + path + "person/getFriends",
+                function(result) {
+                    if (result.error) {
+                        handleRpcError(result);
+                    }
+                    else {
+                        if (typeof args.successCallback == 'function') {
+                            args.successCallback(result);
+                        }
+                    }
+                }).error(handleError);
+        }
+
         return {
             moveWidget : moveWidgetOnPage,
             addWidgetToPage : addWidgetToPage,
@@ -541,7 +581,10 @@ rave.api = rave.api || (function() {
             addMemberToPage : addMemberToPage,
             removeMemberFromPage : removeMemberFromPage,
             updateSharedPageStatus : updateSharedPageStatus,
-            updatePageEditingStatus: updatePageEditingStatus
+            updatePageEditingStatus: updatePageEditingStatus,
+            addFriend : addFriend,
+            removeFriend : removeFriend,
+            getFriends :getFriends
         };
 
     })();

Modified: rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_layout.js
URL: http://svn.apache.org/viewvc/rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_layout.js?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_layout.js (original)
+++ rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_layout.js Mon Aug 13 19:04:12 2012
@@ -528,7 +528,8 @@ rave.layout = rave.layout || (function()
             removeEditingRightsFromMember : removeEditingRightsFromMember,
             confirmPageShare : confirmPageShare,
             acceptShare : acceptShare,
-            declineShare : declineShare
+            declineShare : declineShare,
+            updateParamsInString : updateParamsInString
         }
     })();
 

Modified: rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_person_profile.js
URL: http://svn.apache.org/viewvc/rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_person_profile.js?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_person_profile.js (original)
+++ rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_person_profile.js Mon Aug 13 19:04:12 2012
@@ -46,6 +46,244 @@ rave.personprofile = rave.personprofile 
         });
     }
 
+     function dealWithUserResults(userResults){
+    	 var currentUser = $("#addRemoveFriend").get(0).value;
+         var searchTerm = $('#searchTerm').get(0).value;
+         if(searchTerm == undefined || searchTerm == ""){
+             $('#clearSearchButton').hide();
+         }else{
+             $('#clearSearchButton').show();
+         }
+         var legend;
+         if(userResults.result.resultSet.length < 1){
+             legend = rave.getClientMessage("no.results.found");
+         }else{
+             legend = rave.layout.searchHandler.updateParamsInString(rave.getClientMessage("search.list.result.x.to.y"),
+                 new Array(userResults.result.offset + 1, userResults.result.resultSet.length
+                     + userResults.result.offset, userResults.result.totalResults));
+         }
+         // show the listheader
+         $('#userSearchListHeader').text(legend);
+         var $targetDiv = $('#userSearchResults');
+         $targetDiv.empty();
+         // show the paginator
+         paginate(userResults);
+         //now build the content
+         $targetDiv
+             .append(
+                 $("<table/>")
+                     .addClass("searchdialogcontent")
+                         .append(
+                             $("<tr/>")
+                                 .append(
+                                     $("<td/>")
+                                     .addClass("textcell")
+                                     .append(
+                                         $("<b/>")
+                                         .text(rave.getClientMessage("common.username"))
+                                     )
+                                 )
+                                 .append(
+                                     $("<td/>")
+                                     .addClass("booleancell")
+                                     .append(
+                                         $("<b/>")
+                                         .text("Friend Status")
+                                     )
+                                 )
+                             )
+                     .append(
+                         $("<tbody/>")
+                         .attr("id", "searchResultsBody")
+                     )
+             );
+
+             jQuery.each(userResults.result.resultSet, function() {
+                 $('#searchResultsBody')
+                 .append(
+                     $("<tr/>")
+                     .attr("id", "searchResultRecord")
+                     .append(
+                         $("<td/>")
+                         .text(this.username)
+                     )
+                     .append(
+                         $("<td/>")
+                         .attr("id", "friendStatusButtonHolder" + this.entityId)
+                     )
+                 );
+
+                 if(this.username != currentUser){
+                     // check if already added
+                     if(rave.personprofile.isUserAlreadyFriend(this.username)){
+                         $('#friendStatusButtonHolder'+this.entityId)
+                         .append(
+                             $("<a/>")
+                             .attr("href", "#")
+                             .attr("id", this.entityId)
+                             .attr("onclick", "rave.personprofile.removeFriend("+this.entityId+", '"+this.username+"');")
+                             .text(rave.getClientMessage("common.remove"))
+                         );
+                     // check if already send friend request
+                     }else if(rave.personprofile.hasUserAlreadySentRequest(this.username)){
+                         $('#friendStatusButtonHolder'+this.entityId)
+                         .append(
+                             $("<a/>")
+                             .attr("href", "#")
+                             .attr("id", this.entityId)
+                             .attr("onclick", "rave.personprofile.removeFriendRequest("+this.entityId+", '"+this.username+"');")
+                             .text(rave.getClientMessage("common.cancel.request"))
+                         );
+                     }else {
+                         $('#friendStatusButtonHolder'+this.entityId)
+                         .append(
+                             $("<a/>")
+                             .attr("href", "#")
+                             .attr("id", this.entityId)
+                             .attr("onclick", "rave.personprofile.addFriend("+this.entityId+", '"+this.username+"');")
+                             .text(rave.getClientMessage("common.add"))
+                         );
+                     }
+                 }
+
+     	});
+     }
+
+     function paginate(userResults){
+         var $pagingDiv = $('#userSearchListPaging');
+         $pagingDiv.empty();
+         if(userResults.result.pageSize < userResults.result.totalResults){
+             $pagingDiv.append('<div class="pagination"><ul id="pagingul" >');
+             if(userResults.result.currentPage > 1){
+                 offset = (userResults.result.currentPage - 2) * userResults.result.pageSize;
+                 $('#pagingul').append('<li><a href="#" onclick="rave.api.rpc.getUsers({offset: ' +
+                     offset+', successCallback: function(result)' +
+                         ' {rave.personprofile.dealWithUserResults(result);}});">&lt;</a></li>');
+             }
+             for(var i=1;i<=userResults.result.numberOfPages;i++){
+                 if(i == userResults.result.currentPage){
+                     $('#pagingul').append('<li class="active"><a href="#">'+i+'</a></li>');
+                 }else{
+                     offset = (i - 1) * userResults.result.pageSize;
+                     $('#pagingul').append('<li><a href="#" onclick="rave.api.rpc.getUsers({offset: ' +
+                         offset + ', successCallback: function(result)' +
+                             ' {rave.personprofile.dealWithUserResults(result);}});">' + i + '</a></li>');
+                 }
+             }
+             if (userResults.result.currentPage < userResults.result.numberOfPages){
+                 offset = (userResults.result.currentPage) * userResults.result.pageSize;
+                 $('#pagingul').append('<li><a href="#" onclick="rave.api.rpc.getUsers({offset: ' +
+                     offset + ', successCallback: function(result)' +
+                         ' {rave.personprofile.dealWithUserResults(result);}});">&gt;</a></li>');
+             }
+             $pagingDiv.append('</ul></div>');
+         }
+     }
+
+     // Add a friend to the current user
+     function addFriend(userId, username){
+             $('#friendStatusButtonHolder'+userId).hide();
+             rave.api.rpc.addFriend({friendId : userId,
+                 successCallback: function(result) {
+                     rave.personprofile.addExistingFriendRequest(username);
+                     $('#friendStatusButtonHolder'+userId).empty();
+                     $('#friendStatusButtonHolder'+userId)
+                         .append(
+                                 $("<a/>")
+                                 .attr("href", "#")
+                                 .attr("id", userId)
+                                 .attr("onclick", "rave.personprofile.removeFriendRequest(" +
+                                     userId+", '" + username+"');")
+                                 .text(rave.getClientMessage("common.cancel.request"))
+                         );
+                     $('#friendStatusButtonHolder'+userId).show();
+                 }
+             });
+     }
+
+     // Remove a friend of the current user
+     function removeFriend(userId, username){
+    	 var message = rave.layout.searchHandler.updateParamsInString(rave.getClientMessage("remove.friend.confirm"),
+                 new Array(username));
+         if(confirm(message)){
+	         $('#friendStatusButtonHolder'+userId).hide();
+	         rave.api.rpc.removeFriend({friendId : userId,
+	             successCallback: function(result) {
+	                 rave.personprofile.removeExistingFriend(username);
+	                 $('#friendStatusButtonHolder'+userId).empty();
+	                 $('#friendStatusButtonHolder'+userId)
+	                     .append(
+	                             $("<a/>")
+	                             .attr("href", "#")
+	                             .attr("id", userId)
+	                             .attr("onclick", "rave.personprofile.addFriend(" +
+	                                 userId+", '" + username+"');")
+	                             .text(rave.getClientMessage("common.add"))
+	                     );
+	                 $('#friendStatusButtonHolder'+userId).show();
+	             }
+	         });
+         }
+     }
+
+     // Cancel the friend request already sent to a user
+     function removeFriendRequest(userId, username){
+    	 var message = rave.layout.searchHandler.updateParamsInString(rave.getClientMessage("remove.friend.request.confirm"),
+                 new Array(username));
+         if(confirm(message)){
+	         $('#friendStatusButtonHolder'+userId).hide();
+	         rave.api.rpc.removeFriend({friendId : userId,
+	             successCallback: function(result) {
+	                 rave.personprofile.removeExistingFriendRequest(username);
+	                 $('#friendStatusButtonHolder'+userId).empty();
+	                 $('#friendStatusButtonHolder'+userId)
+	                     .append(
+	                             $("<a/>")
+	                             .attr("href", "#")
+	                             .attr("id", userId)
+	                             .attr("onclick", "rave.personprofile.addFriend(" +
+	                                 userId+", '" + username+"');")
+	                             .text(rave.getClientMessage("common.add"))
+	                     );
+	                 $('#friendStatusButtonHolder'+userId).show();
+	             }
+	         });
+         }
+     }
+     // List with existing friend requests (maintained for the UI)
+     function addExistingFriendRequest(username){
+    	 rave.personprofile.existingRequests.push(username);
+     }
+
+     // Remove a friend from the list of existing friends(maintained for the UI)
+     function removeExistingFriend(friendUsername){
+         rave.personprofile.existingFriends.splice(rave.personprofile.existingFriends.indexOf(friendUsername),1);
+     }
+
+     // Remove a friend request from the list of existing friend requests(maintained for the UI)
+     function removeExistingFriendRequest(friendUsername){
+         rave.personprofile.existingRequests.splice(rave.personprofile.existingRequests.indexOf(friendUsername),1);
+     }
+
+     // Check if the user is already a friend
+     function isUserAlreadyFriend(username){
+         if(rave.personprofile.existingFriends.indexOf(username)>=0){
+             return true;
+         } else {
+        	 return false;
+         }
+     }
+
+     // Check if a friend request is already sent to a particular user
+     function hasUserAlreadySentRequest(username){
+    	 if(rave.personprofile.existingRequests.indexOf(username)>=0){
+             return true;
+         } else {
+        	 return false;
+         }
+     }
+
+
     function initButtons() {
         // setup the edit button if it exists
         var $editButton = $("#profileEdit");
@@ -54,6 +292,40 @@ rave.personprofile = rave.personprofile 
                 rave.api.handler.userProfileEditHandler(true);
             });
         }
+        //user clicks add/remove friend button in the profile page
+        var $friendButton = $("#addRemoveFriend");
+        if ($friendButton) {
+        	$friendButton.click(function() {
+        		rave.personprofile.getFriends({successCallback : function() {
+                    rave.api.rpc.getUsers({offset: 0,
+                        successCallback: function(result) {
+                            dealWithUserResults(result);
+                            $("#userDialog").modal('show');
+                        }
+                    });
+        		}});
+            });
+        }
+        // user clicks "search" in the find users dialog
+        $("#userSearchButton").click(function() {
+            $('#userSearchResults').empty();
+            rave.api.rpc.searchUsers({searchTerm: $('#searchTerm').get(0).value, offset: 0,
+                successCallback: function(result) {
+                    rave.personprofile.dealWithUserResults(result);
+                }
+            });
+        });
+
+        // user clicks "clear search" in the find users dialog
+        $("#clearSearchButton").click(function() {
+            $('#searchTerm').get(0).value = "";
+            $('#userSearchResults').empty();
+            rave.api.rpc.getUsers({offset: 0,
+                successCallback: function(result) {
+                    rave.personprofile.dealWithUserResults(result);
+                }
+            });
+        });
 
         // setup the cancel button if it exists
         var $cancelButton = $("#cancelEdit");
@@ -64,12 +336,43 @@ rave.personprofile = rave.personprofile 
         }
     }
 
+    // Gets the list of friends from the DB when ever the user
+    function getFriends(args) {
+   		rave.personprofile.existingFriends = new Array();
+    	rave.personprofile.existingRequests = new Array();
+    	rave.api.rpc.getFriends({
+            successCallback: function(result) {
+            	jQuery.each(result.result.accepted, function() {
+            		if(!rave.personprofile.isUserAlreadyFriend(this.username))
+            			rave.personprofile.existingFriends.push(this.username);
+            	});
+            	jQuery.each(result.result.pending, function() {
+            		if(!rave.personprofile.hasUserAlreadySentRequest(this.username))
+            			rave.personprofile.existingRequests.push(this.username);
+            	});
+            	if(result !=null && typeof args.successCallback == 'function') {
+                    args.successCallback();
+                }
+            }
+        });
+    }
+
 	function init() {
         initSubPages();
         initButtons();
     }
-	
+
 	return {
-        init : init
+        init : init,
+        dealWithUserResults : dealWithUserResults,
+        addFriend : addFriend,
+        removeFriend : removeFriend,
+        removeExistingFriend : removeExistingFriend,
+        removeFriendRequest : removeFriendRequest,
+        addExistingFriendRequest : addExistingFriendRequest,
+        removeExistingFriendRequest : removeExistingFriendRequest,
+        isUserAlreadyFriend : isUserAlreadyFriend,
+        hasUserAlreadySentRequest :hasUserAlreadySentRequest,
+        getFriends : getFriends
     };
 }());

Modified: rave/trunk/rave-providers/rave-opensocial-provider/rave-opensocial-core/src/main/java/org/apache/rave/opensocial/repository/impl/DecoratingOpenSocialPersonRepository.java
URL: http://svn.apache.org/viewvc/rave/trunk/rave-providers/rave-opensocial-provider/rave-opensocial-core/src/main/java/org/apache/rave/opensocial/repository/impl/DecoratingOpenSocialPersonRepository.java?rev=1372553&r1=1372552&r2=1372553&view=diff
==============================================================================
--- rave/trunk/rave-providers/rave-opensocial-provider/rave-opensocial-core/src/main/java/org/apache/rave/opensocial/repository/impl/DecoratingOpenSocialPersonRepository.java (original)
+++ rave/trunk/rave-providers/rave-opensocial-provider/rave-opensocial-core/src/main/java/org/apache/rave/opensocial/repository/impl/DecoratingOpenSocialPersonRepository.java Mon Aug 13 19:04:12 2012
@@ -27,6 +27,7 @@ import org.apache.shindig.protocol.model
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Repository;
 
+import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -45,8 +46,7 @@ public class DecoratingOpenSocialPersonR
 
     @Override
     public List<Person> findFriends(String username, String field, FilterOperation operation, String value) {
-        throw new NotSupportedException();
-    }
+        throw new NotSupportedException();    }
 
     @Override
     public List<Person> findByGroup(String groupId, String field, FilterOperation operation, String value) {
@@ -122,4 +122,19 @@ public class DecoratingOpenSocialPersonR
     public void delete(Person item) {
         underlying.delete(item);
     }
+
+	@Override
+	public boolean addFriend(Person friend, Person user) {
+		return underlying.addFriend(friend, user);
+	}
+
+	@Override
+	public void removeFriend(Person friend, Person user) {
+		underlying.removeFriend(friend, user);
+	}
+
+	@Override
+	public HashMap<String, List<Person>> findFriendsAndRequests(String username) {
+		return underlying.findFriendsAndRequests(username);
+	}
 }