/** * Copyright 2013 Apereo 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://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.gradebook.entity; import java.io.BufferedReader; import java.io.IOException; import java.util.Date; import java.util.List; import java.util.Map; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletResponse; import org.azeckoski.reflectutils.ConversionUtils; import org.azeckoski.reflectutils.transcoders.JSONTranscoder; import org.azeckoski.reflectutils.transcoders.XMLTranscoder; import org.sakaiproject.entitybroker.EntityReference; import org.sakaiproject.entitybroker.EntityView; import org.sakaiproject.entitybroker.EntityView.Method; import org.sakaiproject.entitybroker.entityprovider.EntityProvider; import org.sakaiproject.entitybroker.entityprovider.annotations.EntityCustomAction; import org.sakaiproject.entitybroker.entityprovider.capabilities.ActionsExecutable; 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.capabilities.RequestAware; import org.sakaiproject.entitybroker.entityprovider.capabilities.Resolvable; import org.sakaiproject.entitybroker.entityprovider.extension.Formats; import org.sakaiproject.entitybroker.entityprovider.extension.RequestGetter; import org.sakaiproject.entitybroker.exception.EntityException; import org.sakaiproject.entitybroker.util.AbstractEntityProvider; import org.sakaiproject.gradebook.logic.ExternalLogic; /** * Grades REST handler - processes and handles everything related to grades in Sakai * * @author Aaron Zeckoski (azeckoski @ gmail.com) (azeckoski @ vt.edu) (azeckoski @ unicon.net) */ public class GradesEntityProvider extends AbstractEntityProvider implements EntityProvider, Resolvable, Outputable, Inputable, Describeable, ActionsExecutable, Redirectable, RequestAware { public static String PREFIX = "grades"; private ExternalLogic externalLogic; public void setExternalLogic(ExternalLogic externalLogic) { this.externalLogic = externalLogic; } protected RequestGetter requestGetter; public void setRequestGetter(RequestGetter requestGetter) { this.requestGetter = requestGetter; } // custom actions @EntityCustomAction(action = "courses", viewKey = EntityView.VIEW_LIST) public Object getInstructorCourses(EntityView view) { String userId = externalLogic.getCurrentUserId(); if (userId == null) { throw new SecurityException( "Only logged in users can access instructor courses listings"); } String courseId = view.getPathSegment(2); List<Course> courses = externalLogic.getCoursesForInstructor(courseId); if (courses.isEmpty()) { throw new SecurityException( "Only instructors can access instructor courses listings"); } Object toEncode; if (courseId != null) { // get a single course Course c = courses.get(0); toEncode = c; } else { toEncode = courses; } return toEncode; } @EntityCustomAction(action = "students", viewKey = EntityView.VIEW_LIST) public List<Student> getCourseStudents(EntityView view) { String courseId = view.getPathSegment(2); if (courseId == null) { throw new IllegalArgumentException( "valid courseId must be included in the URL /grades/students/{courseId}"); } String userId = externalLogic.getCurrentUserId(); if (userId == null) { throw new SecurityException( "Only logged in users can access student enrollment listings"); } if (!externalLogic.isUserAdmin(userId) && !externalLogic.isUserInstructor(userId)) { throw new SecurityException("Only instructors can access course students listing"); } List<Student> students = externalLogic.getStudentsForCourse(courseId); return students; } @EntityCustomAction(action = "gradebook", viewKey = EntityView.VIEW_LIST) public Gradebook getCourseGradebook(EntityView view) { String courseId = view.getPathSegment(2); if (courseId == null) { throw new IllegalArgumentException( "valid courseId must be included in the URL /grades/gradebook/{courseId}"); } String userId = externalLogic.getCurrentUserId(); if (userId == null) { throw new SecurityException( "Only logged in users can access instructor courses listings"); } if (!externalLogic.isUserAdmin(userId) && !externalLogic.isUserInstructor(userId)) { throw new SecurityException("Only instructors can access course gradebook"); } Gradebook gradebook = externalLogic.getCourseGradebook(courseId, null); return gradebook; } @EntityCustomAction(action = "gradeitem", viewKey = "") public GradebookItem handleGradeItem(EntityView view) { String courseId = view.getPathSegment(2); if (courseId == null) { throw new IllegalArgumentException( "valid courseId must be included in the URL /grades/gradeitem/{courseId}"); } String userId = externalLogic.getCurrentUserId(); if (userId == null) { throw new SecurityException( "Only logged in users can access instructor courses listings"); } if (!externalLogic.isUserAdmin(userId) && !externalLogic.isUserInstructor(userId)) { throw new SecurityException("Only instructors can access course gradebook"); } GradebookItem gbItemOut; if (Method.GET.toString().equalsIgnoreCase(view.getMethod())) { String gradeItemName = view.getPathSegment(3); if (gradeItemName == null) { throw new IllegalArgumentException( "valid gbItemName must be included in the URL /grades/gradeitem/{courseId}/{gradeItemName}"); } Gradebook gb = externalLogic.getCourseGradebook(courseId, gradeItemName); gbItemOut = gb.items.get(0); } else if (Method.POST.toString().equalsIgnoreCase(view.getMethod()) || Method.PUT.toString().equalsIgnoreCase(view.getMethod())) { ServletRequest request = requestGetter.getRequest(); if (request == null) { throw new IllegalStateException("Cannot get request to read data from"); } String inputData; try { inputData = readerToString(request.getReader()); } catch (IOException e) { throw new RuntimeException("Failed to read the data from the request: " + e); } if (inputData == null || "".equals(inputData)) { throw new IllegalStateException("Must include the grade item and grades data for input (sent nothing)"); } Map<String, Object> input; if (Formats.JSON.equals(view.getFormat())) { input = new JSONTranscoder().decode(inputData); } else { input = new XMLTranscoder().decode(inputData); } // loop through and get the data out and put it into a gradeitem ConversionUtils cvu = ConversionUtils.getInstance(); String gbItemName = (String) input.get("name"); GradebookItem gbItemIn = new GradebookItem(courseId, gbItemName); gbItemIn.pointsPossible = cvu.convert(input.get("pointsPossible"), Double.class); gbItemIn.dueDate = cvu.convert(input.get("dueDate"), Date.class); @SuppressWarnings("unchecked") List<Object> scores = cvu.convert(input.get("scores"), List.class); if (scores != null) { for (Object o : scores) { GradebookItemScore score = cvu.convert(o, GradebookItemScore.class); gbItemIn.scores.add( score ); } } gbItemOut = externalLogic.saveGradebookItem(gbItemIn); } else { throw new EntityException("Method ("+view.getMethod()+") not supported", "grades/gradeitem", HttpServletResponse.SC_METHOD_NOT_ALLOWED); } return gbItemOut; } public static String readerToString(BufferedReader br) { StringBuilder sb = new StringBuilder(); String line = null; try { while ((line = br.readLine()) != null) { sb.append(line); sb.append('\n'); } } catch (IOException e) { throw new RuntimeException("Failed to get data from stream: " + e.getMessage(), e); } finally { try { br.close(); } catch (IOException e) { // ignore } } return sb.toString(); } // standard methods // NOTE: this provider does not allow entity creation or deletion or list lookup because we are really dealing with gradebooks here /* (non-Javadoc) * @see org.sakaiproject.entitybroker.entityprovider.EntityProvider#getEntityPrefix() */ public String getEntityPrefix() { return PREFIX; } /* (non-Javadoc) * @see org.sakaiproject.entitybroker.entityprovider.capabilities.Resolvable#getEntity(org.sakaiproject.entitybroker.EntityReference) */ public Object getEntity(EntityReference ref) { if (ref.getId() == null) { return new Gradebook(null); } Gradebook entity = externalLogic.getCourseGradebook(ref.getId(), null); if (entity != null) { return entity; } throw new IllegalArgumentException("Invalid id:" + ref.getId()); } /* (non-Javadoc) * @see org.sakaiproject.entitybroker.entityprovider.capabilities.Sampleable#getSampleEntity() */ public Object getSampleEntity() { return new Gradebook(null); } /* (non-Javadoc) * @see org.sakaiproject.entitybroker.entityprovider.capabilities.Outputable#getHandledOutputFormats() */ public String[] getHandledOutputFormats() { return new String[] { Formats.XML, Formats.JSON }; } /* (non-Javadoc) * @see org.sakaiproject.entitybroker.entityprovider.capabilities.Inputable#getHandledInputFormats() */ public String[] getHandledInputFormats() { return new String[] { Formats.HTML, Formats.XML, Formats.JSON }; } }