org.sakaiproject.iclicker.tool.ToolController.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.iclicker.tool.ToolController.java

Source

/**
 * Copyright (c) 2009 i>clicker (R) <http://www.iclicker.com/dnn/>
 *
 * This file is part of i>clicker Sakai integrate.
 *
 * i>clicker Sakai integrate is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * i>clicker Sakai integrate is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with i>clicker Sakai integrate.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.sakaiproject.iclicker.tool;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.azeckoski.reflectutils.ArrayUtils;
import org.sakaiproject.iclicker.logic.*;
import org.sakaiproject.iclicker.logic.ClickerIdInvalidException.Failure;
import org.sakaiproject.iclicker.model.ClickerRegistration;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.PageContext;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Vector;

/**
 * This is the singleton controller for the application,
 * this handles all the display related logic and processing and communicates
 * with the services for the view layer
 * 
 * @author Aaron Zeckoski (azeckoski @ gmail.com)
 */
public class ToolController {

    private static final Log log = LogFactory.getLog(ToolController.class);

    private IClickerLogic logic;
    private ExternalLogic externalLogic;
    private ReloadableResourceBundleMessageSource messageSource;

    public void init() {
        log.info("INIT");
    }

    public ToolController() {
    }

    /**************************************************************************
     * VIEW handling
     **************************************************************************
     */

    public static final String VIEW_REGISTRATION = "registration";
    public static final String VIEW_INSTRUCTOR = "instructor";
    public static final String VIEW_INSTRUCTOR_SSO = "instructor_sso";
    public static final String VIEW_ADMIN = "admin";

    public static final String[] VIEWS = { VIEW_REGISTRATION, VIEW_INSTRUCTOR, VIEW_INSTRUCTOR_SSO, VIEW_ADMIN };

    /**************************************************************************
     * View handling methods
     **************************************************************************
     */

