diff --git a/config/alfresco/model/contentModel.xml b/config/alfresco/model/contentModel.xml
index ddd75d278a..6ee8bb0889 100644
--- a/config/alfresco/model/contentModel.xml
+++ b/config/alfresco/model/contentModel.xml
@@ -435,7 +435,6 @@
Rating
d:float
true
- true
true
true
@@ -446,7 +445,6 @@
Rating Scheme
d:text
true
- true
true
true
@@ -457,7 +455,6 @@
Rated at
d:datetime
true
- true
true
true
diff --git a/source/java/org/alfresco/repo/rating/RatingNodeProperties.java b/source/java/org/alfresco/repo/rating/RatingNodeProperties.java
deleted file mode 100644
index ccf2077374..0000000000
--- a/source/java/org/alfresco/repo/rating/RatingNodeProperties.java
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * Copyright (C) 2005-2010 Alfresco Software Limited.
- *
- * This file is part of Alfresco
- *
- * Alfresco is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Alfresco is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Alfresco. If not, see .
- */
-package org.alfresco.repo.rating;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.alfresco.model.ContentModel;
-import org.alfresco.service.cmr.rating.RatingServiceException;
-import org.alfresco.service.namespace.QName;
-
-/**
- * The cm:rating
content type defines the properties which are used in
- * managing user-applied ratings. See contentModel.xml
for details.
- *
- * Briefly, these properties are multi-valued where a 'slice' through the property
- * set constitutes an individual user-applied rating. So if we were to take the value
- * at index n of each multi-valued property, that would represent the
- * score, appliedAt and scheme for one rating.
- *
- * This class simplifies and centralises the handling of these property sets.
- *
- * @author Neil McErlean
- * @since 3.4
- */
-public class RatingNodeProperties
-{
- // These lists are declared as the concrete type 'ArrayList' as they must implement Serializable
- private final ArrayList schemes;
- private final ArrayList scores;
- private final ArrayList dates;
-
- public RatingNodeProperties(List schemes, List scores, List dates)
- {
- // No null lists.
- if (schemes == null) schemes = new ArrayList();
- if (scores == null) scores = new ArrayList();
- if (dates == null) dates = new ArrayList();
-
- // All lists must be the same length.
- if (scores.size() != schemes.size() || dates.size() != schemes.size())
- {
- throw new RatingServiceException("Rating node properties have unequal list lengths: "
- + schemes.size() + " " + scores.size() + " " + dates.size());
- }
-
- // Copy all these data to ensure no leakage of this class' state into the original properties.
- this.schemes = new ArrayList(schemes.size());
- for (String s : schemes)
- this.schemes.add(s);
-
- this.scores = new ArrayList(scores.size());
- for (Float i : scores)
- this.scores.add(i);
-
- this.dates = new ArrayList(dates.size());
- // We can't copy Dates straight over as Date objects are mutable, so clone.
- for (Date d : dates)
- this.dates.add((Date)d.clone());
- }
-
- /**
- * This factory method creates a new {@link RatingNodeProperties} from the specified
- * properties map.
- * @param props
- * @return
- */
- @SuppressWarnings("unchecked")
- public static RatingNodeProperties createFrom(Map props)
- {
- List schemes = (List) props.get(ContentModel.PROP_RATING_SCHEME);
- List scores = (List)props.get(ContentModel.PROP_RATING_SCORE);
- List dates = (List) props.get(ContentModel.PROP_RATED_AT);
-
- return new RatingNodeProperties(schemes, scores, dates);
- }
-
- /**
- * This method returns the number of ratings currently held by the multivalued properties.
- * @return the number of ratings.
- */
- public int size()
- {
- return this.schemes.size();
- }
-
- /**
- * This method gets the {@link Rating} for the specified index.
- * @param index
- * @return
- */
- public RatingStruct getRatingAt(int index)
- {
- String scheme = this.schemes.get(index);
- float score = this.scores.get(index);
- Date d = this.dates.get(index);
- return new RatingStruct(scheme, score, d);
- }
-
- /**
- * This method appends a new rating in the specified schemeName. The ratedAt date
- * will be set to 'now'.
- *
- * @param schemeName the scheme name.
- * @param score the score.
- */
- public void appendRating(String schemeName, float score)
- {
- this.schemes.add(schemeName);
- this.scores.add(score);
- this.dates.add(new Date());
- }
-
- /**
- * This method sets the rating at the specified index.
- * Note that to persist these changes, {@link RatingNodeProperties#toNodeProperties()}
- * can be called to retrieve a property map which should then be saved via the
- * {@link NodeService} in the usual way.
- *
- * The ratedAt property will be automatically set to 'now'.
- * @param index the index at which the change is to be made.
- * @param scheme the new rating scheme name.
- * @param score the new rating score.
- */
- public void setRatingAt(int index, String scheme, float score)
- {
- this.schemes.set(index, scheme);
- this.scores.set(index, score);
- this.dates.set(index, new Date());
- }
-
- /**
- * This method removes the rating at the specified index.
- * @param index
- * @return the removed rating data.
- */
- public RatingStruct removeRatingAt(int index)
- {
- String removedScheme = this.schemes.remove(index);
- float removedScore = this.scores.remove(index);
- Date removedDate = this.dates.remove(index);
-
- return new RatingStruct(removedScheme, removedScore, removedDate);
- }
-
- /**
- * This method returns all ratings as a List of {@link RatingStruct} objects.
- * @return
- */
- public List getAllRatings()
- {
- List result = new ArrayList(schemes.size());
- for (int i = 0; i < schemes.size(); i++)
- {
- result.add(new RatingStruct(schemes.get(i), scores.get(i), dates.get(i)));
- }
- return result;
- }
-
- /**
- * This method gets the rating which has the specified schemeName. There should only
- * be one such rating.
- * @param schemeName
- * @return the requested {@link RatingStruct} if there is one, else null
.
- */
- public RatingStruct getRating(String schemeName)
- {
- for (int i = 0; i < schemes.size(); i ++)
- {
- if (schemes.get(i).equals(schemeName))
- {
- return new RatingStruct(schemes.get(i), scores.get(i), dates.get(i));
- }
- }
- return null;
- }
-
- /**
- * This method returns the index of the rating with the specified scheme name, if there
- * is such a rating.
- * @param schemeName
- * @return the index of the specified rating if there is one, else -1.
- */
- public int getIndexOfRating(String schemeName)
- {
- for (int i = 0; i < schemes.size(); i ++)
- {
- if (schemes.get(i).equals(schemeName))
- {
- return i;
- }
- }
- return -1;
- }
-
- /**
- * This method converts this {@link RatingNodeProperties} object into a Map of
- * properties consistent with the Alfresco ratings content model. See contentModel.xml.
- * These can then be set in the database via the {@link NodeService} in the usual way.
- * @return
- */
- public Map toNodeProperties()
- {
- Map results = new HashMap();
- results.put(ContentModel.PROP_RATING_SCHEME, this.schemes);
- results.put(ContentModel.PROP_RATING_SCORE, this.scores);
- results.put(ContentModel.PROP_RATED_AT, this.dates);
-
- return results;
- }
-
- /**
- * A simple struct class to help in handling the related properties within a cm:rating.
- * @author Neil Mc Erlean.
- *
- */
- public class RatingStruct
- {
- private String scheme;
- private float score;
- private Date date;
-
- public RatingStruct(String scheme, float score, Date d)
- {
- RatingStruct.this.scheme = scheme;
- RatingStruct.this.score = score;
- RatingStruct.this.date = d;
- }
-
- public String getScheme()
- {
- return scheme;
- }
-
- public float getScore()
- {
- return score;
- }
-
- public Date getDate()
- {
- return date;
- }
-
- @Override
- public boolean equals(Object thatObj)
- {
- if (thatObj == null || thatObj.getClass().equals(RatingStruct.this.getClass()) == false)
- {
- return false;
- }
- RatingStruct that = (RatingStruct)thatObj;
- return RatingStruct.this.scheme.equals(that.scheme) &&
- RatingStruct.this.score == that.score &&
- RatingStruct.this.date.equals(that.date);
- }
-
- @Override
- public int hashCode()
- {
- return scheme.hashCode() + (int)(7 * score) + 11 * date.hashCode();
- }
-
- @Override
- public String toString()
- {
- StringBuilder msg = new StringBuilder();
- msg.append(RatingStruct.this.getClass().getSimpleName())
- .append(" '").append(scheme).append("' ")
- .append(score).append(" ").append(date);
- return msg.toString();
- }
-}
-}
diff --git a/source/java/org/alfresco/repo/rating/RatingNodePropertiesTest.java b/source/java/org/alfresco/repo/rating/RatingNodePropertiesTest.java
deleted file mode 100644
index efb8030079..0000000000
--- a/source/java/org/alfresco/repo/rating/RatingNodePropertiesTest.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2005-2010 Alfresco Software Limited.
- *
- * This file is part of Alfresco
- *
- * Alfresco is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Alfresco is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Alfresco. If not, see .
- */
-
-package org.alfresco.repo.rating;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.alfresco.model.ContentModel;
-import org.alfresco.repo.rating.RatingNodeProperties.RatingStruct;
-import org.alfresco.service.namespace.QName;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Unit Test class for {@link RatingNodeProperties}.
- *
- * @author Neil Mc Erlean
- * @since 3.4
- */
-public class RatingNodePropertiesTest
-{
- private static final String LIKE = "like";
- private static final String FIVESTAR = "fivestar";
-
- // These are declared as ArrayLists in order to be Serializable.
- private ArrayList testSchemes;
- private ArrayList testScores;
- private ArrayList testDates;
- private RatingNodeProperties testProps;
-
- @Before public void initTestData()
- {
- testSchemes = new ArrayList();
- testScores = new ArrayList();
- testDates = new ArrayList();
- // These correspond to two ratings:
- // '1' in the 'like' scheme at 'now'.
- // '5' in the 'fivestar' scheme at 'now'.
- testSchemes.add(LIKE);
- testSchemes.add(FIVESTAR);
-
- testScores.add(1.0f);
- testScores.add(5.0f);
-
- testDates.add(new Date());
- testDates.add(new Date());
-
- Map alfrescoStyleProps = new HashMap();
- alfrescoStyleProps.put(ContentModel.PROP_RATING_SCHEME, testSchemes);
- alfrescoStyleProps.put(ContentModel.PROP_RATING_SCORE, testScores);
- alfrescoStyleProps.put(ContentModel.PROP_RATED_AT, testDates);
-
- testProps = RatingNodeProperties.createFrom(alfrescoStyleProps);
-
- }
-
- /**
- * This test method checks that constructing a RatingNodeProperties with null-valued
- * property lists works.
- */
- @Test public void noNullPropertyLists() throws Exception
- {
- List schemes = null;
- List scores = null;
- List dates = null;
- RatingNodeProperties nullProps = new RatingNodeProperties(schemes, scores, dates);
-
- assertEquals(0, nullProps.size());
- }
-
- @Test public void constructAndAccessRatings() throws Exception
- {
- assertEquals(2, testProps.size());
- assertEquals(2, testProps.getAllRatings().size());
-
- assertEquals(0, testProps.getIndexOfRating(LIKE));
- assertEquals(-1, testProps.getIndexOfRating("noSuchScheme"));
-
- final RatingStruct firstRating = testProps.getRatingAt(0);
- assertEquals(LIKE, firstRating.getScheme());
- assertEquals(1.0f, firstRating.getScore(), 0.1f);
- final Date recoveredLikeDate = firstRating.getDate();
- assertNotNull(recoveredLikeDate);
-
- final RatingStruct secondRating = testProps.getRatingAt(1);
- assertEquals(FIVESTAR, secondRating.getScheme());
- assertEquals(5, secondRating.getScore(), 0.1f);
- final Date recoveredSecondDate = secondRating.getDate();
- assertNotNull(recoveredSecondDate);
-
- RatingStruct l = testProps.getRating(LIKE);
- assertNotNull(l);
- assertEquals(LIKE, l.getScheme());
- assertEquals(1, l.getScore(), 0.1f);
- assertEquals(recoveredLikeDate, l.getDate());
- }
-
- @Test public void appendRating()
- {
- // Check all is right before we start
- assertEquals(2, testProps.size());
-
- testProps.appendRating("appended", 10);
- assertEquals(3, testProps.size());
-
- assertNotNull(testProps.getRating(LIKE));
- assertNotNull(testProps.getRating(FIVESTAR));
- assertNotNull(testProps.getRating("appended"));
- }
-
- @Test public void removeRating()
- {
- // Check all is right before we start
- assertEquals(2, testProps.size());
-
- // Remove the first rating - should be 'like'
-
- testProps.removeRatingAt(0);
- assertEquals(1, testProps.size());
-
- // Now 'like' should be gone, but 'fivestar' should still be there.
- assertNull(testProps.getRating(LIKE));
- assertNotNull(testProps.getRating(FIVESTAR));
- }
-
- @Test public void replaceRating()
- {
- // Check all is right before we start
- assertEquals(2, testProps.size());
-
- // Replace the first rating - should be 'like'
-
- // There's no such rating scheme as 'foo' but for this unit test it doesn't matter.
- testProps.setRatingAt(0, "foo", 42);
- assertEquals(2, testProps.size());
-
- // Now 'like' should be replaced by 'foo'.
- assertNull(testProps.getRating(LIKE));
- assertNotNull(testProps.getRating(FIVESTAR));
- final RatingStruct fooRating = testProps.getRating("foo");
- assertNotNull(fooRating);
- assertEquals(42, fooRating.getScore(), 0.1f);
- }
-
- @SuppressWarnings("unchecked")
- @Test public void extractAlfrescoNodeProperties()
- {
- Map alfProps = this.testProps.toNodeProperties();
- assertNotNull(alfProps);
- final int numberOfProperties = 3;
- assertEquals(numberOfProperties, alfProps.size());
- final List ratingSchemes = (List)alfProps.get(ContentModel.PROP_RATING_SCHEME);
- final List ratingScores = (List)alfProps.get(ContentModel.PROP_RATING_SCORE);
- final List ratingDates = (List)alfProps.get(ContentModel.PROP_RATED_AT);
- final int numberOfRatings = 2;
- assertEquals(numberOfRatings, ratingSchemes.size());
- assertEquals(numberOfRatings, ratingScores.size());
- assertEquals(numberOfRatings, ratingDates.size());
- assertEquals(Arrays.asList(new String[]{LIKE, FIVESTAR}), ratingSchemes);
- assertEquals(Arrays.asList(new Float[]{1.0f, 5.0f}), ratingScores);
- // No Date checks
- }
-}
diff --git a/source/java/org/alfresco/repo/rating/RatingServiceImpl.java b/source/java/org/alfresco/repo/rating/RatingServiceImpl.java
index 980333d7e6..9fe52263a0 100644
--- a/source/java/org/alfresco/repo/rating/RatingServiceImpl.java
+++ b/source/java/org/alfresco/repo/rating/RatingServiceImpl.java
@@ -20,15 +20,12 @@
package org.alfresco.repo.rating;
import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.model.ContentModel;
-import org.alfresco.repo.rating.RatingNodeProperties.RatingStruct;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.rating.Rating;
import org.alfresco.service.cmr.rating.RatingScheme;
@@ -148,17 +145,16 @@ public class RatingServiceImpl implements RatingService
{
// There are no previous ratings from this user, so we create a new cm:rating child node.
- // These are multivalued properties.
Map ratingProps = new HashMap();
- ratingProps.put(ContentModel.PROP_RATING_SCORE, toSerializableList(new Float[]{rating}));
- ratingProps.put(ContentModel.PROP_RATED_AT, toSerializableList(new Date[]{new Date()}));
- ratingProps.put(ContentModel.PROP_RATING_SCHEME, toSerializableList(new String[]{ratingSchemeName}));
+ ratingProps.put(ContentModel.PROP_RATING_SCORE, rating);
+ ratingProps.put(ContentModel.PROP_RATED_AT, new Date());
+ ratingProps.put(ContentModel.PROP_RATING_SCHEME, ratingSchemeName);
nodeService.createNode(targetNode, ContentModel.ASSOC_RATINGS, assocQName, ContentModel.TYPE_RATING, ratingProps);
}
else
{
- // There are previous ratings by this user. Things are a little more complex.
+ // There are previous ratings by this user.
if (myRatingChildren.size() > 1 && log.isDebugEnabled())
{
log.debug("");
@@ -166,27 +162,33 @@ public class RatingServiceImpl implements RatingService
NodeRef myPreviousRatingsNode = myRatingChildren.get(0).getChildRef();
Map existingProps = nodeService.getProperties(myPreviousRatingsNode);
- RatingNodeProperties ratingProps = RatingNodeProperties.createFrom(existingProps);
+ String existingRatingScheme = (String)existingProps.get(ContentModel.PROP_RATING_SCHEME);
- // If the schemes list already contains an entry matching the rating we're setting
- // we need to delete it and then delete the score and date at the corresponding indexes.
- int indexOfExistingRating = ratingProps.getIndexOfRating(ratingSchemeName);
- if (indexOfExistingRating != -1)
+ // If it's a re-rating in the existing scheme, replace.
+ if (ratingScheme.getName().equals(existingRatingScheme))
{
- ratingProps.removeRatingAt(indexOfExistingRating);
+ Map ratingProps = new HashMap();
+ ratingProps.put(ContentModel.PROP_RATING_SCHEME, ratingSchemeName);
+ ratingProps.put(ContentModel.PROP_RATING_SCORE, rating);
+ ratingProps.put(ContentModel.PROP_RATED_AT, new Date());
+
+ nodeService.setProperties(myPreviousRatingsNode, ratingProps);
+ }
+ // But if it's a new rating in a different scheme, we don't support this scenario.
+ else
+ {
+ StringBuilder msg = new StringBuilder();
+ msg.append("Cannot apply rating ")
+ .append(rating).append(" [")
+ .append(ratingSchemeName).append("] to node ")
+ .append(targetNode).append(". Already rated in ")
+ .append(existingRatingScheme);
+
+ throw new RatingServiceException(msg.toString());
}
-
- ratingProps.appendRating(ratingSchemeName, rating);
-
- nodeService.setProperties(myPreviousRatingsNode, ratingProps.toNodeProperties());
}
}
- private Serializable toSerializableList(Object[] array)
- {
- return (Serializable)Arrays.asList(array);
- }
-
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rating.RatingService#getRatingByCurrentUser(org.alfresco.service.cmr.repository.NodeRef, java.lang.String)
@@ -207,27 +209,26 @@ public class RatingServiceImpl implements RatingService
return null;
}
- // Take the last node pertaining to the current user.
- ChildAssociationRef lastChild = ratingChildren.get(ratingChildren.size() - 1);
- Map properties = nodeService.getProperties(lastChild.getChildRef());
+ // Take the node pertaining to the current user.
+ ChildAssociationRef ratingNodeAssoc = ratingChildren.get(0);
+ Map properties = nodeService.getProperties(ratingNodeAssoc.getChildRef());
// Find the index of the rating scheme we're interested in.
- RatingNodeProperties ratingProps = RatingNodeProperties.createFrom(properties);
- int index = ratingProps.getIndexOfRating(ratingSchemeName);
- if (index == -1)
+ String existingRatingScheme = (String)properties.get(ContentModel.PROP_RATING_SCHEME);
+ if (existingRatingScheme.equals(ratingSchemeName) == false)
{
// There is no rating in this scheme by the specified user.
return null;
}
else
{
- // There is a rating and the associated data are at the index'th place in each multivalued property.
- RatingStruct ratingStruct = ratingProps.getRatingAt(index);
+ Float existingRatingScore = (Float)properties.get(ContentModel.PROP_RATING_SCORE);
+ Date existingRatingDate = (Date)properties.get(ContentModel.PROP_RATED_AT);
- Rating result = new Rating(getRatingScheme(ratingStruct.getScheme()),
- ratingStruct.getScore(),
+ Rating result = new Rating(getRatingScheme(existingRatingScheme),
+ existingRatingScore,
user,
- ratingStruct.getDate());
+ existingRatingDate);
return result;
}
}
@@ -243,39 +244,29 @@ public class RatingServiceImpl implements RatingService
return removeRating(targetNode, ratingScheme, currentUser);
}
- private Rating removeRating(NodeRef targetNode, String ratingSchemeName, String user)
+ private Rating removeRating(NodeRef targetNode, String ratingSchemeName, final String user)
{
List ratingChildren = getRatingNodeChildren(targetNode, user);
if (ratingChildren.isEmpty())
{
- // There are no ratings by any user.
return null;
}
- // Take the last node pertaining to the specified user.
- ChildAssociationRef lastChild = ratingChildren.get(ratingChildren.size() - 1);
+ ChildAssociationRef lastChild = ratingChildren.get(0);
Map properties = nodeService.getProperties(lastChild.getChildRef());
-
- // Find the index of the rating scheme we're interested in.
- RatingNodeProperties ratingProps = RatingNodeProperties.createFrom(properties);
- int index = ratingProps.getIndexOfRating(ratingSchemeName);
- if (index == -1)
+ Rating result = null;
+ // If the rating is for the specified scheme delete it.
+ // Get the scheme name and check it.
+ if (ratingSchemeName.equals(properties.get(ContentModel.PROP_RATING_SCHEME)))
{
- // There is no rating in this scheme by the specified user.
- return null;
- }
- else
- {
- // There is a rating and the associated data are at the index'th place in each property.
- RatingStruct removed = ratingProps.removeRatingAt(index);
-
- // Now apply the properties with one deleted.
- Map props = ratingProps.toNodeProperties();
- nodeService.setProperties(lastChild.getChildRef(), props);
+ Float score = (Float) properties.get(ContentModel.PROP_RATING_SCORE);
+ Date date = (Date)properties.get(ContentModel.PROP_RATED_AT);
- return new Rating(this.getRatingScheme(removed.getScheme()),
- removed.getScore(), user, removed.getDate());
+ nodeService.deleteNode(lastChild.getChildRef());
+ result = new Rating(getRatingScheme(ratingSchemeName), score, user, date);
}
+
+ return result;
}
/*
@@ -284,7 +275,7 @@ public class RatingServiceImpl implements RatingService
*/
public float getTotalRating(NodeRef targetNode, String ratingSchemeName)
{
- //TODO Performance improvement? : put node rating total/count/average into a multi-valued
+ //TODO Performance improvement? : put node rating total/count/average into a
// property in the db.
List ratingsNodes = this.getRatingNodeChildren(targetNode, null);
@@ -295,13 +286,10 @@ public class RatingServiceImpl implements RatingService
float result = 0;
for (ChildAssociationRef ratingsNode : ratingsNodes)
{
- List ratings = getRatingsFrom(ratingsNode.getChildRef());
- for (Rating rating : ratings)
+ Rating rating = getRatingFrom(ratingsNode.getChildRef());
+ if (rating.getScheme().getName().equals(ratingSchemeName))
{
- if (rating.getScheme().getName().equals(ratingSchemeName))
- {
- result += rating.getScore();
- }
+ result += rating.getScore();
}
}
return result;
@@ -319,14 +307,11 @@ public class RatingServiceImpl implements RatingService
float ratingTotal = 0;
for (ChildAssociationRef ratingsNode : ratingsNodes)
{
- List ratings = getRatingsFrom(ratingsNode.getChildRef());
- for (Rating rating : ratings)
+ Rating rating = getRatingFrom(ratingsNode.getChildRef());
+ if (rating.getScheme().getName().equals(ratingSchemeName))
{
- if (rating.getScheme().getName().equals(ratingSchemeName))
- {
- ratingCount++;
- ratingTotal += rating.getScore();
- }
+ ratingCount++;
+ ratingTotal += rating.getScore();
}
}
if (ratingCount == 0)
@@ -350,13 +335,10 @@ public class RatingServiceImpl implements RatingService
int result = 0;
for (ChildAssociationRef ratingsNode : ratingsNodes)
{
- List ratings = getRatingsFrom(ratingsNode.getChildRef());
- for (Rating rating : ratings)
+ Rating rating = getRatingFrom(ratingsNode.getChildRef());
+ if (rating.getScheme().getName().equals(ratingSchemeName))
{
- if (rating.getScheme().getName().equals(ratingSchemeName))
- {
- result++;
- }
+ result++;
}
}
return result;
@@ -390,13 +372,11 @@ public class RatingServiceImpl implements RatingService
}
/**
- * This method returns a List of {@link Rating} objects for the specified cm:rating
- * node. As it's one ratingNode the results will be form one user, but will represent
- * 0..n schemes.
+ * This method returns a {@link Rating} object for the specified cm:rating node.
* @param ratingNode
* @return
*/
- private List getRatingsFrom(NodeRef ratingNode)
+ private Rating getRatingFrom(NodeRef ratingNode)
{
// The appliedBy is encoded in the parent assoc qname.
// It will be the same user for all ratings in this node.
@@ -404,16 +384,12 @@ public class RatingServiceImpl implements RatingService
String appliedBy = parentAssoc.getQName().getLocalName();
Map properties = nodeService.getProperties(ratingNode);
- RatingNodeProperties ratingProps = RatingNodeProperties.createFrom(properties);
- List result = new ArrayList(ratingProps.size());
- for (int i = 0; i < ratingProps.size(); i++)
- {
- final String schemeName = ratingProps.getRatingAt(i).getScheme();
- RatingScheme scheme = getRatingScheme(schemeName);
- result.add(new Rating(scheme, ratingProps.getRatingAt(i).getScore(),
- appliedBy, ratingProps.getRatingAt(i).getDate()));
- }
+ final String schemeName = (String)properties.get(ContentModel.PROP_RATING_SCHEME);
+ final Float score = (Float)properties.get(ContentModel.PROP_RATING_SCORE);
+ final Date ratedAt = (Date)properties.get(ContentModel.PROP_RATED_AT);
+ RatingScheme scheme = getRatingScheme(schemeName);
+ Rating result = new Rating(scheme, score, appliedBy, ratedAt);
return result;
}
}
diff --git a/source/java/org/alfresco/repo/rating/RatingServiceIntegrationTest.java b/source/java/org/alfresco/repo/rating/RatingServiceIntegrationTest.java
index 98879c7a1e..1d2fa76cb4 100644
--- a/source/java/org/alfresco/repo/rating/RatingServiceIntegrationTest.java
+++ b/source/java/org/alfresco/repo/rating/RatingServiceIntegrationTest.java
@@ -20,7 +20,6 @@
package org.alfresco.repo.rating;
import java.io.Serializable;
-import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
@@ -179,7 +178,7 @@ public class RatingServiceIntegrationTest extends BaseAlfrescoSpringTest
fail("Illegal rating " + illegalRating + " should have caused exception.");
}
- public void testApplyUpdateDeleteRatings_SingleUserMultipleSchemes() throws Exception
+ public void testApplyUpdateDeleteRatings() throws Exception
{
// We'll do all this as user 'UserOne'.
AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO);
@@ -189,10 +188,8 @@ public class RatingServiceIntegrationTest extends BaseAlfrescoSpringTest
assertNull("Expected a null rating,", nullRating);
assertNull("Expected a null remove result.", ratingService.removeRatingByCurrentUser(testDoc_Admin, LIKES_SCHEME_NAME));
- final float likesScore = 1;
final float fiveStarScore = 5;
- ratingService.applyRating(testDoc_Admin, likesScore, LIKES_SCHEME_NAME);
ratingService.applyRating(testDoc_Admin, fiveStarScore, FIVE_STAR_SCHEME_NAME);
// Some basic node structure tests.
@@ -213,16 +210,8 @@ public class RatingServiceIntegrationTest extends BaseAlfrescoSpringTest
// Now to check the persisted ratings data are ok.
- Rating likeRating = ratingService.getRatingByCurrentUser(testDoc_Admin, LIKES_SCHEME_NAME);
-
Rating fiveStarRating = ratingService.getRatingByCurrentUser(testDoc_Admin, FIVE_STAR_SCHEME_NAME);
- assertNotNull("'like' rating was null.", likeRating);
- assertEquals("Wrong score for rating", likesScore, likeRating.getScore());
- assertEquals("Wrong user for rating", AuthenticationUtil.getFullyAuthenticatedUser(), likeRating.getAppliedBy());
- final Date likeRatingAppliedAt = likeRating.getAppliedAt();
- assertDateIsCloseToNow(likeRatingAppliedAt);
-
assertNotNull("'5*' rating was null.", fiveStarRating);
assertEquals("Wrong score for rating", fiveStarScore, fiveStarRating.getScore());
assertEquals("Wrong user for rating", AuthenticationUtil.getFullyAuthenticatedUser(), fiveStarRating.getAppliedBy());
@@ -248,27 +237,13 @@ public class RatingServiceIntegrationTest extends BaseAlfrescoSpringTest
// Now to check the updated ratings data are ok.
Rating updatedFiveStarRating = ratingService.getRatingByCurrentUser(testDoc_Admin, FIVE_STAR_SCHEME_NAME);
- // 'like' rating data should be unchanged.
- assertNotNull("'like' rating was null.", likeRating);
- assertEquals("Wrong score for rating", likesScore, likeRating.getScore());
- assertEquals("Wrong user for rating", AuthenticationUtil.getFullyAuthenticatedUser(), likeRating.getAppliedBy());
- assertEquals("Wrong date for rating", likeRatingAppliedAt, likeRating.getAppliedAt());
-
- // But these 'five star' data should be changed - new score, new date
+ // 'five star' data should be changed - new score, new date
assertNotNull("'5*' rating was null.", updatedFiveStarRating);
assertEquals("Wrong score for rating", updatedFiveStarScore, updatedFiveStarRating.getScore());
assertEquals("Wrong user for rating", AuthenticationUtil.getFullyAuthenticatedUser(), updatedFiveStarRating.getAppliedBy());
assertTrue("five star rating date was unchanged.", fiveStarRatingAppliedAt.equals(updatedFiveStarRating.getAppliedAt()) == false);
assertDateIsCloseToNow(updatedFiveStarRating.getAppliedAt());
- // Now we'll delete the 'likes' rating.
- Rating deletedLikesRating = ratingService.removeRatingByCurrentUser(testDoc_Admin, LIKES_SCHEME_NAME);
- // 'like' rating data should be unchanged.
- assertNotNull("'like' rating was null.", deletedLikesRating);
- assertEquals("Wrong score for rating", likesScore, deletedLikesRating.getScore());
- assertEquals("Wrong user for rating", AuthenticationUtil.getFullyAuthenticatedUser(), deletedLikesRating.getAppliedBy());
- assertEquals("Wrong date for rating", likeRatingAppliedAt, deletedLikesRating.getAppliedAt());
-
// And delete the 'five star' rating.
Rating deletedStarRating = ratingService.removeRatingByCurrentUser(testDoc_Admin, FIVE_STAR_SCHEME_NAME);
// 'five star' rating data should be unchanged.
@@ -279,7 +254,6 @@ public class RatingServiceIntegrationTest extends BaseAlfrescoSpringTest
// And the deleted ratings should be gone.
assertNull("5* rating not null.", ratingService.getRatingByCurrentUser(testDoc_Admin, FIVE_STAR_SCHEME_NAME));
- assertNull("likes rating not null", ratingService.getRatingByCurrentUser(testDoc_Admin, LIKES_SCHEME_NAME));
}
/**
@@ -299,6 +273,64 @@ public class RatingServiceIntegrationTest extends BaseAlfrescoSpringTest
// assertTrue("Date was not within " + millisTolerance + "ms of 'now'.", now.getTime() - d.getTime() < millisTolerance);
}
+ public void testOneUserRatesAndRerates() throws Exception
+ {
+ AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE);
+ ratingService.applyRating(testDoc_Admin, 1.0f, FIVE_STAR_SCHEME_NAME);
+
+ // A new score in the same rating scheme by the same user should replace the previous score.
+ ratingService.applyRating(testDoc_Admin, 2.0f, FIVE_STAR_SCHEME_NAME);
+
+ float meanRating = ratingService.getAverageRating(testDoc_Admin, FIVE_STAR_SCHEME_NAME);
+ assertEquals("Document had wrong mean rating.", 2f, meanRating);
+
+ float totalRating = ratingService.getTotalRating(testDoc_Admin, FIVE_STAR_SCHEME_NAME);
+ assertEquals("Document had wrong total rating.", 2.0f, totalRating);
+
+ int ratingsCount = ratingService.getRatingsCount(testDoc_Admin, FIVE_STAR_SCHEME_NAME);
+ assertEquals("Document had wrong ratings count.", 1, ratingsCount);
+
+ // There should only be one rating child node under the rated node.
+ assertEquals("Wrong number of child nodes", 1 , nodeService.getChildAssocs(testDoc_Admin).size());
+ }
+
+ /**
+ * This test method ensures that if a single user attempts to rate a piece of content in two
+ * different rating schemes, then an exception should be thrown.
+ * @throws Exception
+ */
+ public void testOneUserRatesInTwoSchemes() throws Exception
+ {
+ AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE);
+ ratingService.applyRating(testDoc_Admin, 1.0f, FIVE_STAR_SCHEME_NAME);
+
+ // A new score in a different rating scheme by the same user should fail.
+ boolean correctExceptionThrown = false;
+ try
+ {
+ ratingService.applyRating(testDoc_Admin, 2.0f, LIKES_SCHEME_NAME);
+ } catch (RatingServiceException expected)
+ {
+ correctExceptionThrown = true;
+ }
+ if (correctExceptionThrown == false)
+ {
+ fail("Expected exception not thrown.");
+ }
+
+ float meanRating = ratingService.getAverageRating(testDoc_Admin, FIVE_STAR_SCHEME_NAME);
+ assertEquals("Document had wrong mean rating.", 1f, meanRating);
+
+ float totalRating = ratingService.getTotalRating(testDoc_Admin, FIVE_STAR_SCHEME_NAME);
+ assertEquals("Document had wrong total rating.", 1f, totalRating);
+
+ int ratingsCount = ratingService.getRatingsCount(testDoc_Admin, FIVE_STAR_SCHEME_NAME);
+ assertEquals("Document had wrong ratings count.", 1, ratingsCount);
+
+ // There should only be one rating child node under the rated node.
+ assertEquals("Wrong number of child nodes", 1 , nodeService.getChildAssocs(testDoc_Admin).size());
+ }
+
public void testApplyRating_MultipleUsers() throws Exception
{
// 2 different users rating the same piece of content in the same rating scheme
diff --git a/source/java/org/alfresco/repo/rating/script/ScriptRatingService.java b/source/java/org/alfresco/repo/rating/script/ScriptRatingService.java
index c49d833762..ef69d05542 100644
--- a/source/java/org/alfresco/repo/rating/script/ScriptRatingService.java
+++ b/source/java/org/alfresco/repo/rating/script/ScriptRatingService.java
@@ -26,6 +26,7 @@ import org.alfresco.repo.jscript.ScriptNode;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.rating.Rating;
import org.alfresco.service.cmr.rating.RatingService;
+import org.alfresco.service.cmr.rating.RatingServiceException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -92,10 +93,13 @@ public class ScriptRatingService extends BaseScopableProcessorExtension
* Applies the given rating to the specified node using the specified ratingScheme.
* It is the responsibility of the caller to ensure that the rating scheme exists
* and that the rating is within the limits defined for that scheme.
+ * Furthermore, only one rating scheme per user per target node is supported. Any attempt
+ * by one user to apply a second rating in a different scheme will result in a {@link RatingServiceException}.
*
* @param node
* @param rating
* @param ratingSchemeName
+ * @throws RatingServiceException
* @see ScriptRatingService#getMin(String)
* @see ScriptRatingService#getMax(String)
*/
diff --git a/source/java/org/alfresco/service/cmr/rating/RatingService.java b/source/java/org/alfresco/service/cmr/rating/RatingService.java
index 9845f069f5..69226bb200 100644
--- a/source/java/org/alfresco/service/cmr/rating/RatingService.java
+++ b/source/java/org/alfresco/service/cmr/rating/RatingService.java
@@ -64,13 +64,18 @@ public interface RatingService
/**
* This method applies the given rating to the specified target node. If a rating
* from the current user in the specified scheme already exists, it will be replaced.
+ *
+ * Note that only one rating scheme per user per targetNode is supported at present.
+ * If a user attempts to apply a second rating in a different rating scheme to any given
+ * target node, a {@link RatingServiceException} will be thrown.
*
* @param targetNode the node to which the rating is to be applied.
* @param rating the rating which is to be applied.
* @param ratingSchemeName the name of the rating scheme to use.
*
* @throws RatingServiceException if the rating is not within the range defined by the named scheme
- * or if the named scheme is not registered.
+ * or if the named scheme is not registered or if the rating would result
+ * in multiple ratings by the same user.
* @see RatingService#getRatingSchemes()
* @see RatingScheme
*/
@@ -91,6 +96,20 @@ public interface RatingService
@NotAuditable
int getRatingsCount(NodeRef targetNode, String ratingSchemeName);
+ /**
+ * This method gets the total accumulated rating score for
+ * the specified node in the specified {@link RatingScheme}.
+ * That is, the rating scores for all users for the specified
+ * node are summed to give the result.
+ *
+ * @param targetNode the node on which the rating total is sought.
+ * @param ratingScheme the rating scheme to use.
+ *
+ * @return the sum of all individual ratings applied to this node in the specified scheme.
+ * @see RatingService#getRatingSchemes()
+ * @see RatingScheme
+ */
+ @NotAuditable
float getTotalRating(NodeRef targetNode, String ratingSchemeName);
/**
@@ -100,6 +119,7 @@ public interface RatingService
* @param ratingSchemeName the rating scheme name in which the average is defined.
* @return the average (mean) value if there is one, else -1.
*/
+ @NotAuditable
float getAverageRating(NodeRef targetNode, String ratingSchemeName);
/**