001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/sections/tags/sakai_2-4-1/sections-app/src/java/org/sakaiproject/tool/section/jsf/backingbean/EditSectionBean.java $
003: * $Id: EditSectionBean.java 20230 2007-01-10 01:13:02Z jholtzman@berkeley.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2005, 2006 The Regents of the University of California and The Regents of the University of Michigan
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.tool.section.jsf.backingbean;
021:
022: import java.io.Serializable;
023: import java.util.ArrayList;
024: import java.util.Iterator;
025: import java.util.List;
026:
027: import javax.faces.context.FacesContext;
028: import javax.faces.event.ActionEvent;
029:
030: import org.apache.commons.lang.StringUtils;
031: import org.apache.commons.logging.Log;
032: import org.apache.commons.logging.LogFactory;
033: import org.sakaiproject.section.api.coursemanagement.CourseSection;
034: import org.sakaiproject.tool.section.jsf.JsfUtil;
035:
036: /**
037: * Controls the edit and delete sections pages.
038: *
039: * @author <a href="mailto:jholtzman@berkeley.edu">Josh Holtzman</a>
040: *
041: */
042: public class EditSectionBean extends AddSectionsBean implements
043: SectionEditor, Serializable {
044:
045: private static final long serialVersionUID = 1L;
046: private static final Log log = LogFactory
047: .getLog(EditSectionBean.class);
048:
049: private String sectionUuid;
050: private LocalSectionModel section;
051: private transient String elementToFocus;
052:
053: /** A list composed of a single section. This is used so we can share UI code with AddSections */
054: private List<CourseSection> sections;
055:
056: /**
057: * @inheritDoc
058: */
059: public void init() {
060: // TODO Remove this code. Replace it with a request param in faces-config
061: if (sectionUuid == null || isNotValidated()) {
062: String sectionUuidFromParam = JsfUtil
063: .getStringFromParam("sectionUuid");
064: if (sectionUuidFromParam != null) {
065: sectionUuid = sectionUuidFromParam;
066: }
067: CourseSection sectionFromService = getSectionManager()
068: .getSection(sectionUuid);
069: section = new LocalSectionModel(sectionFromService);
070: sections = new ArrayList<CourseSection>();
071: sections.add(section);
072: }
073: }
074:
075: public void processAddMeeting(ActionEvent action) {
076: if (log.isDebugEnabled())
077: log.debug("processing an 'add meeting' action from "
078: + this .getClass().getName());
079: section.getMeetings().add(new LocalMeetingModel());
080: elementToFocus = action.getComponent().getClientId(
081: FacesContext.getCurrentInstance());
082: }
083:
084: /**
085: * Since the validation and conversion rules rely on the *relative*
086: * values of one component to another, we can't use JSF validators and
087: * converters. So we check everything here.
088: *
089: * @return
090: */
091: protected boolean validationFails() {
092:
093: boolean validationFailure = false;
094:
095: // We also need to keep track of whether an invalid time was entered,
096: // so we can skip the time comparisons
097: boolean invalidTimeEntered = false;
098:
099: // Ensure that this title isn't being used by another section
100: if (isDuplicateSectionTitle()) {
101: if (log.isDebugEnabled())
102: log
103: .debug("Failed to update section... duplicate title: "
104: + section.getTitle());
105: JsfUtil.addErrorMessage(JsfUtil.getLocalizedMessage(
106: "section_update_failure_duplicate_title",
107: new String[] { section.getTitle() }),
108: "editSectionForm:titleInput");
109: validationFailure = true;
110: }
111:
112: // Ensure that the max enrollments is a number >= 0.
113: if (isInvalidMaxEnrollments()) {
114: if (log.isDebugEnabled())
115: log
116: .debug("Failed to update section... max enrollments is not valid");
117: JsfUtil.addErrorMessage(JsfUtil.getLocalizedMessage(
118: "javax.faces.validator.LongRangeValidator.MINIMUM",
119: new String[] { "0" }),
120: "editSectionForm:maxEnrollmentInput");
121: validationFailure = true;
122: }
123:
124: // Ensure that the user didn't choose to limit the size of the section without specifying a max size
125: if (Boolean.TRUE.toString().equals(section.getLimitSize())
126: && section.getMaxEnrollments() == null) {
127: String componentId = "editSectionForm:maxEnrollmentInput";
128: JsfUtil.addErrorMessage(JsfUtil
129: .getLocalizedMessage("sections_specify_limit"),
130: componentId);
131: validationFailure = true;
132: }
133:
134: // Ensure that the times entered in the meetings are valid, and that they end after they start
135: int meetingIndex = 0;
136: for (Iterator iter = section.getMeetings().iterator(); iter
137: .hasNext(); meetingIndex++) {
138: LocalMeetingModel meeting = (LocalMeetingModel) iter.next();
139: if (!meeting.isStartTimeDefault()
140: && super
141: .isInvalidTime(meeting.getStartTimeString())) {
142: if (log.isDebugEnabled())
143: log
144: .debug("Failed to update section... start time is invalid");
145: JsfUtil
146: .addErrorMessage(
147: JsfUtil
148: .getLocalizedMessage("javax.faces.convert.DateTimeConverter.CONVERSION"),
149: "editSectionForm:sectionTable_0:meetingsTable_"
150: + meetingIndex + ":startTime");
151: validationFailure = true;
152: invalidTimeEntered = true;
153: }
154:
155: if (!meeting.isEndTimeDefault()
156: && super .isInvalidTime(meeting.getEndTimeString())) {
157: if (log.isDebugEnabled())
158: log
159: .debug("Failed to update section... end time is invalid");
160: JsfUtil
161: .addErrorMessage(
162: JsfUtil
163: .getLocalizedMessage("javax.faces.convert.DateTimeConverter.CONVERSION"),
164: "editSectionForm:sectionTable_0:meetingsTable_"
165: + meetingIndex + ":endTime");
166: validationFailure = true;
167: invalidTimeEntered = true;
168: }
169:
170: if (!invalidTimeEntered
171: && super .isEndTimeWithoutStartTime(meeting)) {
172: if (log.isDebugEnabled())
173: log
174: .debug("Failed to update section... start time without end time");
175: JsfUtil
176: .addErrorMessage(
177: JsfUtil
178: .getLocalizedMessage("section_update_failure_end_without_start"),
179: "editSectionForm:sectionTable_0:meetingsTable_"
180: + meetingIndex + ":startTime");
181: validationFailure = true;
182: }
183:
184: if (!invalidTimeEntered
185: && super .isEndTimeBeforeStartTime(meeting)) {
186: if (log.isDebugEnabled())
187: log
188: .debug("Failed to update section... end time is before start time");
189: JsfUtil
190: .addErrorMessage(
191: JsfUtil
192: .getLocalizedMessage("section_update_failure_end_before_start"),
193: "editSectionForm:sectionTable_0:meetingsTable_"
194: + meetingIndex + ":endTime");
195: validationFailure = true;
196: }
197: }
198:
199: return validationFailure;
200: }
201:
202: /**
203: * Updates the section in persistence.
204: *
205: * @return
206: */
207: public String update() {
208: if (log.isInfoEnabled())
209: log.info("Updating section " + sectionUuid);
210:
211: if (validationFails()) {
212: return null;
213: }
214:
215: // Perform the update
216: getSectionManager().updateSection(sectionUuid,
217: section.getTitle(), section.getMaxEnrollments(),
218: section.getMeetings());
219:
220: // Add a success message
221: JsfUtil.addRedirectSafeInfoMessage(JsfUtil.getLocalizedMessage(
222: "section_update_successful", new String[] { section
223: .getTitle() }));
224:
225: // Add a warning if max enrollments has been exceeded
226: CourseSection section = getSectionManager().getSection(
227: sectionUuid);
228: Integer maxEnrollments = section.getMaxEnrollments();
229: int totalEnrollments = getSectionManager().getTotalEnrollments(
230: section.getUuid());
231: if (maxEnrollments != null
232: && totalEnrollments > maxEnrollments.intValue()) {
233: JsfUtil
234: .addRedirectSafeWarnMessage(JsfUtil
235: .getLocalizedMessage(
236: "edit_student_over_max_warning",
237: new String[] {
238: section.getTitle(),
239: Integer
240: .toString(totalEnrollments),
241: Integer
242: .toString(totalEnrollments
243: - maxEnrollments
244: .intValue()) }));
245: }
246: return "overview";
247: }
248:
249: /**
250: * Returns true if the title is a duplicate of another section.
251: *
252: * @return
253: */
254: private boolean isDuplicateSectionTitle() {
255: for (Iterator iter = getAllSiteSections().iterator(); iter
256: .hasNext();) {
257: CourseSection sec = (CourseSection) iter.next();
258: // Skip this section, since it is OK for it to keep the same title
259: if (sec.getUuid().equals(sectionUuid)) {
260: continue;
261: }
262: if (sec.getTitle().equals(section.getTitle())) {
263: if (log.isDebugEnabled())
264: log.debug("Conflicting section name found.");
265: return true;
266: }
267: }
268: return false;
269: }
270:
271: private boolean isInvalidMaxEnrollments() {
272: return section.getMaxEnrollments() != null
273: && section.getMaxEnrollments().intValue() < 0;
274: }
275:
276: // TODO What was this method used for?
277:
278: // public String getDays() {
279: // CourseSection section = getSectionManager().getSection(sectionUuid);
280: // CourseSectionDecorator decorator = new CourseSectionDecorator(section, null);
281: // return decorator.getMeetingDays();
282: // }
283:
284: public String getSectionUuid() {
285: return sectionUuid;
286: }
287:
288: public void setSectionUuid(String sectionUuid) {
289: this .sectionUuid = sectionUuid;
290: }
291:
292: public LocalSectionModel getSection() {
293: return section;
294: }
295:
296: public void setSection(LocalSectionModel section) {
297: this .section = section;
298: }
299:
300: public List<CourseSection> getSections() {
301: return sections;
302: }
303:
304: /**
305: * Gets the css to use in the table generated for display. Needed for sharing IU
306: * code with AddSections.
307: *
308: * @return
309: */
310: public String getRowStyleClasses() {
311: return "sectionPadRow";
312: }
313:
314: /**
315: * This method is needed so we can share UI code with AddSections.
316: * @return
317: */
318: public List getMeetings() {
319: return section.getMeetings();
320: }
321:
322: public String getElementToFocus() {
323: return elementToFocus;
324: }
325:
326: public void setElementToFocus(String scrollDepth) {
327: this.elementToFocus = scrollDepth;
328: }
329:
330: }
|