    public void processRegistration(PageContext pageContext, HttpServletRequest request) {
        // Handle the POST if there is one
        pageContext.setAttribute("newRegistration", false);
        pageContext.setAttribute("clickerIdText", "");
        if ("POST".equalsIgnoreCase(request.getMethod())) {
            if ((request.getParameter("register") != null)) {
                // we are registering a clicker
                if ((request.getParameter("clickerId") == null)) {
                    ToolController.addMessage(pageContext, ToolController.KEY_ERROR, "reg.activate.clickerId.empty",
                            (Object[]) null);
                } else {
                    String clickerId = request.getParameter("clickerId");
                    pageContext.setAttribute("clickerIdText", clickerId);
                    // save a new clicker registration
                    try {
                        this.getLogic().createRegistration(clickerId);
                        ToolController.addMessage(pageContext, ToolController.KEY_INFO, "reg.registered.success",
                                clickerId);
                        ToolController.addMessage(pageContext, ToolController.KEY_BELOW,
                                "reg.registered.below.success", (Object[]) null);
                        pageContext.setAttribute("newRegistration", true);
                    } catch (ClickerRegisteredException e) {
                        ToolController.addMessage(pageContext, ToolController.KEY_ERROR,
                                "reg.registered.clickerId.duplicate", clickerId);
                        ToolController.addMessage(pageContext, ToolController.KEY_BELOW,
                                "reg.registered.below.duplicate", clickerId);
                    } catch (ClickerIdInvalidException e) {
                        if (Failure.EMPTY.equals(e.failure)) {
                            ToolController.addMessage(pageContext, ToolController.KEY_ERROR,
                                    "reg.registered.clickerId.empty", (Object[]) null);
                        } else if (Failure.LENGTH.equals(e.failure)) {
                            ToolController.addMessage(pageContext, ToolController.KEY_ERROR,
                                    "reg.registered.clickerId.wrong.length", (Object[]) null);
                        } else if (Failure.GO_NO_USER.equals(e.failure)) {
                            ToolController.addMessage(pageContext, ToolController.KEY_ERROR,
                                    "reg.registered.clickerId.failure", clickerId);
                        } else if (Failure.GO_LASTNAME.equals(e.failure)) {
                            ToolController.addMessage(pageContext, ToolController.KEY_ERROR,
                                    "reg.registered.clickerId.go.wrong.lastname", (Object[]) null);
                        } else if (Failure.GO_NO_MATCH.equals(e.failure)) {
                            ToolController.addMessage(pageContext, ToolController.KEY_ERROR,
                                    "reg.registered.clickerId.go.invalid", clickerId);
                        } else {
                            ToolController.addMessage(pageContext, ToolController.KEY_ERROR,
                                    "reg.registered.clickerId.invalid", clickerId);
                        }
                    }
                }

            } else if ((request.getParameter("activate") != null)) {
                // First arrived at this page
                boolean activate = Boolean.parseBoolean(request.getParameter("activate"));
                if ((request.getParameter("registrationId") == null)) {
                    ToolController.addMessage(pageContext, ToolController.KEY_ERROR,
                            "reg.activate.registrationId.empty", (Object[]) null);
                } else {
                    try {
                        Long registrationId = Long.parseLong(request.getParameter("registrationId"));
                        // save a new clicker registration
                        ClickerRegistration cr = this.getLogic().setRegistrationActive(registrationId, activate);
                        if (cr != null) {
                            ToolController.addMessage(pageContext, ToolController.KEY_INFO,
                                    "reg.activate.success." + cr.isActivated(), cr.getClickerId());
                        }
                    } catch (NumberFormatException e) {
                        ToolController.addMessage(pageContext, ToolController.KEY_ERROR,
                                "reg.activate.registrationId.nonnumeric", request.getParameter("registrationId"));
                    }
                }

            } else if ((request.getParameter("remove") != null)) {
                if ((request.getParameter("registrationId") == null)) {
                    ToolController.addMessage(pageContext, ToolController.KEY_ERROR,
                            "reg.activate.registrationId.empty", (Object[]) null);
                } else {
                    try {
                        Long registrationId = Long.parseLong(request.getParameter("registrationId"));
                        // remove a new clicker registration by deactivating it
                        ClickerRegistration cr = this.getLogic().setRegistrationActive(registrationId, false);
                        if (cr != null) {
                            ToolController.addMessage(pageContext, ToolController.KEY_INFO, "reg.remove.success",
                                    cr.getClickerId());
                        }
                    } catch (NumberFormatException e) {
                        ToolController.addMessage(pageContext, ToolController.KEY_ERROR,
                                "reg.activate.registrationId.nonnumeric", request.getParameter("registrationId"));
                    }
                }

            } else {
                // invalid POST
                System.err.println("WARN: Invalid POST: does not contain register or activate, nothing to do");
            }
        }

        pageContext.setAttribute("regs", this.getAllVisibleItems(null));
        pageContext.setAttribute("isInstructor", this.isInstructor(), PageContext.REQUEST_SCOPE);
        // SSO handling
        pageContext.setAttribute("ssoEnabled", logic.isSingleSignOnEnabled());
        // added to allow special messages below the forms
        pageContext.setAttribute("belowMessages", ToolController.getMessages(pageContext, ToolController.KEY_BELOW),
                PageContext.REQUEST_SCOPE);
    }

    public void processInstructor(PageContext pageContext, HttpServletRequest request) {
        // admin/instructor check
        if (!this.isAdmin() && !this.isInstructor()) {
            throw new SecurityException("Current user is not an instructor and cannot access the instructor view");
        }
        String courseId = request.getParameter("courseId");
        pageContext.setAttribute("courseId", courseId);
        if (courseId != null) {
            pageContext.setAttribute("courseTitle", this.getLogic().getCourseTitle(courseId));
        }
        List<Course> courses = logic.getCoursesForInstructorWithStudents(courseId);
        pageContext.setAttribute("courses", courses);
        pageContext.setAttribute("coursesCount", courses.size());
        pageContext.setAttribute("showStudents", false);
        if (courseId != null && courses.size() == 1) {
            Course course = courses.get(0);
            pageContext.setAttribute("showStudents", true);
            pageContext.setAttribute("course", course);
            pageContext.setAttribute("students", course.students);
            pageContext.setAttribute("studentsCount", course.students.size());
        }
        // SSO handling
        pageContext.setAttribute("ssoEnabled", logic.isSingleSignOnEnabled());
    }

    public void processInstructorSSO(PageContext pageContext, HttpServletRequest request) {
        // admin/instructor check
        if (!this.isAdmin() && !this.isInstructor()) {
            throw new SecurityException("Current user is not an instructor and cannot access the instructor view");
        }
        // SSO handling
        boolean ssoEnabled = logic.isSingleSignOnEnabled();
        pageContext.setAttribute("ssoEnabled", ssoEnabled);
        if (ssoEnabled) {
            String userKey = null;
            if ("POST".equalsIgnoreCase(request.getMethod())) {
                if ((request.getParameter("generateKey") != null)) {
                    userKey = logic.makeUserKey(null, true);
                    ToolController.addMessage(pageContext, ToolController.KEY_INFO, "inst.sso.generated.new.key",
                            (Object[]) null);
                }
            }
            if (userKey == null) {
                userKey = logic.makeUserKey(null, false);
            }
            pageContext.setAttribute("ssoUserKey", userKey);
        }
    }

