/**********************************************************************************
* $URL: https://source.sakaiproject.org/svn/sections/trunk/sections-app/src/java/org/sakaiproject/tool/section/jsf/backingbean/EditSectionBean.java $
* $Id: EditSectionBean.java 105080 2012-02-24 23:10:31Z ottenhoff@longsight.com $
***********************************************************************************
*
* Copyright (c) 2005, 2006, 2007, 2008 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.section.jsf.backingbean;
import java.io.Serializable;
import java.text.DateFormatSymbols;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.section.api.coursemanagement.CourseSection;
import org.sakaiproject.tool.section.jsf.JsfUtil;
/**
* Controls the edit and delete sections pages.
*
* @author <a href="mailto:jholtzman@berkeley.edu">Josh Holtzman</a>
*
*/
public class EditSectionBean extends AddSectionsBean implements SectionEditor, Serializable {
private static final long serialVersionUID = 1L;
private static final Log log = LogFactory.getLog(EditSectionBean.class);
private String sectionUuid;
private LocalSectionModel section;
private transient String elementToFocus;
/** A list composed of a single section. This is used so we can share UI code with AddSections */
private List<CourseSection> sections;
/**
* @inheritDoc
*/
public void init() {
// TODO Remove this code. Replace it with a request param in faces-config
if(sectionUuid == null || isNotValidated()) {
String sectionUuidFromParam = JsfUtil.getStringFromParam("sectionUuid");
if(sectionUuidFromParam != null) {
sectionUuid = sectionUuidFromParam;
}
CourseSection sectionFromService = getSectionManager().getSection(sectionUuid);
section = new LocalSectionModel(sectionFromService);
sections = new ArrayList<CourseSection>();
sections.add(section);
}
initDaysOfWeek();
}
public void processAddMeeting(ActionEvent action) {
if(log.isDebugEnabled()) log.debug("processing an 'add meeting' action from " + this.getClass().getName());
section.getMeetings().add(new LocalMeetingModel());
elementToFocus = action.getComponent().getClientId(FacesContext.getCurrentInstance());
}
/**
* Since the validation and conversion rules rely on the *relative*
* values of one component to another, we can't use JSF validators and
* converters. So we check everything here.
*
* @return
*/
protected boolean validationFails() {
boolean validationFailure = false;
// We also need to keep track of whether an invalid time was entered,
// so we can skip the time comparisons
boolean invalidTimeEntered = false;
// Ensure that this title isn't being used by another section
if(isDuplicateSectionTitle()) {
if(log.isDebugEnabled()) log.debug("Failed to update section... duplicate title: " + section.getTitle());
JsfUtil.addErrorMessage(JsfUtil.getLocalizedMessage(
"section_update_failure_duplicate_title", new String[] {section.getTitle()}), "editSectionForm:titleInput");
validationFailure = true;
}
// Ensure that the max enrollments is a number >= 0.
if(isInvalidMaxEnrollments()) {
if(log.isDebugEnabled()) log.debug("Failed to update section... max enrollments is not valid");
JsfUtil.addErrorMessage(JsfUtil.getLocalizedMessage(
"javax.faces.validator.LongRangeValidator.MINIMUM", new String[] {"0"}),
"editSectionForm:maxEnrollmentInput");
validationFailure = true;
}
// Ensure that the user didn't choose to limit the size of the section without specifying a max size
if(Boolean.TRUE.toString().equals(section.getLimitSize()) && section.getMaxEnrollments() == null) {
String componentId = "editSectionForm:sectionTable:" + 0 + ":maxEnrollmentInput";
JsfUtil.addErrorMessage(JsfUtil.getLocalizedMessage(
"sections_specify_limit"), componentId);
validationFailure = true;
}
// Ensure that the times entered in the meetings are valid, and that they end after they start
int meetingIndex = 0;
for(Iterator iter = section.getMeetings().iterator(); iter.hasNext(); meetingIndex++) {
LocalMeetingModel meeting = (LocalMeetingModel)iter.next();
if( ! meeting.isStartTimeDefault() && super.isInvalidTime(meeting.getStartTimeString())) {
if(log.isDebugEnabled()) log.debug("Failed to update section... start time is invalid");
JsfUtil.addErrorMessage(JsfUtil.getLocalizedMessage(
"javax.faces.convert.DateTimeConverter.CONVERSION"), "editSectionForm:sectionTable:0:meetingsTable:" + 0 + ":startTime");
validationFailure = true;
invalidTimeEntered = true;
}
if( ! meeting.isEndTimeDefault() && super.isInvalidTime(meeting.getEndTimeString())) {
if(log.isDebugEnabled()) log.debug("Failed to update section... end time is invalid");
JsfUtil.addErrorMessage(JsfUtil.getLocalizedMessage(
"javax.faces.convert.DateTimeConverter.CONVERSION"), "editSectionForm:sectionTable:0:meetingsTable:" + 0 + ":endTime");
validationFailure = true;
invalidTimeEntered = true;
}
if(!invalidTimeEntered && super.isEndTimeWithoutStartTime(meeting)) {
if(log.isDebugEnabled()) log.debug("Failed to update section... start time without end time");
JsfUtil.addErrorMessage(JsfUtil.getLocalizedMessage(
"section_update_failure_end_without_start"), "editSectionForm:sectionTable:0:meetingsTable:" + 0 + ":startTime");
validationFailure = true;
}
if(!invalidTimeEntered && super.isEndTimeBeforeStartTime(meeting)) {
if(log.isDebugEnabled()) log.debug("Failed to update section... end time is before start time");
JsfUtil.addErrorMessage(JsfUtil.getLocalizedMessage(
"section_update_failure_end_before_start"), "editSectionForm:sectionTable:0:meetingsTable:" + 0 + ":endTime");
validationFailure = true;
}
}
return validationFailure;
}
/**
* Updates the section in persistence.
*
* @return
*/
public String update() {
if(log.isInfoEnabled()) log.info("Updating section " + sectionUuid);
if(validationFails()) {
return null;
}
// Perform the update
getSectionManager().updateSection(sectionUuid, section.getTitle(),
section.getMaxEnrollments(), section.getMeetings());
// Add a success message
JsfUtil.addRedirectSafeInfoMessage(JsfUtil.getLocalizedMessage(
"section_update_successful", new String[] {section.getTitle()}));
// Add a warning if max enrollments has been exceeded
CourseSection section = getSectionManager().getSection(sectionUuid);
Integer maxEnrollments = section.getMaxEnrollments();
int totalEnrollments = getSectionManager().getTotalEnrollments(section.getUuid());
if(maxEnrollments != null && totalEnrollments > maxEnrollments.intValue()) {
JsfUtil.addRedirectSafeWarnMessage(JsfUtil.getLocalizedMessage(
"edit_student_over_max_warning", new String[] {
section.getTitle(),
Integer.toString(totalEnrollments),
Integer.toString(totalEnrollments - maxEnrollments.intValue()) }));
}
return "overview";
}
/**
* Returns true if the title is a duplicate of another section.
*
* @return
*/
private boolean isDuplicateSectionTitle() {
for(Iterator iter = getAllSiteSections().iterator(); iter.hasNext();) {
CourseSection sec = (CourseSection)iter.next();
// Skip this section, since it is OK for it to keep the same title
if(sec.getUuid().equals(sectionUuid)) {
continue;
}
if(sec.getTitle().equals(section.getTitle())) {
if(log.isDebugEnabled()) log.debug("Conflicting section name found.");
return true;
}
}
return false;
}
private boolean isInvalidMaxEnrollments() {
return section.getMaxEnrollments() != null && section.getMaxEnrollments().intValue() < 0;
}
// TODO What was this method used for?
// public String getDays() {
// CourseSection section = getSectionManager().getSection(sectionUuid);
// CourseSectionDecorator decorator = new CourseSectionDecorator(section, null);
// return decorator.getMeetingDays();
// }
public String getSectionUuid() {
return sectionUuid;
}
public void setSectionUuid(String sectionUuid) {
this.sectionUuid = sectionUuid;
}
public LocalSectionModel getSection() {
return section;
}
public void setSection(LocalSectionModel section) {
this.section = section;
}
public List<CourseSection> getSections() {
return sections;
}
/**
* Gets the css to use in the table generated for display. Needed for sharing IU
* code with AddSections.
*
* @return
*/
public String getRowStyleClasses() {
return "sectionPadRow";
}
/**
* This method is needed so we can share UI code with AddSections.
* @return
*/
public List getMeetings() {
return section.getMeetings();
}
public String getElementToFocus() {
return elementToFocus;
}
public void setElementToFocus(String scrollDepth) {
this.elementToFocus = scrollDepth;
}
}