/**********************************************************************************
* $URL: https://source.sakaiproject.org/svn/sam/trunk/samigo-app/src/java/org/sakaiproject/tool/assessment/ui/listener/author/PublishAssessmentListener.java $
* $Id: PublishAssessmentListener.java 132501 2013-12-11 21:18:24Z holladay@longsight.com $
***********************************************************************************
*
* Copyright (c) 2004, 2005, 2006, 2007, 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.tool.assessment.ui.listener.author;
import java.io.UnsupportedEncodingException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.tool.assessment.integration.helper.ifc.CalendarServiceHelper;
import org.sakaiproject.component.cover.ServerConfigurationService;
import org.sakaiproject.event.cover.EventTrackingService;
import org.sakaiproject.service.gradebook.shared.AssignmentHasIllegalPointsException;
import org.sakaiproject.service.gradebook.shared.GradebookExternalAssessmentService;
import org.sakaiproject.service.gradebook.shared.GradebookService;
import org.sakaiproject.spring.SpringBeanLocator;
import org.sakaiproject.tool.assessment.data.dao.assessment.PublishedMetaData;
import org.sakaiproject.tool.assessment.data.ifc.assessment.AssessmentAccessControlIfc;
import org.sakaiproject.tool.assessment.data.ifc.assessment.AssessmentMetaDataIfc;
import org.sakaiproject.tool.assessment.data.ifc.assessment.EvaluationModelIfc;
import org.sakaiproject.tool.assessment.facade.AgentFacade;
import org.sakaiproject.tool.assessment.facade.AssessmentFacade;
import org.sakaiproject.tool.assessment.facade.PublishedAssessmentFacade;
import org.sakaiproject.tool.assessment.integration.context.IntegrationContextFactory;
import org.sakaiproject.tool.assessment.integration.helper.ifc.GradebookServiceHelper;
import org.sakaiproject.tool.assessment.services.GradingService;
import org.sakaiproject.tool.assessment.services.assessment.AssessmentService;
import org.sakaiproject.tool.assessment.services.assessment.PublishedAssessmentService;
import org.sakaiproject.tool.assessment.ui.bean.author.AssessmentSettingsBean;
import org.sakaiproject.tool.assessment.ui.bean.author.AuthorBean;
import org.sakaiproject.tool.assessment.ui.bean.author.PublishRepublishNotificationBean;
import org.sakaiproject.tool.assessment.ui.bean.evaluation.TotalScoresBean;
import org.sakaiproject.tool.assessment.ui.listener.util.ContextUtil;
import org.sakaiproject.tool.assessment.util.TextFormat;
import org.sakaiproject.util.ResourceLoader;
import org.sakaiproject.email.cover.EmailService;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.tool.cover.ToolManager;
import org.sakaiproject.site.cover.SiteService;
import org.sakaiproject.exception.IdUnusedException;
/**
* <p>Title: Samigo</p>2
* <p>Description: Sakai Assessment Manager</p>
* @author Ed Smiley
* @version $Id: PublishAssessmentListener.java 132501 2013-12-11 21:18:24Z holladay@longsight.com $
*/
public class PublishAssessmentListener
implements ActionListener {
private static Log log = LogFactory.getLog(PublishAssessmentListener.class);
private static final GradebookServiceHelper gbsHelper =
IntegrationContextFactory.getInstance().getGradebookServiceHelper();
private static final boolean integrated =
IntegrationContextFactory.getInstance().isIntegrated();
private static final Lock repeatedPublishLock = new ReentrantLock();
private static boolean repeatedPublish = false;
private CalendarServiceHelper calendarService = IntegrationContextFactory.getInstance().getCalendarServiceHelper();
private ResourceLoader rl= new ResourceLoader("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages");
public PublishAssessmentListener() {
}
public void processAction(ActionEvent ae) throws AbortProcessingException {
repeatedPublishLock.lock();
try {
//FacesContext context = FacesContext.getCurrentInstance();
if (ae == null) {
repeatedPublish = false;
return;
}
else {
UIComponent eventSource = (UIComponent) ae.getSource();
ValueBinding vb = eventSource.getValueBinding("value");
if (vb == null) {
repeatedPublish = false;
return;
}
else {
String buttonValue = (String) vb.getExpressionString();
if(buttonValue.endsWith(".button_unique_save_and_publish}"))
{
repeatedPublish = false;
return;
}
}
}
if(!repeatedPublish)
{
//Map reqMap = context.getExternalContext().getRequestMap();
//Map requestParams = context.getExternalContext().getRequestParameterMap();
AuthorBean author = (AuthorBean) ContextUtil.lookupBean(
"author");
AssessmentSettingsBean assessmentSettings = (AssessmentSettingsBean) ContextUtil.lookupBean("assessmentSettings");
AssessmentService assessmentService = new AssessmentService();
AssessmentFacade assessment = assessmentService.getAssessment(
assessmentSettings.getAssessmentId().toString());
// 0. sorry need double checking assesmentTitle and everything
boolean error = checkTitle(assessment);
if (error){
return;
}
// Tell AuthorBean that we just published an assessment
// This will allow us to jump directly to published assessments tab
author.setJustPublishedAnAssessment(true);
//update any random draw questions from pool since they could have changed
int success = assessmentService.updateAllRandomPoolQuestions(assessment, true);
if(success == assessmentService.UPDATE_SUCCESS){
//grab new updated assessment
assessment = assessmentService.getAssessment(assessment.getAssessmentId().toString());
publish(assessment, assessmentSettings);
GradingService gradingService = new GradingService();
PublishedAssessmentService publishedAssessmentService = new PublishedAssessmentService();
AuthorActionListener authorActionListener = new AuthorActionListener();
authorActionListener.prepareAssessmentsList(author, assessmentService, gradingService, publishedAssessmentService);
repeatedPublish = true;
}else{
repeatedPublish = false;
FacesContext context = FacesContext.getCurrentInstance();
if(success == AssessmentService.UPDATE_ERROR_DRAW_SIZE_TOO_LARGE){
String err=ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AuthorMessages","update_pool_error_size_too_large");
context.addMessage(null,new FacesMessage(err));
}else{
String err=ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AuthorMessages","update_pool_error_unknown");
context.addMessage(null,new FacesMessage(err));
}
return;
}
}
} finally{
repeatedPublishLock.unlock();
}
}
private void publish(AssessmentFacade assessment,
AssessmentSettingsBean assessmentSettings) {
PublishedAssessmentService publishedAssessmentService = new PublishedAssessmentService();
PublishedAssessmentFacade pub = null;
try {
assessment.addAssessmentMetaData("ALIAS", assessmentSettings.getAlias());
pub = publishedAssessmentService.publishAssessment(assessment);
PublishRepublishNotificationBean publishRepublishNotification = (PublishRepublishNotificationBean) ContextUtil.lookupBean("publishRepublishNotification");
boolean sendNotification = publishRepublishNotification.getSendNotification();
String subject = publishRepublishNotification.getNotificationSubject();
String notificationMessage = getNotificationMessage(publishRepublishNotification, assessmentSettings.getTitle(), assessmentSettings.getReleaseTo(), assessmentSettings.getStartDateString(), assessmentSettings.getPublishedUrl(),
assessmentSettings.getReleaseToGroupsAsString(), assessmentSettings.getDueDateString(), assessmentSettings.getTimedHours(), assessmentSettings.getTimedMinutes(),
assessmentSettings.getUnlimitedSubmissions(), assessmentSettings.getSubmissionsAllowed(), assessmentSettings.getScoringType(), assessmentSettings.getFeedbackDelivery(), assessmentSettings.getFeedbackDateString());
if (sendNotification) {
sendNotification(pub, publishedAssessmentService, subject, notificationMessage,
assessmentSettings.getReleaseTo());
}
EventTrackingService.post(EventTrackingService.newEvent("sam.assessment.publish", "siteId=" + AgentFacade.getCurrentSiteId() + ", assessmentId=" + assessment.getAssessmentId() + ", publishedAssessmentId=" + pub.getPublishedAssessmentId(), true));
//update Calendar Events
boolean addDueDateToCalendar = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("publishAssessmentForm:calendarDueDate") != null;
calendarService.updateAllCalendarEvents(pub, assessmentSettings.getReleaseTo(), assessmentSettings.getGroupsAuthorized(), rl.getString("calendarDueDatePrefix") + " ", addDueDateToCalendar, notificationMessage);
} catch (AssignmentHasIllegalPointsException gbe) {
// Right now gradebook can only accept assessements with totalPoints > 0
// this might change later
log.warn(gbe);
gbe.printStackTrace();
// Add a global message (not bound to any component) to the faces context indicating the failure
String err=(String)ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AuthorMessages",
"gradebook_exception_min_points");
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(err));
throw new AbortProcessingException(gbe);
} catch (Exception e) {
log.warn(e);
e.printStackTrace();
// Add a global message (not bound to any component) to the faces context indicating the failure
String err=(String)ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AuthorMessages",
"gradebook_exception_error");
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(err));
throw new AbortProcessingException(e);
}
// Add ALIAS if it doesn't exist
if ("".equals(assessment.getAssessmentMetaDataByLabel("ALIAS"))) {
// generate an alias to the pub assessment
String alias = assessmentSettings.getAlias();
PublishedMetaData meta = new PublishedMetaData(pub.getData(),
AssessmentMetaDataIfc.ALIAS, alias);
publishedAssessmentService.saveOrUpdateMetaData(meta);
}
}
private boolean checkTitle(AssessmentFacade assessment){
boolean error=false;
String assessmentName = assessment.getTitle();
AssessmentService assessmentService = new AssessmentService();
String assessmentId = assessment.getAssessmentBaseId().toString();
//#a - look for error: check if core assessment title is unique
if (assessmentName!=null &&(assessmentName.trim()).equals("")){
String publish_error=ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages","publish_error_message");
FacesContext.getCurrentInstance().addMessage(null,new FacesMessage(publish_error));
error=true;
}
if (!assessmentService.assessmentTitleIsUnique(assessmentId, TextFormat.convertPlaintextToFormattedTextNoHighUnicode(log, assessmentName), false)){
error=true;
String nameUnique_err=ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages","assessmentName_error");
FacesContext.getCurrentInstance().addMessage(null,new FacesMessage(nameUnique_err));
}
//#b - check if gradebook exist, if so, if assessment title already exists in GB
GradebookExternalAssessmentService g = null;
if (integrated){
g = (GradebookExternalAssessmentService) SpringBeanLocator.getInstance().
getBean("org.sakaiproject.service.gradebook.GradebookExternalAssessmentService");
}
String toGradebook = assessment.getEvaluationModel().getToGradeBook();
try{
if (toGradebook!=null && toGradebook.equals(EvaluationModelIfc.TO_DEFAULT_GRADEBOOK.toString()) &&
gbsHelper.isAssignmentDefined(assessmentName, g)){
error=true;
String gbConflict_error=ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages","gbConflict_error");
FacesContext.getCurrentInstance().addMessage(null,new FacesMessage(gbConflict_error));
}
}
catch(Exception e){
log.warn("external assessment in GB has the same title:"+e.getMessage());
}
return error;
}
public void sendNotification(PublishedAssessmentFacade pub, PublishedAssessmentService service, String subject, String message,
String releaseTo) {
TotalScoresBean totalScoresBean = (TotalScoresBean) ContextUtil.lookupBean("totalScores");
AgentFacade instructor = new AgentFacade();
InternetAddress fromIA = null;
try {
fromIA = new InternetAddress(instructor.getEmail(), instructor.getDisplayName());
} catch (UnsupportedEncodingException e) {
log.warn("UnsupportedEncodingException encountered when constructing instructor's email.");
}
boolean groupRelease = AssessmentAccessControlIfc.RELEASE_TO_SELECTED_GROUPS.equals(releaseTo);
if (groupRelease) {
totalScoresBean.setSelectedSectionFilterValue(TotalScoresBean.RELEASED_SECTIONS_GROUPS_SELECT_VALUE);
}
else {
totalScoresBean.setSelectedSectionFilterValue(TotalScoresBean.ALL_SECTIONS_SELECT_VALUE);
}
totalScoresBean.setPublishedId(pub.getPublishedAssessmentId().toString());
Map useridMap= totalScoresBean.getUserIdMap(TotalScoresBean.CALLED_FROM_NOTIFICATION_LISTENER);
AgentFacade agent = null;
int size = useridMap.size() + 1;
ArrayList<InternetAddress> toIAList = new ArrayList();
try {
toIAList.add(new InternetAddress(instructor.getEmail())); // send one copy to instructor
} catch (AddressException e) {
log.warn("AddressException encountered when constructing instructor's email.");
}
Iterator iter = useridMap.keySet().iterator();
int i = 1;
while (iter.hasNext()) {
String userUid = (String) iter.next();
agent = new AgentFacade(userUid);
InternetAddress ia = null;
try {
ia = new InternetAddress(agent.getEmail());
} catch (AddressException e) {
log.warn("AddressException encountered when constructing toIAList email. userUid = " + userUid);
}
if (ia != null) {
toIAList.add(ia);
}
}
InternetAddress[] toIA = new InternetAddress[toIAList.size()];
int count = 0;
Iterator iter2 = toIAList.iterator();
while (iter2.hasNext()) {
toIA[count++] = (InternetAddress) iter2.next();
}
String noReplyEmaillAddress = "no-reply@" + ServerConfigurationService.getServerName();
InternetAddress[] noReply = new InternetAddress[1];
try {
noReply[0] = new InternetAddress(noReplyEmaillAddress);
} catch (AddressException e) {
log.warn("AddressException encountered when constructing no_reply@serverName email.");
}
List<String> headers = new ArrayList<String>();
headers.add("Content-Type: text/html");
EmailService.sendMail(fromIA, toIA, subject.toString(), message, noReply, noReply, headers);
}
public String getNotificationMessage(PublishRepublishNotificationBean publishRepublishNotification, String title, String releaseTo, String startDateString, String publishedURL, String releaseToGroupsAsString, String dueDateString, Integer timedHours, Integer timedMinutes, String unlimitedSubmissions, String submissionsAllowed, String scoringType, String feedbackDelivery, String feedbackDateString){
String siteTitle = publishRepublishNotification.getSiteTitle();
if(siteTitle == null || "".equals(siteTitle)){
try {
Site site = SiteService.getSite(ToolManager.getCurrentPlacement().getContext());
siteTitle = site.getTitle();
publishRepublishNotification.setSiteTitle(siteTitle);
} catch (IdUnusedException iue) {
log.warn(iue);
}
}
String newline = "<br />\n";
String bold_open = "<b>";
String bold_close = "</b>";
StringBuilder message = new StringBuilder();
String prePopulateText = publishRepublishNotification.getPrePopulateText();
if (prePopulateText != null && !prePopulateText.trim().equals("") &&
(!prePopulateText.trim().equals(rl.getString("pre_populate_text_publish")) &&
!prePopulateText.trim().equals(rl.getString("pre_populate_text_republish")) &&
!prePopulateText.trim().equals(rl.getString("pre_populate_text_regrade_republish")))) {
message.append(TextFormat.convertPlaintextToFormattedTextNoHighUnicode(log, prePopulateText));
message.append(newline);
message.append(newline);
}
message.append("\"");
message.append(bold_open);
message.append(TextFormat.convertPlaintextToFormattedTextNoHighUnicode(log, title));
message.append(bold_close);
message.append("\"");
message.append(" ");
publishedURL = "<a target=\"_blank\" href=\"" + publishedURL + "\">" + publishedURL + "</a>";
if ("Anonymous Users".equals(releaseTo)) {
message.append(MessageFormat.format(rl.getString("available_anonymously_at"), startDateString, publishedURL));
}
else if (AssessmentAccessControlIfc.RELEASE_TO_SELECTED_GROUPS.equals(releaseTo)) {
message.append(MessageFormat.format(rl.getString("available_group_at"), startDateString, releaseToGroupsAsString, publishedURL));
}
else {
message.append(MessageFormat.format(rl.getString("available_class_at"), startDateString, publishedURL));
}
if (dueDateString != null && !dueDateString.trim().equals("")) {
message.append(newline);
message.append(newline);
message.append(MessageFormat.format(rl.getString("it_is_due"), dueDateString));
}
message.append(newline);
message.append(newline);
// Time limited
if (timedHours > 0 || timedMinutes > 0) {
message.append(rl.getString("the_time_limit_is"));
message.append(" ");
message.append(timedHours);
message.append(" ");
message.append(rl.getString("hours"));
if (timedMinutes > 0) {
message.append(", ");
message.append(timedMinutes);
message.append(" ");
message.append(rl.getString("minutes"));
}
message.append(". ");
message.append(rl.getString("submit_when_time_is_up"));
}
else {
message.append(rl.getString("there_is_no_time_limit"));
}
message.append(" ");
// Number of submissions
if ("1".equals(unlimitedSubmissions)) {
message.append(rl.getString("student_submit_unlimited_times"));
}
else {
message.append(MessageFormat.format(rl.getString("student_submit_certain_time"), submissionsAllowed));
}
// Scoring type
message.append(" ");
if ("1".equals(scoringType)) {
message.append(rl.getString("record_highest"));
}
else {
message.append(rl.getString("record_last"));
}
message.append(newline);
message.append(newline);
// Feedback
if ("1".equals(feedbackDelivery)) {
message.append(rl.getString("receive_immediate"));
}
else if ("4".equals(feedbackDelivery)) {
message.append(rl.getString("receive_feedback_on_submission"));
}
else if ("3".equals(feedbackDelivery)) {
message.append(rl.getString("receive_no_feedback"));
}
else {
message.append(MessageFormat.format(rl.getString("feedback_available_on"), feedbackDateString));
}
message.append(newline);
message.append(newline);
StringBuffer siteTitleSb = new StringBuffer();
siteTitleSb.append(" \"");
siteTitleSb.append(siteTitle);
siteTitleSb.append("\" ");
StringBuffer portalUrlSb = new StringBuffer();
portalUrlSb.append(" <a href=\"");
portalUrlSb.append(ServerConfigurationService.getPortalUrl());
portalUrlSb.append("\" target=\"_blank\">");
portalUrlSb.append(ServerConfigurationService.getPortalUrl());
portalUrlSb.append("</a>");
message.append(MessageFormat.format(rl.getString("notification_content"), siteTitleSb.toString(), portalUrlSb.toString()));
message.append(newline);
message.append(newline);
return message.toString();
}
}