    public void processAdmin(PageContext pageContext, HttpServletRequest request) {
        // admin check
        if (!this.isAdmin()) {
            throw new SecurityException("Current user is not an admin and cannot access the admin view");
        }

        int pageNum = 1;
        int perPageNum = 20; // does not change
        if ((request.getParameter("page") != null)) {
            try {
                pageNum = Integer.parseInt(request.getParameter("page"));
                if (pageNum < 1) {
                    pageNum = 1;
                }
            } catch (NumberFormatException e) {
                // nothing to do
                System.err.println("WARN: invalid page number: " + request.getParameter("page") + ":" + e);
            }
        }
        pageContext.setAttribute("page", pageNum);
        pageContext.setAttribute("perPage", perPageNum);
        String sort = "clickerId";
        if ((request.getParameter("sort") != null)) {
            sort = request.getParameter("sort");
        }
        pageContext.setAttribute("sort", sort);

        if ("POST".equalsIgnoreCase(request.getMethod())) {
            if ((request.getParameter("activate") != null)) {
                // First arrived at this page
                boolean activate = Boolean.parseBoolean(request.getParameter("activate"));
                if ((request.getParameter("registrationId") == null)) {
                    ToolController.addMessage(pageContext, ToolController.KEY_ERROR,
                            "reg.activate.registrationId.empty", (Object[]) null);
                } else {
                    try {
                        Long registrationId = Long.parseLong(request.getParameter("registrationId"));
                        // save a new clicker registration
                        ClickerRegistration cr = this.getLogic().setRegistrationActive(registrationId, activate);
                        if (cr != null) {
                            ToolController.addMessage(pageContext, ToolController.KEY_INFO,
                                    "admin.activate.success." + cr.isActivated(), cr.getClickerId(),
                                    this.getLogic().getUserDisplayName(cr.getOwnerId()));
                        }
                    } catch (NumberFormatException e) {
                        ToolController.addMessage(pageContext, ToolController.KEY_ERROR,
                                "reg.activate.registrationId.nonnumeric", request.getParameter("registrationId"));
                    }
                }
            } else if ((request.getParameter("remove") != null)) {
                if ((request.getParameter("registrationId") == null)) {
                    ToolController.addMessage(pageContext, ToolController.KEY_ERROR,
                            "reg.activate.registrationId.empty", (Object[]) null);
                } else {
                    try {
                        Long registrationId = Long.parseLong(request.getParameter("registrationId"));
                        ClickerRegistration cr = this.getLogic().getItemById(registrationId);
                        if (cr != null) {
                            this.getLogic().removeItem(cr);
                            ToolController.addMessage(pageContext, ToolController.KEY_INFO, "admin.delete.success",
                                    cr.getClickerId(), registrationId,
                                    this.getLogic().getUserDisplayName(cr.getOwnerId()));
                        }
                    } catch (NumberFormatException e) {
                        ToolController.addMessage(pageContext, ToolController.KEY_ERROR,
                                "reg.activate.registrationId.nonnumeric", request.getParameter("registrationId"));
                    }
                }
            } else if ((request.getParameter("runner") != null)) {
                // initiate the runner process
                String runnerType;
                if ((request.getParameter("addAll") != null)) {
                    runnerType = BigRunner.RUNNER_TYPE_ADD;
                } else if ((request.getParameter("removeAll") != null)) {
                    runnerType = BigRunner.RUNNER_TYPE_REMOVE;
                } else if ((request.getParameter("syncAll") != null)) {
                    runnerType = BigRunner.RUNNER_TYPE_SYNC;
                } else {
                    throw new IllegalArgumentException("Invalid request type: missing valid parameter");
                }
                try {
                    logic.startRunnerOperation(runnerType);
                    String msgKey = "admin.process.message." + runnerType;
                    ToolController.addMessage(pageContext, ToolController.KEY_INFO, msgKey, (Object[]) null);
                } catch (ClickerLockException e) {
                    ToolController.addMessage(pageContext, ToolController.KEY_ERROR, "admin.process.message.locked",
                            runnerType);
                } catch (IllegalStateException e) {
                    ToolController.addMessage(pageContext, ToolController.KEY_ERROR, "admin.process.message.locked",
                            runnerType);
                }
            } else {
                // invalid POST
                System.err
                        .println("WARN: Invalid POST: does not contain runner, remove, or activate, nothing to do");
            }
        }

        // put config data into page
        pageContext.setAttribute("ssoEnabled", logic.isSingleSignOnEnabled());
        if (logic.isSingleSignOnEnabled()) {
            pageContext.setAttribute("ssoSharedKey", logic.getSharedKey());
        }
        pageContext.setAttribute("domainURL", logic.domainURL);
        pageContext.setAttribute("workspacePageTitle", logic.workspacePageTitle);

        // put error data into page
        pageContext.setAttribute("recentFailures", logic.getFailures());

        // put runner status in page
        makeRunnerStatus(pageContext, true);

        // handling the calcs for paging
        int first = (pageNum - 1) * perPageNum;
        int totalCount = this.getLogic().countAllItems();
        int pageCount = (totalCount + perPageNum - 1) / perPageNum;
        pageContext.setAttribute("totalCount", totalCount);
        pageContext.setAttribute("pageCount", pageCount);
        pageContext.setAttribute("registrations", this.getLogic().getAllItems(first, perPageNum, sort, null, true));

        if (totalCount > 0) {
            StringBuilder sb = new StringBuilder();
            Date d = new Date();
            for (int i = 0; i < pageCount; i++) {
                int currentPage = i + 1;
                int currentStart = (i * perPageNum) + 1;
                int currentEnd = currentStart + perPageNum - 1;
                if (currentEnd > totalCount) {
                    currentEnd = totalCount;
                }
                String marker = "[" + currentStart + ".." + currentEnd + "]";
                if (currentPage == pageNum) {
                    // make it bold and not a link
                    sb.append("<span class=\"paging_current paging_item\">").append(marker).append("</span>\n");
                } else {
                    // make it a link
                    sb.append("<a class=\"paging_link paging_item\" href=\"")
                            .append(pageContext.findAttribute("adminPath")).append("&page=").append(currentPage)
                            .append("&sort=").append(sort).append("&nc=").append(d.getTime() + currentPage)
                            .append("\">").append(marker).append("</a>\n");
                }
            }
            pageContext.setAttribute("pagerHTML", sb.toString());
        }
    }

