/********************************************************************************** * * $Id: GradebookDependentBean.java 105079 2012-02-24 23:08:11Z ottenhoff@longsight.com $ * *********************************************************************************** * * Copyright (c) 2005, 2006, 2007, 2008 The Sakai Foundation, The MIT Corporation * * 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.gradebook.ui; import java.text.DateFormat; import java.text.DecimalFormatSymbols; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import javax.faces.context.FacesContext; import org.apache.commons.lang.StringUtils; import org.sakaiproject.jsf.util.LocaleUtil; import org.sakaiproject.section.api.SectionAwareness; import org.sakaiproject.section.api.coursemanagement.CourseSection; import org.sakaiproject.section.api.facade.Role; import org.sakaiproject.service.gradebook.shared.GradebookService; import org.sakaiproject.service.gradebook.shared.GradebookExternalAssessmentService; import org.sakaiproject.service.gradebook.shared.GradebookPermissionService; import org.sakaiproject.tool.api.ToolSession; import org.sakaiproject.tool.cover.SessionManager; import org.sakaiproject.tool.gradebook.Category; import org.sakaiproject.tool.gradebook.GradeMapping; import org.sakaiproject.tool.gradebook.Gradebook; import org.sakaiproject.tool.gradebook.business.GradebookManager; import org.sakaiproject.tool.gradebook.facades.Authn; import org.sakaiproject.tool.gradebook.facades.UserDirectoryService; import org.sakaiproject.tool.gradebook.jsf.FacesUtil; import org.sakaiproject.util.ResourceLoader; public abstract class GradebookDependentBean extends InitializableBean { private String pageName; /** Used by breadcrumb display */ private String breadcrumbPage; private Boolean editing; private Boolean adding; private Boolean middle; private boolean isExistingConflictScale = false; protected final String BREADCRUMBPAGE = "breadcrumbPage"; /** * Marked transient to allow serializable subclasses. */ private transient GradebookBean gradebookBean; private transient PreferencesBean preferencesBean; /** * Convenience method, for use in calling locally implemented services * that assume the gradebook ID is an integer. */ Long getGradebookId() { return getGradebookBean().getGradebookId(); } /** * Convenience method, for use in calling external facades * that assume the gradebook ID is an string. */ private transient String gradebookUid; String getGradebookUid() { if (gradebookUid == null) { gradebookUid = getGradebookManager().getGradebookUid(getGradebookId()); } return gradebookUid; } /** * Convenience method to hide the Authn context object. */ public String getUserUid() { return getAuthnService().getUserUid(); } /** * Convenience method to load the current gradebook object. */ private transient Gradebook gradebook; Gradebook getGradebook() { if (gradebook == null) { gradebook = getGradebookManager().getGradebook(getGradebookId()); } return gradebook; } /** * Gets a localized message string based on the locale determined by the * FacesContext. Useful for adding localized FacesMessages from a backing bean. * * TODO Replace with direct calls to FacesUtil. * * @param key The key to look up the localized string */ public String getLocalizedString(String key) { return FacesUtil.getLocalizedString(key); } /** * Gets a localized message string based on the locale determined by the * FacesContext. Useful for adding localized FacesMessages from a backing bean. * * TODO Replace with direct calls to FacesUtil. * * @param key The key to look up the localized string * @param params The array of strings to use in replacing the placeholders * in the localized string */ public String getLocalizedString(String key, String[] params) { return FacesUtil.getLocalizedString(key, params); } /** * Gets a localized percent input symbol based on the locale determined by FacesContext. */ public String getLocalizedPercentInput() { Locale locale = LocaleUtil.getLocale(FacesContext.getCurrentInstance()); DecimalFormatSymbols dfs = new DecimalFormatSymbols(locale); return String.valueOf(dfs.getPercent()); } // Still more convenience methods, hiding the bean configuration details. public GradebookManager getGradebookManager() { return getGradebookBean().getGradebookManager(); } public SectionAwareness getSectionAwareness() { return getGradebookBean().getSectionAwareness(); } public UserDirectoryService getUserDirectoryService() { return getGradebookBean().getUserDirectoryService(); } public Authn getAuthnService() { return getGradebookBean().getAuthnService(); } public GradebookPermissionService getGradebookPermissionService() { return getGradebookBean().getGradebookPermissionService(); } public GradebookExternalAssessmentService getGradebookExternalAssessmentService() { return getGradebookBean().getGradebookExternalAssessmentService(); } // Because these methods are referred to inside "rendered" tag attributes, // JSF will call them multiple times in every request. To cut back on // business logic traffic, cache them in request scope. They need to be // declared transient, however, so that they aren't copied between // requests (which would prevent changes in a user's authz status). private transient Boolean userAbleToEditAssessments; public boolean isUserAbleToEditAssessments() { if (userAbleToEditAssessments == null) { userAbleToEditAssessments = new Boolean(getGradebookBean().getAuthzService().isUserAbleToEditAssessments(getGradebookUid())); } return userAbleToEditAssessments.booleanValue(); } private transient Boolean userAbleToGradeAll; public boolean isUserAbleToGradeAll() { if (userAbleToGradeAll == null) { userAbleToGradeAll = new Boolean(getGradebookBean().getAuthzService().isUserAbleToGradeAll(getGradebookUid())); } return userAbleToGradeAll.booleanValue(); } private transient List viewableSections; public List getViewableSections() { if (viewableSections == null) { viewableSections = getGradebookBean().getAuthzService().getViewableSections(getGradebookUid()); } return viewableSections; } private transient List viewableSectionIds; public List getViewableSectionIds() { if (viewableSectionIds == null) { viewableSectionIds = new ArrayList(); List sectionList = getViewableSections(); if (sectionList == null || sectionList.isEmpty()) { return viewableCategoryIds; } if (!sectionList.isEmpty()) { for (Iterator sectionIter = sectionList.iterator(); sectionIter.hasNext();) { CourseSection section = (CourseSection) sectionIter.next(); if (section != null) { viewableSectionIds.add(section.getUuid()); } } } } return viewableSectionIds; } private transient Boolean userHasGraderPermissions; public boolean isUserHasGraderPermissions() { if (userHasGraderPermissions == null) { userHasGraderPermissions = new Boolean(getGradebookBean().getAuthzService().isUserHasGraderPermissions(getGradebookId(), getUserUid())); } return userHasGraderPermissions.booleanValue(); } private transient Boolean userWithTaFlagExistsInSite; public boolean isUserWithTaFlagExistsInSite() { if (userWithTaFlagExistsInSite == null) { List tas = getSectionAwareness().getSiteMembersInRole(getGradebookUid(), Role.TA); userWithTaFlagExistsInSite = new Boolean(tas != null && tas.size() > 0); } return userWithTaFlagExistsInSite.booleanValue(); } private transient Boolean userHasPermissionsForAllItems; public boolean isUserHasPermissionsForAllItems() { if (userHasPermissionsForAllItems == null) { userHasPermissionsForAllItems = new Boolean(getGradebookBean().getGradebookPermissionService().getPermissionForUserForAllAssignment(getGradebookId(), getUserUid())); } return userHasPermissionsForAllItems.booleanValue(); } private transient List viewableCategories; public List getViewableCategories() { if (viewableCategories == null) { viewableCategories = new ArrayList(); List categoryList = getGradebookManager().getCategories(getGradebookId()); if (categoryList == null || categoryList.isEmpty()) { return viewableCategories; } if (isUserAbleToGradeAll()) { viewableCategories = categoryList; } else { if (getGradebookBean().getAuthzService().isUserHasGraderPermissions(getGradebookId(), getUserUid())) { //SAK-19896, eduservice's can't share the same "Category" class, so just pass the ID's List<Long> catIds = new ArrayList<Long>(); for (Category category : (List<Category>) categoryList) { catIds.add(category.getId()); } List<Long> viewableCats = getGradebookPermissionService().getCategoriesForUser(getGradebookId(), getUserUid(), catIds, getGradebook().getCategory_type()); List<Category> viewableCategories = new ArrayList<Category>(); for (Category category : (List<Category>) categoryList) { if(viewableCats.contains(category.getId())){ viewableCategories.add(category); } } } else { viewableCategories = categoryList; } } } return viewableCategories; } private transient List viewableCategoryIds; public List getViewableCategoryIds() { if (viewableCategoryIds == null) { viewableCategoryIds = new ArrayList(); List categoryList = getViewableCategories(); if (categoryList == null || categoryList.isEmpty()) { return viewableCategoryIds; } if (!categoryList.isEmpty()) { for (Iterator catIter = categoryList.iterator(); catIter.hasNext();) { Category category = (Category) catIter.next(); if (category != null) { viewableCategoryIds.add(category.getId()); } } } } return viewableCategories; } public Map findMatchingEnrollmentsForItem(Long categoryId, String optionalSearchString, String optionalSectionUid) { return getGradebookBean().getAuthzService().findMatchingEnrollmentsForItem(getGradebookUid(), categoryId, getGradebook().getCategory_type(), optionalSearchString, optionalSectionUid); } public Map findMatchingEnrollmentsForAllItems(String optionalSearchString, String optionalSectionUid) { return getGradebookBean().getAuthzService().findMatchingEnrollmentsForViewableItems(getGradebookUid(), getGradebookManager().getAssignments(getGradebookId()), optionalSearchString, optionalSectionUid); } public Map findMatchingEnrollmentsForViewableCourseGrade(String optionalSearchString, String optionalSectionUid) { return getGradebookBean().getAuthzService().findMatchingEnrollmentsForViewableCourseGrade(getGradebookUid(), getGradebook().getCategory_type(), optionalSearchString, optionalSectionUid); } public List getAllSections() { return getGradebookBean().getAuthzService().getAllSections(getGradebookUid()); } /** * Get the gradebook context. */ public GradebookBean getGradebookBean() { if (gradebookBean == null) { // This probably happened because gradebookBean is transient. // Just restore it from the session context. setGradebookBean((GradebookBean)FacesUtil.resolveVariable("gradebookBean")); } return gradebookBean; } /** * Set the gradebook context. */ public void setGradebookBean(GradebookBean gradebookBean) { this.gradebookBean = gradebookBean; } /** * @return Returns the preferencesBean. */ public PreferencesBean getPreferencesBean() { if (preferencesBean == null) { setPreferencesBean((PreferencesBean)FacesUtil.resolveVariable("preferencesBean")); } return preferencesBean; } /** * @param preferencesBean The preferencesBean to set. */ public void setPreferencesBean(PreferencesBean preferencesBean) { this.preferencesBean = preferencesBean; } /** * Set up close relations with page and action names for easier control * of menus. */ public String getPageName() { return pageName; } public void setPageName(String pageName) { this.pageName = pageName; } /** * Saves state for menu and breadcrumb trail. Pass in NULL to keep * current value. * * @param breadcrumbPage * Top level page to return to * @param editing * If navigating TO edit page. * @param adding * If navigating TO add page. * @param middle * If navigating TO 2nd level down. * @param fromPage * currently, when navigating from details page down. */ public void setNav(String breadcrumbPage, String editing, String adding, String middle, String fromPage) { final ToolSession session = SessionManager.getCurrentToolSession(); if (breadcrumbPage != null) session.setAttribute(BREADCRUMBPAGE, breadcrumbPage); if (editing != null) session.setAttribute("editing", editing); if (adding != null) session.setAttribute("adding", adding); if (middle != null) session.setAttribute("middle", middle); if (fromPage != null) session.setAttribute("fromPage", fromPage); } /** * Used to determine where details page called from */ public String getBreadcrumbPage() { if (breadcrumbPage == null) { breadcrumbPage = (String) SessionManager.getCurrentToolSession().getAttribute(BREADCRUMBPAGE); return breadcrumbPage; } else { return breadcrumbPage; } } /** * Used to set where details page called from */ public void setBreadcrumbPage(String breadcrumbPage) { this.breadcrumbPage = breadcrumbPage; } /** * In IE (but not Mozilla/Firefox) empty request parameters may be returned * to JSF as the string "null". JSF always "restores" some idea of the * last view, even if that idea is always going to be null because a redirect * has occurred. Put these two things together, and you end up with * a class cast exception when redirecting from this request-scoped * bean to a static page. */ public void setBreadcrumbPageParam(String breadcrumbPageParam) { if (SessionManager.getCurrentToolSession().getAttribute(BREADCRUMBPAGE) != null) { if ((breadcrumbPageParam != null) && !breadcrumbPageParam.equals("null")) { setBreadcrumbPage(breadcrumbPageParam); if (!"".equals(breadcrumbPageParam)) SessionManager.getCurrentToolSession().setAttribute(BREADCRUMBPAGE, breadcrumbPageParam); } else { ToolSession session = SessionManager.getCurrentToolSession(); final String fromPage = (String) session.getAttribute(BREADCRUMBPAGE); if (fromPage != null) { setBreadcrumbPage(fromPage); } } } } /** * Return if breadcrumb will display 'Edit' piece */ public Boolean getEditing() { return new Boolean((String) SessionManager.getCurrentToolSession().getAttribute("editing")); } /** * Return if breadcrumb will display 'Add' piece */ public Boolean getAdding() { if (adding == null) { final ToolSession session = SessionManager.getCurrentToolSession(); adding = new Boolean((String) session.getAttribute("adding")); } return adding; } /** * Return if breadcrumb trail needs to display the middle section */ public Boolean getMiddle() { if (middle == null) { final ToolSession session = SessionManager.getCurrentToolSession(); middle = new Boolean((String) session.getAttribute("middle")); } return middle; } /** * Generates a default filename (minus the extension) for a download from this Gradebook. * * @param prefix for filename * @return The appropriate filename for the export */ public String getDownloadFileName(String prefix) { Date now = new Date(); DateFormat df = DateFormat.getDateInstance( DateFormat.SHORT, (new ResourceLoader()).getLocale() ); StringBuilder fileName = new StringBuilder(prefix); String gbName = getGradebook().getName(); if(StringUtils.trimToNull(gbName) != null) { gbName = gbName.replaceAll("\\s", "_"); // replace whitespace with '_' fileName.append("-"); fileName.append(gbName); } fileName.append("-"); fileName.append(df.format(now)); return fileName.toString(); } /** * * @return */ public String getAuthzLevel(){ return (getGradebookBean().getAuthzService().isUserAbleToGradeAll(getGradebookUid())) ?"instructor" : "TA"; } /** * Returns whether the gb has enabled categories (with or without weighting) */ private transient Boolean categoriesEnabled; public boolean getCategoriesEnabled() { if (categoriesEnabled == null) categoriesEnabled = new Boolean(getGradebook().getCategory_type() != GradebookService.CATEGORY_TYPE_NO_CATEGORY); return categoriesEnabled.booleanValue(); } /** * Returns whether the gb has enabled weighting */ private transient Boolean weightingEnabled; public boolean getWeightingEnabled() { if (weightingEnabled == null) weightingEnabled = new Boolean(getGradebook().getCategory_type() == GradebookService.CATEGORY_TYPE_WEIGHTED_CATEGORY); return weightingEnabled.booleanValue(); } /** * Returns whether the gb grade entry is by points */ private transient Boolean gradeEntryByPoints; public boolean getGradeEntryByPoints() { if (gradeEntryByPoints == null) gradeEntryByPoints = new Boolean(getGradebook().getGrade_type() == GradebookService.GRADE_TYPE_POINTS); return gradeEntryByPoints.booleanValue(); } /** * Returns whether the gb grade entry is by percentage */ private transient Boolean gradeEntryByPercent; public boolean getGradeEntryByPercent() { if (gradeEntryByPercent == null) gradeEntryByPercent = new Boolean(getGradebook().getGrade_type() == GradebookService.GRADE_TYPE_PERCENTAGE); return gradeEntryByPercent.booleanValue(); } /** * Returns whether the gb grade entry is by letter */ private transient Boolean gradeEntryByLetter; public boolean getGradeEntryByLetter() { if (gradeEntryByLetter == null) gradeEntryByLetter = new Boolean(getGradebook().getGrade_type() == GradebookService.GRADE_TYPE_LETTER); return gradeEntryByLetter.booleanValue(); } /** * Set proper text for navigation button on assignment detials and * instructor view pages. */ public String getReturnString() { final String breadcrumbPage = getBreadcrumbPage(); if (breadcrumbPage != null && !"".equals(breadcrumbPage)) { return ("overview".equals(breadcrumbPage)) ? getLocalizedString("assignment_details_return_to_overview") : getLocalizedString("assignment_details_return_to_roster"); } else { final String where = (String) SessionManager.getCurrentToolSession().getAttribute("fromPage"); return ("overview".equals(where)) ? getLocalizedString("assignment_details_return_to_overview") : getLocalizedString("assignment_details_return_to_roster"); } } /** * Return fromPage property within tool session - used for breadcrumb trail. */ public String getFromPage() { final String fp = (String) SessionManager.getCurrentToolSession().getAttribute("fromPage"); return fp; } /** * Return back to overview page. State is kept in * tool session, hence attribute setting. */ public String navigateToOverview() { setNav("overview", "false", "false", "false", ""); return "overview"; } /** * Go to roster page. State is kept in * tool session, hence attribute setting. */ public String navigateToRoster() { setNav("roster", "false", "false", "false", ""); return "roster"; } /** * Go to edit assignment page. State is kept in * tool session, hence attribute setting. */ public String navigateToEdit() { setNav(null, "true", "false", "true", null); return "editAssignment"; } /** * Go to gradebook setup. State is kept in * tool session, hence attribute setting. */ public String navigateToGradebookSetup() { setNav("other","false","false","false",""); return "gradebookSetup"; } /** * Go to permissions page. State is kept in * tool session, hence attribute setting. */ public String navigateToPermissionSettings() { setNav("other","false","false","false",""); return "graderRules"; } /** * Go to gradebook course grade setup. State is kept in * tool session, hence attribute setting. */ public String navigateToFeedbackOptions() { setNav("other","false","false","false",""); return "feedbackOptions"; } /** * Go to spreadsheet (csv) export/bulk import. State is kept in * tool session, hence attribute setting. */ public String navigateToImportGrades() { setNav("other","false","false","false",""); return "spreadsheetAll"; } /** * Go to course grade details pg. State is kept in * tool session, hence attribute setting. */ public String navigateToCourseGrades() { setNav("other","false","false","false",""); return "courseGradeDetails"; } /** * Determine where to return to. Used by both Assignmenet Details and * Instructor View pages, so put here in super class. */ public String processCancel() { final String breadcrumbPage = getBreadcrumbPage(); if (breadcrumbPage != null && !"".equals(breadcrumbPage)) { return breadcrumbPage; } else { String where = (String) SessionManager.getCurrentToolSession().getAttribute(BREADCRUMBPAGE); if ("assignmentDetails".equals(where)) { where = (String) SessionManager.getCurrentToolSession().getAttribute("fromPage"); SessionManager.getCurrentToolSession().removeAttribute("fromPage"); } return where; } } /** * We can't rely on the converters to properly display 2 decimals for us, * b/c setMaxFractionDigits rounds * @param score * @return */ public Double truncateScore(Double score) { if (score == null) return null; return new Double(FacesUtil.getRoundDown(score.doubleValue(), 2)); } public boolean getIsExistingConflictScale() { isExistingConflictScale = true; Gradebook gb = getGradebookManager().getGradebookWithGradeMappings(getGradebookId()); if(gb != null && gb.getGrade_type() == GradebookService.GRADE_TYPE_LETTER) { if((gb.getSelectedGradeMapping().getGradingScale() != null && gb.getSelectedGradeMapping().getGradingScale().getUid().equals("LetterGradeMapping")) || (gb.getSelectedGradeMapping().getGradingScale() == null && gb.getSelectedGradeMapping().getName().equals("Letter Grades"))) { isExistingConflictScale = false; return isExistingConflictScale; } Set mappings = gb.getGradeMappings(); for(Iterator iter = mappings.iterator(); iter.hasNext();) { GradeMapping gm = (GradeMapping) iter.next(); if(gm != null) { if((gm.getGradingScale() != null && gm.getGradingScale().getUid().equals("LetterGradePlusMinusMapping")) || (gm.getGradingScale() == null && gm.getName().equals("Letter Grades with +/-"))) { Map defaultMapping = gm.getDefaultBottomPercents(); for (Iterator gradeIter = gm.getGrades().iterator(); gradeIter.hasNext(); ) { String grade = (String)gradeIter.next(); Double percentage = (Double)gm.getValue(grade); Double defautPercentage = (Double)defaultMapping.get(grade); if (percentage != null && !percentage.equals(defautPercentage)) { isExistingConflictScale = false; break; } } } } } } return isExistingConflictScale; } public void setIsExistingConflictScale(boolean isExistingConflictScale) { this.isExistingConflictScale = isExistingConflictScale; } }