Java tutorial
/* * Copyright 2011-2015 CAST, Inc. * * This file is part of the UDL Curriculum Toolkit: * see <http://udl-toolkit.cast.org>. * * The UDL Curriculum Toolkit is free software: you can redistribute and/or * modify it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * The UDL Curriculum Toolkit 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this software. If not, see <http://www.gnu.org/licenses/>. */ package org.cast.isi.page; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import lombok.Getter; import net.databinder.hib.Databinder; import net.databinder.models.hib.HibernateObjectModel; import org.apache.wicket.AttributeModifier; import org.apache.wicket.Component; import org.apache.wicket.MarkupContainer; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink; import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation; import org.apache.wicket.behavior.AttributeAppender; import org.apache.wicket.markup.html.IHeaderContributor; import org.apache.wicket.markup.html.IHeaderResponse; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.ChoiceRenderer; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.link.BookmarkablePageLink; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.markup.html.list.ListView; import org.apache.wicket.markup.repeater.RepeatingView; import org.apache.wicket.model.IModel; import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.model.ResourceModel; import org.apache.wicket.model.StringResourceModel; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.cast.cwm.components.AttributePrepender; import org.cast.cwm.components.ClassAttributeModifier; import org.cast.cwm.data.Prompt; import org.cast.cwm.data.Response; import org.cast.cwm.data.ResponseMetadata; import org.cast.cwm.data.Role; import org.cast.cwm.xml.XmlDocument; import org.cast.cwm.xml.XmlSection; import org.cast.cwm.xml.XmlSectionModel; import org.cast.isi.ISIApplication; import org.cast.isi.ISISession; import org.cast.isi.ISIXmlSection; import org.cast.isi.ResponseViewerFactory; import org.cast.isi.data.ContentLoc; import org.cast.isi.data.ISIPrompt; import org.cast.isi.data.ISIResponse; import org.cast.isi.data.PromptType; import org.cast.isi.panel.RemoveDialog; import org.cast.isi.panel.ResponseButtons; import org.cast.isi.panel.ResponseList; import org.cast.isi.panel.StudentScorePanel; import org.cast.isi.service.IISIResponseService; import org.hibernate.LockOptions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.Inject; /** * A base Notebook view. Notebook entries viewable by Chapter. Entries can be directly inside * the notebook or copied from the reading pages. * * @author jbrookover * */ @AuthorizeInstantiation("STUDENT") public class Notebook extends ISIBasePage implements IHeaderContributor { private static final long serialVersionUID = 1L; private static final Logger log = LoggerFactory.getLogger(Notebook.class); protected boolean isTeacher = false; // A map of responses in the notebook grouped by prompt protected Map<ISIPrompt, List<ISIResponse>> responseMap; protected ContentLoc currentLoc, currentChapterLoc; protected IModel<List<XmlSection>> mChapterList; @Getter protected DropDownChoice<XmlSection> chapterChoice; protected ResponseMetadata notebookMetadata; @Inject IISIResponseService responseService; private static final ResponseViewerFactory factory = new ResponseViewerFactory(); public Notebook(PageParameters parameters) { super(parameters); // set the heading for this page - modify the properties file to change this String pageTitleEnd = (new StringResourceModel("Notebook.pageTitle", this, null, "Notebook").getString()); setPageTitle(pageTitleEnd); add(new Label("pageTitle", new PropertyModel<String>(this, "pageTitle"))); isTeacher = ISISession.get().getUser().getRole().subsumes(Role.TEACHER); add(new Label("heading", isTeacher ? (pageTitleEnd + " - " + ISISession.get().getCurrentPeriodModel().getObject().getName() + " > " + ISISession.get().getTargetUserModel().getObject().getFullName()) : pageTitleEnd)); add(ISIApplication.get().getToolbar("tht", this)); setNotebookMetadata(notebookMetadata); getChapterList(); getInitChapter(parameters); addComponents(this); } protected void addComponents(MarkupContainer container) { addCloseButton(container); addChapterChoice(container); addNotebookResponses(container); populateResponseMap(); addResponses(container); } /** * Create a loadable detachable model of the Chapters (level 1). */ protected void getChapterList() { mChapterList = new LoadableDetachableModel<List<XmlSection>>() { private static final long serialVersionUID = 1L; @Override protected List<XmlSection> load() { List<XmlSection> chapterList = new ArrayList<XmlSection>(); for (XmlDocument doc : ISIApplication.get().getStudentContent()) { // For each XML document chapterList.addAll(doc.getTocSection().getChildren()); // get all chapters (level1) } return chapterList; } }; } /** * Determine which chapter you are currently in based on the location parameter * or the bookmark set for this user. */ protected void getInitChapter(PageParameters parameters) { // check if there is a location passed in otherwise default to the current bookmarked location if (parameters.getNamedKeys().contains("loc")) { currentLoc = new ContentLoc(parameters.get("loc").toString()); } else { currentLoc = ISIApplication.get().getBookmarkLoc(); } // Determine which chapter is the current chapter for (XmlSection chapter : mChapterList.getObject()) { if (chapter.isAncestorOf(currentLoc.getSection())) { currentChapterLoc = new ContentLoc(chapter); } else if (chapter.equals(currentLoc.getSection())) { currentChapterLoc = currentLoc; } } // if for some reason you can't find the chapter, then load the first chapter if (currentChapterLoc == null) { currentChapterLoc = new ContentLoc(mChapterList.getObject().get(0)); } } private void addCloseButton(MarkupContainer container) { WebMarkupContainer closeWindowLink = new WebMarkupContainer("closeWindow"); add(closeWindowLink); String onClickString = "if (typeof AutoSaver !== 'undefined') { AutoSaver.autoSaveMaybeSave(function() {window.close();});} else {window.close();}"; closeWindowLink.add(new AttributeAppender("onclick", onClickString)); container.add(closeWindowLink); } /** * Add a drop down choice of chapters. */ protected void addChapterChoice(MarkupContainer container) { Form<Void> chapterSelectForm = new Form<Void>("chapterSelectForm") { private static final long serialVersionUID = 1L; @Override protected void onSubmit() { super.onSubmit(); // only reload the page if the user has selected a new chapter ContentLoc newLoc = new ContentLoc((ISIXmlSection) chapterChoice.getModelObject()); if (!newLoc.equals(currentChapterLoc)) { currentLoc = newLoc; PageParameters param = new PageParameters(); param.add("loc", currentLoc.getLocation()); setResponsePage(ISIApplication.get().getNotebookPageClass(), param); } } }; container.add(chapterSelectForm); // display only the titles in the dropdown ChoiceRenderer<XmlSection> renderer = new ChoiceRenderer<XmlSection>("title"); chapterChoice = new DropDownChoice<XmlSection>("chapterChoice", new XmlSectionModel(currentChapterLoc.getSection()), mChapterList, renderer); chapterChoice.add(new AttributeModifier("autocomplete", "off")); chapterChoice.add(new AttributeModifier("ignore", "true")); chapterSelectForm.add(chapterChoice); AjaxSubmitLink submitLink = new AjaxSubmitLink("submitLink", chapterSelectForm) { private static final long serialVersionUID = 1L; @Override protected void onSubmit(AjaxRequestTarget target, Form<?> form) { target.prependJavaScript("AutoSaver.autoSaveMaybeSave(null);"); super.onSubmit(target, form); } }; String onClickString = "if (typeof AutoSaver !== 'undefined') { AutoSaver.autoSaveMaybeSave(null);}"; submitLink.add(new AttributePrepender("onclick", onClickString, ";")); chapterSelectForm.add(submitLink); } /** * Add a response list to the notebook. These are notes NOT associated with reading pages. * These responses are associated with the Chapter (level1). */ protected void addNotebookResponses(MarkupContainer container) { IModel<Prompt> mPrompt = responseService.getOrCreatePrompt(PromptType.NOTEBOOK_NOTES, currentChapterLoc); ResponseList responseList = new ResponseList("nbResponseList", mPrompt, notebookMetadata, currentChapterLoc, ISISession.get().getTargetUserModel()); String context = "notebook" + (isTeacher ? ".teacher" : ""); responseList.setContext(context); responseList.setAllowEdit(!isTeacher); responseList.setAllowNotebook(false); responseList.setAllowWhiteboard(false); container.add(responseList); ResponseButtons responseButtons = new ResponseButtons("responseButtons", mPrompt, notebookMetadata, currentLoc); responseButtons.setContext("notebook"); responseButtons.setVisible(!isTeacher); container.add(responseButtons); } /** * Fill the response map with responses from the database. This creates a TreeHashMap (sorted * by the Prompt's curriculum order). */ protected void populateResponseMap() { // get all the notebook responses for this target user, sorted by notebook insert time IModel<List<ISIResponse>> responseList = responseService .getAllNotebookResponsesByStudent(ISISession.get().getTargetUserModel()); responseMap = new TreeMap<ISIPrompt, List<ISIResponse>>(); // Loop through the responses. Group them by prompt, starting with the response added // to the notebook most recently. ISIXmlSection currentChapterSection = currentChapterLoc.getSection(); for (ISIResponse r : responseList.getObject()) { // check if the response is a child of the current chapter, if so add it to the response map ISIPrompt prompt = (ISIPrompt) r.getPrompt(); ISIXmlSection promptSection = new ContentLoc(prompt.getContentElement().getContentLocation()) .getSection(); if (promptSection != null) { if (currentChapterSection.isAncestorOf(promptSection)) { if (responseMap.containsKey(r.getPrompt())) responseMap.get(r.getPrompt()).add(r); else responseMap.put((ISIPrompt) r.getPrompt(), new ArrayList<ISIResponse>(Arrays.asList(r))); } } else { log.error("Notebook contained content item that no longer exists: {}", prompt); } } } /** * Add a repeater with the set of Prompts/Responses */ protected void addResponses(MarkupContainer container) { // "No Responses" message WebMarkupContainer noNotes = new WebMarkupContainer("noNotes"); container.add(noNotes.setVisible(responseMap.isEmpty())); RepeatingView noteRepeater = new RepeatingView("noteRepeater"); container.add(noteRepeater.setVisible(!responseMap.isEmpty())); for (Entry<ISIPrompt, List<ISIResponse>> entry : responseMap.entrySet()) { ISIPrompt currentPrompt = entry.getKey(); WebMarkupContainer promptGroup = new WebMarkupContainer(noteRepeater.newChildId()); noteRepeater.add(promptGroup); String crumbTrail = currentPrompt.getContentElement().getContentLocObject().getSection() .getCrumbTrailAsString(1, 1); promptGroup.add(new Label("responseHeader", crumbTrail)); // Prompt Icon promptGroup.add(ISIApplication.get().iconFor( currentPrompt.getContentElement().getContentLocObject().getSection().getSectionAncestor())); // Add the title and link to the page where this note is located BookmarkablePageLink<ISIStandardPage> link = new SectionLinkFactory().linkToPage("titleLink", currentPrompt.getContentElement().getContentLocObject().getSection()); link.add(new Label("sectionTitle", currentPrompt.getContentElement().getContentLocObject().getSection().getTitle())); link.add(new ClassAttributeModifier("sectionLink")); promptGroup.add(link); // Show the score promptGroup.add(new StudentScorePanel("responseScore", getModels(entry.getValue()))); // Text associated with Prompt promptGroup.add(factory.makeQuestionTextComponent("question", currentPrompt)); // The list of responses under this prompt promptGroup.add(makeResponseListView(entry)); } } private List<IModel<Response>> getModels(List<ISIResponse> responses) { List<IModel<Response>> result = new ArrayList<IModel<Response>>(); for (ISIResponse response : responses) { result.add(new HibernateObjectModel<Response>(response)); } return result; } private ListView<ISIResponse> makeResponseListView(Entry<ISIPrompt, List<ISIResponse>> entry) { final ISIPrompt currentPrompt = entry.getKey(); return new ListView<ISIResponse>("responseList", entry.getValue()) { private static final long serialVersionUID = 1L; @Override protected void populateItem(ListItem<ISIResponse> item) { // Anchor so links can jump to this id item.add(new WebMarkupContainer("responseAnchor") .add(new AttributeModifier("name", String.valueOf(item.getModelObject().getId())))); // Actual response item.add(factory.makeResponseViewComponent("response", item.getModel())); // Remove From Notebook button NotebookRemoveDialog removeDialog = new NotebookRemoveDialog("removeModal", item.getModel()); item.add(removeDialog); Component removeLink = new WebMarkupContainer("removeLink") .add(removeDialog.getClickToOpenBehavior()); removeLink.setVisible(!isTeacher); item.add(removeLink); // Link back to content BookmarkablePageLink<ISIStandardPage> editLink = new SectionLinkFactory().linkTo("editLink", currentPrompt.getContentElement().getContentLocObject().getSection(), currentPrompt.getContentElement().getXmlId()); editLink.add(new ClassAttributeModifier("sectionLink")); item.add(editLink); } }; } public void setNotebookMetadata(ResponseMetadata notebookMetadata) { this.notebookMetadata = ISIApplication.get().getResponseMetadata(); } @Override public String getPageName() { return "notebook"; } @Override public String getPageType() { return "notebook"; } @Override public String getPageViewDetail() { return null; } public void renderHead(IHeaderResponse response) { super.renderHead(response); renderThemeCSS(response, "css/window.css"); renderThemeCSS(response, "css/window_print.css", "print"); response.renderOnLoadJavaScript("bindSectionOpenerLinks()"); } @Override protected void onDetach() { if (mChapterList != null) mChapterList.detach(); super.onDetach(); } protected class NotebookRemoveDialog extends RemoveDialog { private static final long serialVersionUID = 1L; public NotebookRemoveDialog(String id, IModel<?> model) { super(id, model); } protected void removeObject() { ISIResponse resp = (ISIResponse) getDefaultModelObject(); // Lock to current session - necessary since objects are stored detached in responseMap Databinder.getHibernateSession().buildLockRequest(LockOptions.UPGRADE).lock(resp); responseService.removeFromNotebook(resp, getPage()); setResponsePage(ISIApplication.get().getNotebookPageClass(), getPageParameters()); } @Override protected String getDialogTitle() { return new ResourceModel("Notebook.removeDialogTitle", "Remove from Notebook?").getObject(); } @Override protected String getDialogText() { return new ResourceModel("Notebook.removeDialogText", "Are you sure you want to remove this response from the Notebook?").getObject(); } } }