/**
* $Id: PollVoteEntityProvider.java 127888 2013-07-29 11:54:18Z steve.swinsburg@gmail.com $
* $URL: https://source.sakaiproject.org/svn/polls/trunk/tool/src/java/org/sakaiproject/poll/tool/entityproviders/PollVoteEntityProvider.java $
* VoteEntityProvider.java - polls - Aug 22, 2008 9:50:39 PM - azeckoski
**************************************************************************
* Copyright (c) 2008, 2009 The Sakai Foundation
*
* Licensed under the Educational Community 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.opensource.org/licenses/ECL-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.sakaiproject.poll.tool.entityproviders;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.entitybroker.EntityReference;
import org.sakaiproject.entitybroker.EntityView;
import org.sakaiproject.entitybroker.entityprovider.CoreEntityProvider;
import org.sakaiproject.entitybroker.entityprovider.annotations.EntityCustomAction;
import org.sakaiproject.entitybroker.entityprovider.capabilities.ActionsExecutable;
import org.sakaiproject.entitybroker.entityprovider.capabilities.CollectionResolvable;
import org.sakaiproject.entitybroker.entityprovider.capabilities.Createable;
import org.sakaiproject.entitybroker.entityprovider.capabilities.Describeable;
import org.sakaiproject.entitybroker.entityprovider.capabilities.Inputable;
import org.sakaiproject.entitybroker.entityprovider.capabilities.Outputable;
import org.sakaiproject.entitybroker.entityprovider.capabilities.Redirectable;
import org.sakaiproject.entitybroker.entityprovider.extension.Formats;
import org.sakaiproject.entitybroker.entityprovider.search.Restriction;
import org.sakaiproject.entitybroker.entityprovider.search.Search;
import org.sakaiproject.entitybroker.exception.EntityException;
import org.sakaiproject.entitybroker.util.AbstractEntityProvider;
import org.sakaiproject.event.api.UsageSession;
import org.sakaiproject.event.api.UsageSessionService;
import org.sakaiproject.poll.logic.PollListManager;
import org.sakaiproject.poll.logic.PollVoteManager;
import org.sakaiproject.poll.model.Option;
import org.sakaiproject.poll.model.Poll;
import org.sakaiproject.poll.model.Vote;
import org.sakaiproject.user.api.UserDirectoryService;
/**
* Entity provider which represents poll votes
*
* @author Aaron Zeckoski (azeckoski @ gmail.com)
*/
public class PollVoteEntityProvider extends AbstractEntityProvider implements CoreEntityProvider,
Createable, CollectionResolvable, Outputable, Inputable, Describeable, ActionsExecutable, Redirectable {
private static Log log = LogFactory.getLog(PollVoteEntityProvider.class);
private PollListManager pollListManager;
public void setPollListManager(final PollListManager pollListManager) {
this.pollListManager = pollListManager;
}
private PollVoteManager pollVoteManager;
public void setPollVoteManager(final PollVoteManager pollVoteManager) {
this.pollVoteManager = pollVoteManager;
}
private UsageSessionService usageSessionService;
public void setUsageSessionService(UsageSessionService usageSessionService) {
this.usageSessionService = usageSessionService;
}
private UserDirectoryService userDirectoryService;
public void setUserDirectoryService(UserDirectoryService userDirectoryService) {
this.userDirectoryService = userDirectoryService;
}
public static final String PREFIX = "poll-vote";
public String getEntityPrefix() {
return PREFIX;
}
@Deprecated
public boolean entityExists(String id) {
if (id == null) {
return false;
}
if ("".equals(id)) {
return true;
}
Vote vote = getVoteById(id);
boolean exists = (vote != null);
return exists;
}
@Deprecated
public String createEntity(EntityReference ref, Object entity, Map<String, Object> params) {
String userId = userDirectoryService.getCurrentUser().getId();
Vote vote = (Vote) entity;
log.debug("got vote: " + vote.toString());
Long pollId = null;
try {
pollId = Long.valueOf((String)params.get("pollId"));
}
catch (Exception e) {
log.warn(e);
}
if (pollId == null) {
throw new IllegalArgumentException("Poll Id must be set to create a vote");
}
vote.setPollId(pollId);
Long optionId = null;
try {
optionId = Long.valueOf((String)params.get("pollOption"));
}
catch (Exception e) {
log.warn(e);
}
if (optionId == null) {
throw new IllegalArgumentException("Poll Option must be set to create a vote");
}
if (! pollVoteManager.isUserAllowedVote(userId, pollId, false)) {
throw new SecurityException("User ("+userId+") is not allowed to vote in this poll ("+pollId+")");
}
vote.setPollOption(optionId);
// validate option
Option option = pollListManager.getOptionById(vote.getPollOption());
if (option == null) {
throw new IllegalArgumentException("Invalid poll option ("+vote.getPollOption()+") [cannot find option] in vote ("+vote+") for user ("+userId+")");
} else {
if (! pollId.equals(option.getPollId())) {
throw new IllegalArgumentException("Invalid poll option ("+vote.getPollOption()+") [not in poll ("+pollId+")] in vote ("+vote+") for user ("+userId+")");
}
}
// set default vote values
vote.setVoteDate( new Date() );
vote.setUserId(userId);
if (vote.getSubmissionId() == null) {
String sid = userId + ":" + UUID.randomUUID();
vote.setSubmissionId(sid);
}
// set the IP address
UsageSession usageSession = usageSessionService.getSession();
if (usageSession != null) {
vote.setIp( usageSession.getIpAddress() );
}
boolean saved = pollVoteManager.saveVote(vote);
if (!saved) {
throw new IllegalStateException("Unable to save vote ("+vote+") for user ("+userId+"): " + ref);
}
return vote.getId()+"";
}
public Object getSampleEntity() {
return new Vote();
}
@Deprecated
public Object getEntity(EntityReference ref) {
String id = ref.getId();
String currentUser = developerHelperService.getCurrentUserReference();
log.debug("current user is: " + currentUser);
if (currentUser == null || currentUser.length() == 0) {
throw new EntityException("Anonymous users cannot view specific votes", ref.getId(), HttpServletResponse.SC_UNAUTHORIZED);
}
//is this a new object?
if (ref.getId() == null) {
new Vote();
}
Vote vote = getVoteById(id);
String userId = developerHelperService.getUserIdFromRef(currentUser);
if (developerHelperService.isUserAdmin(currentUser)) {
// ok to view this vote
} else if (userId.equals(vote.getUserId())) {
// ok to view own
} else if (developerHelperService.isEntityRequestInternal(ref.toString())) {
// ok for all internal requests
} else {
// TODO - check vote location and perm?
// not allowed to view
throw new SecurityException("User ("+currentUser+") cannot view vote ("+ref+")");
}
if (id == null) {
return new Vote();
}
return vote;
}
@Deprecated
public List<?> getEntities(EntityReference ref, Search search) {
String currentUserId = userDirectoryService.getCurrentUser().getId();
Restriction pollRes = search.getRestrictionByProperty("pollId");
if (pollRes == null || pollRes.getSingleValue() == null) {
// throw new IllegalArgumentException("Must include a non-null pollId in order to retreive a list of votes");
return null;
}
Long pollId = null;
boolean viewVoters = false;
if (developerHelperService.isUserAdmin(developerHelperService.getCurrentUserReference())) {
viewVoters = true;
}
try {
pollId = developerHelperService.convert(pollRes.getSingleValue(), Long.class);
} catch (UnsupportedOperationException e) {
throw new IllegalArgumentException("Invalid: pollId must be a long number: " + e.getMessage(), e);
}
Poll poll = pollListManager.getPollById(pollId);
if (poll == null) {
throw new IllegalArgumentException("pollId ("+pollId+") is invalid and does not match any known polls");
}
List<Vote> votes = pollVoteManager.getAllVotesForPoll(poll);
if (developerHelperService.isEntityRequestInternal(ref.toString())) {
// ok for all internal requests
} else if (!pollListManager.isAllowedViewResults(poll, currentUserId)) {
// TODO - check vote location and perm?
// not allowed to view
throw new SecurityException("User ("+currentUserId+") cannot view vote ("+ref+")");
}
if (viewVoters)
return votes;
else
return anonymizeVotes(votes);
}
@Deprecated
private List<?> anonymizeVotes(List<Vote> votes) {
List<Vote> ret = new ArrayList<Vote>();
String userId = userDirectoryService.getCurrentUser().getId();
for (int i = 0; i < votes.size(); i++) {
Vote vote = (Vote)votes.get(i);
if (!userId.equals(vote.getUserId())) {
Vote newVote = new Vote();
newVote.setPollId(vote.getPollId());
newVote.setPollOption(vote.getPollOption());
newVote.setSubmissionId(vote.getSubmissionId());
ret.add(newVote);
} else {
ret.add(vote);
}
}
return ret;
}
/**
* Allows a user to create multiple Vote objects at once, taking one or more
* pollOption parameters.
*/
@Deprecated
@EntityCustomAction(action = "vote", viewKey = EntityView.VIEW_NEW)
public List<Vote> vote(EntityView view, EntityReference ref, String prefix, Search search, OutputStream out,
Map<String, Object> params) {
Long pollId = null;
try {
pollId = Long.valueOf((String) params.get("pollId"));
} catch (NumberFormatException nfe) {
throw new IllegalArgumentException("No pollId found.");
}
String userId = userDirectoryService.getCurrentUser().getId();
Poll poll = pollListManager.getPollById(pollId, false);
if (poll == null) {
throw new IllegalArgumentException("No poll found to update for the given reference: " + ref);
}
if (!pollVoteManager.isUserAllowedVote(userId, poll.getPollId(), false)) {
throw new SecurityException("User (" + userId + ") is not allowed to vote in this poll ("
+ poll.getPollId() + ")");
}
Set<String> optionIds = new HashSet<String>();
Object param = params.get("pollOption");
if (param == null) {
throw new IllegalArgumentException("At least one pollOption parameter must be provided to vote.");
} else if (param instanceof String) {
optionIds.add((String) param);
} else if (param instanceof Iterable<?>) {
for (Object o : (Iterable<?>) param)
if (o instanceof String)
optionIds.add((String) o);
else
throw new IllegalArgumentException("Each pollOption must be a String, not "
+ o.getClass().getName());
} else if (param instanceof Object[]) {
for (Object o : (Object[]) param)
if (o instanceof String)
optionIds.add((String) o);
else
throw new IllegalArgumentException("Each pollOption must be a String, not "
+ o.getClass().getName());
} else
throw new IllegalArgumentException("pollOption must be String, String[] or List<String>, not "
+ param.getClass().getName());
// Turn each option String into an Option, making sure that each is a
// valid choice for the poll. We use a Map to make sure one cannot vote
// more than once for any option by specifying it using equivalent
// representations
Map<Long, Option> options = new HashMap<Long, Option>();
for (String optionId : optionIds) {
try {
Option option = pollListManager.getOptionById(Long.valueOf(optionId));
if (!poll.getPollId().equals(option.getPollId()))
throw new Exception();
options.put(option.getOptionId(), option);
} catch (Exception e) {
throw new IllegalArgumentException("Invalid pollOption: " + optionId);
}
}
// Validate that the number of options voted for is within acceptable
// bounds.
if (options.size() < poll.getMinOptions())
throw new IllegalArgumentException("You must provide at least " + poll.getMinOptions() + " options, not "
+ options.size() + ".");
if (options.size() > poll.getMaxOptions())
throw new IllegalArgumentException("You may provide at most " + poll.getMaxOptions() + " options, not "
+ options.size() + ".");
// Create and save the Vote objects.
UsageSession usageSession = usageSessionService.getSession();
List<Vote> votes = new ArrayList<Vote>();
for (Option option : options.values()) {
Vote vote = new Vote();
vote.setVoteDate(new Date());
vote.setUserId(userId);
vote.setPollId(poll.getPollId());
vote.setPollOption(option.getOptionId());
if (vote.getSubmissionId() == null) {
String sid = userId + ":" + UUID.randomUUID();
vote.setSubmissionId(sid);
}
if (usageSession != null)
vote.setIp(usageSession.getIpAddress());
boolean saved = pollVoteManager.saveVote(vote);
if (!saved) {
throw new IllegalStateException("Unable to save vote (" + vote + ") for user (" + userId + "): " + ref);
}
votes.add(vote);
}
return votes;
}
public String[] getHandledOutputFormats() {
return new String[] {Formats.XML, Formats.JSON, Formats.FORM};
}
public String[] getHandledInputFormats() {
return new String[] {Formats.XML, Formats.JSON, Formats.HTML};
}
/**
* @param id
* @return
*/
@Deprecated
private Vote getVoteById(String id) {
Long voteId;
try {
voteId = Long.valueOf(id);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Cannot convert id ("+id+") to long: " + e.getMessage(), e);
}
Vote vote = pollVoteManager.getVoteById(voteId);
return vote;
}
@Deprecated
public void updateEntity(EntityReference ref, Object entity, Map<String, Object> params) {
throw new UnsupportedOperationException("Votes cannot currently be updated: " + ref);
}
}