    public void makeRunnerStatus(PageContext pageContext, boolean clearOnComplete) {
        // check for running process and include the info in the page
        BigRunner runner = logic.getRunnerStatus();
        pageContext.setAttribute("runnerExists", runner != null);
        if (runner != null) {
            pageContext.setAttribute("runnerType", runner.getType());
            pageContext.setAttribute("runnerPercent", runner.getPercentCompleted());
            pageContext.setAttribute("runnerComplete", runner.isComplete());
            pageContext.setAttribute("runnerError", runner.isError());
            if (runner.isComplete() && clearOnComplete) {
                // clear the runner since it is completed
                logic.clearRunner();
            }
        } else {
            pageContext.setAttribute("runnerType", "none");
            pageContext.setAttribute("runnerPercent", 100);
            pageContext.setAttribute("runnerComplete", true);
            pageContext.setAttribute("runnerError", false);
        }
    }

    /**
     * Used to ensure that the view being displayed is valid 
     * and allowed for the current user
     * @param viewParam
     *            the value of the parameter "view"
     * @return the valid and allowed view for the given user
     */
    public String getValidView(String viewParam) {
        String view = VIEW_REGISTRATION;
        if (viewParam != null && !"".equals(viewParam)) {
            if (ArrayUtils.contains(VIEWS, viewParam)) {
                if (userAllowedForView(viewParam)) {
                    view = viewParam;
                }
            }
        }
        return view;
    }

    /**
     * Check if the user is allowed in a view
     * @param view the view constant from VIEWS
     * @return true if allowed, false if not
     */
    protected boolean userAllowedForView(String view) {
        boolean allowed = false;
        if (view != null && !"".equals(view)) {
            if (ArrayUtils.contains(VIEWS, view)) {
                String userId = externalLogic.getCurrentUserId();
                if (VIEW_ADMIN.equals(view)) {
                    if (externalLogic.isUserAdmin(userId)) {
                        allowed = true;
                    }
                } else if (VIEW_INSTRUCTOR.equals(view)) {
                    if (externalLogic.isUserAdmin(userId) || externalLogic.isUserInstructor(userId)) {
                        allowed = true;
                    }
                } else {
                    // everyone allowed on registration view
                    allowed = true;
                }
            }
        }
        return allowed;
    }

    /**************************************************************************
     * Services pass-throughs
     **************************************************************************
     */

    /**
     * This gets the list of clicker registrations for a current user (or visible to them)
     * 
     * @param locationId
     *            [OPTIONAL] a unique id which represents the current location of the user (entity
     *            reference)
     * @return a List of ClickerRegistration objects visible to the current user
     */
    public List<ClickerRegistration> getAllVisibleItems(String locationId) {
        String userId = externalLogic.getCurrentUserId();
        return logic.getAllVisibleItems(userId, locationId);
    }

    /**
     * Use this for passthroughs which require no additional work
     * @return the logic service
     */
    public IClickerLogic getLogic() {
        return this.logic;
    }

    public boolean isAdmin() {
        String userId = externalLogic.getCurrentUserId();
        return externalLogic.isUserAdmin(userId);
    }

    public boolean isInstructor() {
        String userId = externalLogic.getCurrentUserId();
        return externalLogic.isUserInstructor(userId);
    }

    /**************************************************************************
     * STATICS and methods for message handling
     **************************************************************************
     */

    private static final String ICLICKER_MESSAGES = "iClickerMessages";

    public static final String MESSAGE_BUNDLE = "i18n.Messages";
    public static final String KEY_INFO = "INFO";
    public static final String KEY_ERROR = "ERROR";
    public static final String KEY_BELOW = "BELOW";

    public static String springMessageBundle() {
        return "classpath:" + MESSAGE_BUNDLE.replace('.', '/');
    }

    @SuppressWarnings("unchecked")
    public static void addMessage(PageContext context, String key, String message) {
        if (context == null || key == null) {
            throw new IllegalArgumentException(
                    "context (" + context + ") and key (" + key + ") must both not be null");
        }
        if (message != null && !"".equals(message)) {
            String keyVal = ICLICKER_MESSAGES + key;
            if (context.getAttribute(keyVal) == null) {
                context.setAttribute(keyVal, new Vector<String>(), PageContext.REQUEST_SCOPE);
            }
            List<String> l = (List<String>) context.getAttribute(keyVal, PageContext.REQUEST_SCOPE);
            l.add(message);
        }
    }

    public static void addMessage(PageContext context, String key, String messageKey, Object... args) {
        if (context == null || key == null) {
            throw new IllegalArgumentException(
                    "context (" + context + ") and key (" + key + ") must both not be null");
        }
        if (messageKey != null && !"".equals(messageKey)) {
            // get the message from messageSource if possible
            MessageSource ms = (MessageSource) context.findAttribute("messageSource");
            Locale locale = context.getRequest().getLocale();
            String message;
            try {
                message = ms.getMessage(messageKey, args, locale);
            } catch (NoSuchMessageException e) {
                message = "{{INVALID KEY:" + messageKey + "}}";
            }
            addMessage(context, key, message);
        }
    }

    @SuppressWarnings("unchecked")
    public static String[] getMessages(PageContext context, String key) {
        if (context == null || key == null) {
            throw new IllegalArgumentException(
                    "context (" + context + ") and key (" + key + ") must both not be null");
        }
        String[] messages;
        String keyVal = ICLICKER_MESSAGES + key;
        if (context.getAttribute(keyVal, PageContext.REQUEST_SCOPE) == null) {
            messages = new String[] {};
        } else {
            List<String> l = (List<String>) context.getAttribute(keyVal, PageContext.REQUEST_SCOPE);
            messages = l.toArray(new String[l.size()]);
        }
        return messages;
    }

    public ReloadableResourceBundleMessageSource getMessageSource() {
        return messageSource;
    }

    public Locale getMessageLocale(@SuppressWarnings("UnusedParameters") PageContext context) {
        // l = context.getRequest().getLocale();
        return externalLogic.getCurrentLocale();
    }

    /**************************************************************************
     * Service setters for spring
     **************************************************************************
     */

    public void setLogic(IClickerLogic logic) {
        this.logic = logic;
    }

    public void setExternalLogic(ExternalLogic externalLogic) {
        this.externalLogic = externalLogic;
    }

    public void setMessageSource(ReloadableResourceBundleMessageSource messageSource) {
        this.messageSource = messageSource;
    }

}