/********************************************************************************** * $URL: $ * $Id: $ *********************************************************************************** * * This was was originally part of Simple Page Tool and was * * Copyright (c) 2007 Sakai Project/Sakai Foundation * Licensed under the Educational Community License version 1.0 * * The author was Joshua Ryan josh@asu.edu * * However this version is primarily new code. The new code is * * Copyright (c) 2010 Rutgers, the State University of New Jersey * * Author: Eric Jeney, jeney@rutgers.edu * * 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.lessonbuildertool.tool.producers; import java.io.IOException; import java.lang.reflect.Method; import java.net.HttpURLConnection; import java.net.ProtocolException; import java.net.URL; import java.net.URLEncoder; import java.text.DateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.StringTokenizer; import java.util.TimeZone; import java.util.Calendar; import java.text.SimpleDateFormat; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import javax.xml.bind.DatatypeConverter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sakaiproject.component.cover.ComponentManager; import org.sakaiproject.component.cover.ServerConfigurationService; import org.sakaiproject.content.api.ContentHostingService; import org.sakaiproject.content.api.ContentResource; import org.sakaiproject.content.cover.ContentTypeImageService; import org.sakaiproject.entity.api.ResourceProperties; import org.sakaiproject.event.api.UsageSession; import org.sakaiproject.event.cover.UsageSessionService; import org.sakaiproject.lessonbuildertool.SimplePage; import org.sakaiproject.lessonbuildertool.SimplePageComment; import org.sakaiproject.lessonbuildertool.SimplePageItem; import org.sakaiproject.lessonbuildertool.SimplePageLogEntry; import org.sakaiproject.lessonbuildertool.SimplePageQuestionAnswer; import org.sakaiproject.lessonbuildertool.SimplePageQuestionResponse; import org.sakaiproject.lessonbuildertool.SimplePageQuestionResponseTotals; import org.sakaiproject.lessonbuildertool.SimplePagePeerEvalResult; import org.sakaiproject.lessonbuildertool.SimpleStudentPage; import org.sakaiproject.lessonbuildertool.model.SimplePageToolDao; import org.sakaiproject.lessonbuildertool.service.BltiInterface; import org.sakaiproject.lessonbuildertool.service.LessonEntity; import org.sakaiproject.lessonbuildertool.tool.beans.SimplePageBean; import org.sakaiproject.lessonbuildertool.tool.beans.SimplePageBean.GroupEntry; import org.sakaiproject.lessonbuildertool.tool.beans.SimplePageBean.Status; import org.sakaiproject.lessonbuildertool.tool.evolvers.SakaiFCKTextEvolver; import org.sakaiproject.lessonbuildertool.tool.view.CommentsGradingPaneViewParameters; import org.sakaiproject.lessonbuildertool.tool.view.CommentsViewParameters; import org.sakaiproject.lessonbuildertool.tool.view.FilePickerViewParameters; import org.sakaiproject.lessonbuildertool.tool.view.GeneralViewParameters; import org.sakaiproject.lessonbuildertool.tool.view.QuestionGradingPaneViewParameters; import org.sakaiproject.lessonbuildertool.tool.view.ExportCCViewParameters; import org.sakaiproject.lessonbuildertool.service.LessonBuilderAccessService; import org.sakaiproject.memory.api.Cache; import org.sakaiproject.memory.api.MemoryService; import org.sakaiproject.time.api.TimeService; import org.sakaiproject.tool.api.Placement; import org.sakaiproject.tool.api.Session; import org.sakaiproject.tool.api.ToolManager; import org.sakaiproject.tool.api.ToolSession; import org.sakaiproject.tool.cover.SessionManager; import org.sakaiproject.user.api.UserNotDefinedException; import org.sakaiproject.user.cover.UserDirectoryService; import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.util.ResourceLoader; import org.sakaiproject.util.FormattedText; import org.sakaiproject.portal.util.CSSUtils; import uk.org.ponder.localeutil.LocaleGetter; import uk.org.ponder.messageutil.MessageLocator; import uk.org.ponder.rsf.builtin.UVBProducer; import uk.org.ponder.rsf.components.UIBoundBoolean; import uk.org.ponder.rsf.components.UIBoundString; import uk.org.ponder.rsf.components.UIBranchContainer; import uk.org.ponder.rsf.components.UICommand; import uk.org.ponder.rsf.components.UIComponent; import uk.org.ponder.rsf.components.UIContainer; import uk.org.ponder.rsf.components.UIELBinding; import uk.org.ponder.rsf.components.UIForm; import uk.org.ponder.rsf.components.UIInitBlock; import uk.org.ponder.rsf.components.UIInput; import uk.org.ponder.rsf.components.UIInternalLink; import uk.org.ponder.rsf.components.UILink; import uk.org.ponder.rsf.components.UIOutput; import uk.org.ponder.rsf.components.UISelect; import uk.org.ponder.rsf.components.UISelectChoice; import uk.org.ponder.rsf.components.UIVerbatim; import uk.org.ponder.rsf.components.decorators.UIDisabledDecorator; import uk.org.ponder.rsf.components.decorators.UIFreeAttributeDecorator; import uk.org.ponder.rsf.components.decorators.UIStyleDecorator; import uk.org.ponder.rsf.components.decorators.UITooltipDecorator; import uk.org.ponder.rsf.evolvers.FormatAwareDateInputEvolver; import uk.org.ponder.rsf.evolvers.TextInputEvolver; import uk.org.ponder.rsf.flow.jsfnav.NavigationCase; import uk.org.ponder.rsf.flow.jsfnav.NavigationCaseReporter; import uk.org.ponder.rsf.view.ComponentChecker; import uk.org.ponder.rsf.view.DefaultView; import uk.org.ponder.rsf.view.ViewComponentProducer; import uk.org.ponder.rsf.viewstate.SimpleViewParameters; import uk.org.ponder.rsf.viewstate.ViewParameters; import uk.org.ponder.rsf.viewstate.ViewParamsReporter; import org.apache.commons.lang.StringEscapeUtils; /** * This produces the primary view of the page. It also handles the editing of * the properties of most of the items (through JQuery dialogs). * * @author Eric Jeney <jeney@rutgers.edu> */ public class ShowPageProducer implements ViewComponentProducer, DefaultView, NavigationCaseReporter, ViewParamsReporter { private static Log log = LogFactory.getLog(ShowPageProducer.class); String reqStar = "<span class=\"reqStar\">*</span>"; private SimplePageBean simplePageBean; private SimplePageToolDao simplePageToolDao; private FormatAwareDateInputEvolver dateevolver; private TimeService timeService; private HttpServletRequest httpServletRequest; private HttpServletResponse httpServletResponse; // have to do it here because we need it in urlCache. It has to happen before Spring initialization private static MemoryService memoryService = (MemoryService)ComponentManager.get(MemoryService.class); private ToolManager toolManager; public TextInputEvolver richTextEvolver; private LessonBuilderAccessService lessonBuilderAccessService; private Map<String,String> imageToMimeMap; public void setImageToMimeMap(Map<String,String> map) { this.imageToMimeMap = map; } public boolean useSakaiIcons = ServerConfigurationService.getBoolean("lessonbuilder.use-sakai-icons", false); public boolean allowSessionId = ServerConfigurationService.getBoolean("session.parameter.allow", false); public boolean allowCcExport = ServerConfigurationService.getBoolean("lessonbuilder.cc-export", true); // I don't much like the static, because it opens us to a possible race // condition, but I don't see much option // see the setter. It has to be static because it's used in makeLink, which // is static so it can be used // by ReorderProducer. I wonder if this whole producer could be made // application scope? private static LessonEntity forumEntity; private static LessonEntity quizEntity; private static LessonEntity assignmentEntity; private static LessonEntity bltiEntity; public MessageLocator messageLocator; private LocaleGetter localegetter; public static final String VIEW_ID = "ShowPage"; private static final String DEFAULT_HTML_TYPES = "html,xhtml,htm,xht"; private static String[] htmlTypes = null; // mp4 means it plays with the flash player if HTML5 doesn't work. // flv is also played with the flash player, but it doesn't get a backup <OBJECT> inside the player // Strobe claims to handle MOV files as well, but I feel safer passing them to quicktime, though that requires Quicktime installation private static final String DEFAULT_MP4_TYPES = "video/mp4,video/m4v,audio/mpeg"; private static String[] mp4Types = null; private static final String DEFAULT_HTML5_TYPES = "video/mp4,video/m4v,video/webm,video/ogg,audio/mpeg,audio/ogg,audio/wav,audio/x-wav,audio/webm,audio/ogg,audio/mp4,audio/aac"; // jw can also handle audio: audio/mp4,audio/mpeg,audio/ogg private static String[] html5Types = null; // WARNING: this must occur after memoryService, for obvious reasons. // I'm doing it this way because it doesn't appear that Spring can do this kind of initialization // and it's better to let Java's initialization code handle synchronization than do it ourselves in // an init method private static Cache urlCache = memoryService.newCache("org.sakaiproject.lessonbuildertool.tool.producers.ShowPageProducer.url.cache"); String browserString = ""; // set by checkIEVersion; protected static final int DEFAULT_EXPIRATION = 10 * 60; static final String ICONSTYLE = "\n.portletTitle .action img {\n background: url({}/help.gif) center right no-repeat;\n}\n.portletTitle .action img:hover, .portletTitle .action img:focus {\n background: url({}/help_h.gif) center right no-repeat\n}\n.portletTitle .title img {\n background: url({}/reload.gif) center left no-repeat;\n}\n.portletTitle .title img:hover, .portletTitle .title img:focus {\n background: url({}/reload_h.gif) center left no-repeat\n}\n"; public String getViewID() { return VIEW_ID; } // this code is written to handle the fact the CSS uses NNNpx and old code // NNN. We need to be able to convert. // Length is intended to be a neutral representation. getOld returns without // px, getNew with px, and getOrig // the original version public class Length { String number; String unit; Length(String spec) { spec = spec.trim(); int numlen; for (numlen = 0; numlen < spec.length(); numlen++) { if (!Character.isDigit(spec.charAt(numlen))) { break; } } number = spec.substring(0, numlen).trim(); unit = spec.substring(numlen).trim().toLowerCase(); } public String getOld() { return number + (unit.equals("px") ? "" : unit); } public String getNew() { return number + (unit.equals("") ? "px" : unit); } } // problem is it needs to work with a null argument public static String getOrig(Length l) { if (lengthOk(l)) return l.number + l.unit; else return ""; } // do we have a valid length? public static boolean lengthOk(Length l) { if (l == null || l.number == null || l.number.equals("")) { if (l != null && l.unit.equals("auto")) return true; return false; } return true; } // created style arguments. This was done at the time when i thought // the OBJECT tag actually paid attention to the CSS size. it doesn't. public String getStyle(Length w, Length h) { String ret = null; if (lengthOk(w)) ret = "width:" + w.getNew(); if (lengthOk(h)) { if (ret != null) ret = ret + ";"; ret = ret + "height:" + h.getNew(); } return ret; } // produce abbreviated versions of URLs, for use in constructing titles public String abbrevUrl(String url) { if (url.startsWith("/")) { int suffix = url.lastIndexOf("/"); if (suffix > 0) { url = url.substring(suffix + 1); } if (url.startsWith("http:__")) { url = url.substring(7); suffix = url.indexOf("_"); if (suffix > 0) { url = messageLocator.getMessage("simplepage.fromhost").replace("{}", url.substring(0, suffix)); } } else if (url.startsWith("https:__")) { url = url.substring(8); suffix = url.indexOf("_"); if (suffix > 0) { url = messageLocator.getMessage("simplepage.fromhost").replace("{}", url.substring(0, suffix)); } } } else { // external, the hostname is probably best try { URL u = new URL(url); url = messageLocator.getMessage("simplepage.fromhost").replace("{}", u.getHost()); } catch (Exception ignore) { log.error("exception in abbrevurl " + ignore); } ; } return url; } public String myUrl() { // previously we computed something, but this will give us the official one return ServerConfigurationService.getServerUrl(); } // NOTE: // pages should normally be called with 3 arguments: // sendingPageId - the page to show // itemId - the item used to choose the page, because pages can occur in // different places, and we need // to know the context in which this was called. Note that there's an item // even for top-level pages // path - push, next, or a number. The number is an index into the // breadcrumbs if someone clicks // on breadcrumbs. This item is used to maintain the path (the internal // form of the breadcrumbs) // missing is treated as next. // for startup, none of this will be known, so getCurrentPage will find the // top level page and item if // nothing is specified public void fillComponents(UIContainer tofill, ViewParameters viewParams, ComponentChecker checker) { GeneralViewParameters params = (GeneralViewParameters) viewParams; UIOutput.make(tofill, "html").decorate(new UIFreeAttributeDecorator("lang", localegetter.get().getLanguage())) .decorate(new UIFreeAttributeDecorator("xml:lang", localegetter.get().getLanguage())); boolean iframeJavascriptDone = false; // security model: // canEditPage and canReadPage are normal Sakai privileges. They apply // to all // pages in the site. // However when presented with a page, we need to make sure it's // actually in // this site, or users could get to pages in other sites. That's done // by updatePageObject. The model is that producers always work on the // current page, and updatePageObject makes sure that is in the current // site. // At that point we can safely use canEditPage. // somewhat misleading. sendingPage specifies the page we're supposed to // go to. If path is "none", we don't want this page to be what we see // when we come back to the tool if (params.getSendingPage() != -1) { // will fail if page not in this site // security then depends upon making sure that we only deal with // this page try { simplePageBean.updatePageObject(params.getSendingPage(), !params.getPath().equals("none")); } catch (Exception e) { log.warn("ShowPage permission exception " + e); UIOutput.make(tofill, "error-div"); UIOutput.make(tofill, "error", messageLocator.getMessage("simplepage.not_available")); return; } } boolean canEditPage = simplePageBean.canEditPage(); boolean canReadPage = simplePageBean.canReadPage(); boolean canSeeAll = simplePageBean.canSeeAll(); // always on if caneditpage boolean cameFromGradingPane = params.getPath().equals("none"); if (!canReadPage) { // this code is intended for the situation where site permissions // haven't been set up. // So if the user can't read the page (which is pretty abnormal), // see if they have site.upd. // if so, give them some explanation and offer to call the // permissions helper String ref = "/site/" + simplePageBean.getCurrentSiteId(); if (simplePageBean.canEditSite()) { SimplePage currentPage = simplePageBean.getCurrentPage(); UIOutput.make(tofill, "needPermissions"); GeneralViewParameters permParams = new GeneralViewParameters(); permParams.setSendingPage(-1L); createStandardToolBarLink(PermissionsHelperProducer.VIEW_ID, tofill, "callpermissions", "simplepage.permissions", permParams, "simplepage.permissions.tooltip"); } // in any case, tell them they can't read the page UIOutput.make(tofill, "error-div"); UIOutput.make(tofill, "error", messageLocator.getMessage("simplepage.nopermissions")); return; } if (params.addTool == GeneralViewParameters.COMMENTS) { simplePageBean.addCommentsSection(); }else if(params.addTool == GeneralViewParameters.STUDENT_CONTENT) { simplePageBean.addStudentContentSection(); }else if(params.addTool == GeneralViewParameters.STUDENT_PAGE) { simplePageBean.createStudentPage(params.studentItemId); canEditPage = simplePageBean.canEditPage(); } // Find the MSIE version, if we're running it. int ieVersion = checkIEVersion(); // as far as I can tell, none of these supports fck or ck // we can make it configurable if necessary, or use WURFL // however this test is consistent with CKeditor's check. // that desireable, since if CKeditor is going to use a bare // text block, we want to handle it as noEditor String userAgent = httpServletRequest.getHeader("User-Agent"); if (userAgent == null) userAgent = ""; boolean noEditor = userAgent.toLowerCase().indexOf("mobile") >= 0; // set up locale Locale M_locale = null; String langLoc[] = localegetter.get().toString().split("_"); if (langLoc.length >= 2) { if ("en".equals(langLoc[0]) && "ZA".equals(langLoc[1])) { M_locale = new Locale("en", "GB"); } else { M_locale = new Locale(langLoc[0], langLoc[1]); } } else { M_locale = new Locale(langLoc[0]); } // clear session attribute if necessary, after calling Samigo String clearAttr = params.getClearAttr(); if (clearAttr != null && !clearAttr.equals("")) { Session session = SessionManager.getCurrentSession(); // don't let users clear random attributes if (clearAttr.startsWith("LESSONBUILDER_RETURNURL")) { session.setAttribute(clearAttr, null); } } if (htmlTypes == null) { String mmTypes = ServerConfigurationService.getString("lessonbuilder.html.types", DEFAULT_HTML_TYPES); htmlTypes = mmTypes.split(","); for (int i = 0; i < htmlTypes.length; i++) { htmlTypes[i] = htmlTypes[i].trim().toLowerCase(); } Arrays.sort(htmlTypes); } if (mp4Types == null) { String m4Types = ServerConfigurationService.getString("lessonbuilder.mp4.types", DEFAULT_MP4_TYPES); mp4Types = m4Types.split(","); for (int i = 0; i < mp4Types.length; i++) { mp4Types[i] = mp4Types[i].trim().toLowerCase(); } Arrays.sort(mp4Types); } if (html5Types == null) { String jTypes = ServerConfigurationService.getString("lessonbuilder.html5.types", DEFAULT_HTML5_TYPES); html5Types = jTypes.split(","); for (int i = 0; i < html5Types.length; i++) { html5Types[i] = html5Types[i].trim().toLowerCase(); } Arrays.sort(html5Types); } // remember that page tool was reset, so we need to give user the option // of going to the last page from the previous session SimplePageToolDao.PageData lastPage = simplePageBean.toolWasReset(); // if this page was copied from another site we may have to update links // can only do the fixups if you can write. We could hack permissions, but // I assume a site owner will access the site first if (canEditPage) simplePageBean.maybeUpdateLinks(); // if starting the tool, sendingpage isn't set. the following call // will give us the top page. SimplePage currentPage = simplePageBean.getCurrentPage(); // now we need to find our own item, for access checks, etc. SimplePageItem pageItem = null; if (currentPage != null) { pageItem = simplePageBean.getCurrentPageItem(params.getItemId()); } // one more security check: make sure the item actually involves this // page. // otherwise someone could pass us an item from a different page in // another site // actually this normally happens if the page doesn't exist and we don't // have permission to create it if (currentPage == null || pageItem == null || (pageItem.getType() != SimplePageItem.STUDENT_CONTENT &&Long.valueOf(pageItem.getSakaiId()) != currentPage.getPageId())) { log.warn("ShowPage item not in page"); UIOutput.make(tofill, "error-div"); if (currentPage == null) // most likely tool was created by site info but no page // has created. It will created the first time an item is created, // so from a user point of view it looks like no item has been added UIOutput.make(tofill, "error", messageLocator.getMessage("simplepage.noitems_error_user")); else UIOutput.make(tofill, "error", messageLocator.getMessage("simplepage.not_available")); return; } // check two parts of isitemvisible where we want to give specific errors // potentially need time zone for setting release date if (!canSeeAll && currentPage.getReleaseDate() != null && currentPage.getReleaseDate().after(new Date())) { DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, M_locale); TimeZone tz = timeService.getLocalTimeZone(); df.setTimeZone(tz); String releaseDate = df.format(currentPage.getReleaseDate()); String releaseMessage = messageLocator.getMessage("simplepage.not_yet_available_releasedate").replace("{}", releaseDate); UIOutput.make(tofill, "error-div"); UIOutput.make(tofill, "error", releaseMessage); return; } // the only thing not already tested in isItemVisible is groups. In theory // no one should have a URL to a page for which they aren't in the group, // so I'm not trying to give a better message than just hidden if (!canSeeAll && currentPage.isHidden() || !simplePageBean.isItemVisible(pageItem)) { UIOutput.make(tofill, "error-div"); UIOutput.make(tofill, "error", messageLocator.getMessage("simplepage.not_available_hidden")); return; } // I believe we've now checked all the args for permissions issues. All // other item and // page references are generated here based on the contents of the page // and items. // needed to process path arguments first, so refresh page goes the right page if (simplePageBean.getTopRefresh()) { UIOutput.make(tofill, "refresh"); return; // but there's no point doing anything more } // error from previous operation // consumes the message, so don't do it if refreshing List<String> errMessages = simplePageBean.errMessages(); if (errMessages != null) { UIOutput.make(tofill, "error-div"); for (String e: errMessages) { UIBranchContainer er = UIBranchContainer.make(tofill, "errors:"); UIOutput.make(er, "error-message", e); } } if (canEditPage) { // special instructor-only javascript setup. // but not if we're refreshing UIOutput.make(tofill, "instructoronly"); // Chome and IE will abort a page if some on it was input from // a previous submit. I.e. if an HTML editor was used. In theory they // only do this if part of it is Javascript, but in practice they do // it for images as well. The protection isn't worthwhile, since it only // protects the first time. Since it will reesult in a garbled page, // people will just refresh the page, and then they'll get the new // contents. The Chrome guys refuse to fix this so it just applies to Javascript httpServletResponse.setHeader("X-XSS-Protection", "0"); } if (currentPage == null || pageItem == null) { UIOutput.make(tofill, "error-div"); if (canEditPage) { UIOutput.make(tofill, "error", messageLocator.getMessage("simplepage.impossible1")); } else { UIOutput.make(tofill, "error", messageLocator.getMessage("simplepage.not_available")); } return; } // Set up customizable CSS ContentResource cssLink = simplePageBean.getCssForCurrentPage(); if(cssLink != null) { UIOutput.make(tofill, "customCSS").decorate(new UIFreeAttributeDecorator("href", cssLink.getUrl())); } // offer to go to saved page if this is the start of a session, in case // user has logged off and logged on again. // need to offer to go to previous page? even if a new session, no need // if we're already on that page if (lastPage != null && lastPage.pageId != currentPage.getPageId()) { UIOutput.make(tofill, "refreshAlert"); UIOutput.make(tofill, "refresh-message", messageLocator.getMessage("simplepage.last-visited")); // Should simply refresh GeneralViewParameters p = new GeneralViewParameters(VIEW_ID); p.setSendingPage(lastPage.pageId); p.setItemId(lastPage.itemId); // reset the path to the saved one p.setPath("log"); String name = lastPage.name; // Titles are set oddly by Student Content Pages SimplePage lastPageObj = simplePageToolDao.getPage(lastPage.pageId); if(lastPageObj.getOwner() != null) { name = lastPageObj.getTitle(); } UIInternalLink.make(tofill, "refresh-link", name, p); } // path is the breadcrumbs. Push, pop or reset depending upon path= // programmer documentation. String title; String ownerName = null; if(pageItem.getType() != SimplePageItem.STUDENT_CONTENT) { title = pageItem.getName(); }else { title = currentPage.getTitle(); if(!pageItem.isAnonymous() || canEditPage) { try { String owner = currentPage.getOwner(); String group = currentPage.getGroup(); if (group != null) ownerName = simplePageBean.getCurrentSite().getGroup(group).getTitle(); else ownerName = UserDirectoryService.getUser(owner).getDisplayName(); } catch (Exception ignore) {}; if (ownerName != null && !ownerName.equals(title)) title += " (" + ownerName + ")"; } } String newPath = null; // If the path is "none", then we don't want to record this page as being viewed, or set a path if(!params.getPath().equals("none")) { newPath = simplePageBean.adjustPath(params.getPath(), currentPage.getPageId(), pageItem.getId(), title); simplePageBean.adjustBackPath(params.getBackPath(), currentPage.getPageId(), pageItem.getId(), pageItem.getName()); } // put out link to index of pages GeneralViewParameters showAll = new GeneralViewParameters(PagePickerProducer.VIEW_ID); showAll.setSource("summary"); UIInternalLink.make(tofill, "show-pages", messageLocator.getMessage("simplepage.showallpages"), showAll); if (canEditPage) { // show tool bar, but not if coming from grading pane if(!cameFromGradingPane) { createToolBar(tofill, currentPage, (pageItem.getType() == SimplePageItem.STUDENT_CONTENT)); } UIOutput.make(tofill, "title-descrip"); String label = null; if (pageItem.getType() == SimplePageItem.STUDENT_CONTENT) label = messageLocator.getMessage("simplepage.editTitle"); else label = messageLocator.getMessage("simplepage.title"); String descrip = null; if (pageItem.getType() == SimplePageItem.STUDENT_CONTENT) descrip = messageLocator.getMessage("simplepage.title-student-descrip"); else if (pageItem.getPageId() == 0) descrip = messageLocator.getMessage("simplepage.title-top-descrip"); else descrip = messageLocator.getMessage("simplepage.title-descrip"); UIOutput.make(tofill, "edit-title").decorate(new UIFreeAttributeDecorator("title", descrip)); UIOutput.make(tofill, "edit-title-text", label); UIOutput.make(tofill, "title-descrip-text", descrip); if (pageItem.getPageId() == 0 && currentPage.getOwner() == null) { // top level page // need dropdown UIOutput.make(tofill, "dropdown"); UIOutput.make(tofill, "moreDiv"); UIOutput.make(tofill, "new-page").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.new-page-tooltip"))); UIOutput.make(tofill, "import-cc").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.import_cc.tooltip"))); UIOutput.make(tofill, "export-cc").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.export_cc.tooltip"))); } // Checks to see that user can edit and that this is either a top level page, // or a top level student page (not a subpage to a student page) if(simplePageBean.getEditPrivs() == 0 && (pageItem.getPageId() == 0)) { UIOutput.make(tofill, "remove-li"); UIOutput.make(tofill, "remove-page").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.remove-page-tooltip"))); } else if (simplePageBean.getEditPrivs() == 0 && currentPage.getOwner() != null) { // getEditPrivs < 2 if we want to let the student delete. Currently we don't. There can be comments // from other students and the page can be shared SimpleStudentPage studentPage = simplePageToolDao.findStudentPage(currentPage.getTopParent()); if (studentPage != null && studentPage.getPageId() == currentPage.getPageId()) { System.out.println("is student page 2"); UIOutput.make(tofill, "remove-student"); UIOutput.make(tofill, "remove-page-student").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.remove-page-tooltip"))); } } UIOutput.make(tofill, "dialogDiv"); } else if (!canReadPage) { return; } else if (!canSeeAll) { // see if there are any unsatisfied prerequisites // if this isn't a top level page, this will check that the page above is // accessible. That matters because we check visible, available and release // only for this page but not for the containing page List<String> needed = simplePageBean.pagesNeeded(pageItem); if (needed.size() > 0) { // yes. error and abort if (pageItem.getPageId() != 0) { // not top level. This should only happen from a "next" // link. // at any rate, the best approach is to send the user back // to the calling page List<SimplePageBean.PathEntry> path = simplePageBean.getHierarchy(); SimplePageBean.PathEntry containingPage = null; if (path.size() > 1) { // page above this. this page is on the top containingPage = path.get(path.size() - 2); } if (containingPage != null) { // not a top level page, point // to containing page GeneralViewParameters view = new GeneralViewParameters(VIEW_ID); view.setSendingPage(containingPage.pageId); view.setItemId(containingPage.pageItemId); view.setPath(Integer.toString(path.size() - 2)); UIInternalLink.make(tofill, "redirect-link", containingPage.title, view); UIOutput.make(tofill, "redirect"); } else { UIOutput.make(tofill, "error-div"); UIOutput.make(tofill, "error", messageLocator.getMessage("simplepage.not_available")); } return; } // top level page where prereqs not satisified. Output list of // pages he needs to do first UIOutput.make(tofill, "pagetitle", currentPage.getTitle()); UIOutput.make(tofill, "error-div"); UIOutput.make(tofill, "error", messageLocator.getMessage("simplepage.has_prerequistes")); UIBranchContainer errorList = UIBranchContainer.make(tofill, "error-list:"); for (String errorItem : needed) { UIBranchContainer errorListItem = UIBranchContainer.make(errorList, "error-item:"); UIOutput.make(errorListItem, "error-item-text", errorItem); } return; } } ToolSession toolSession = SessionManager.getCurrentToolSession(); String helpurl = (String)toolSession.getAttribute("sakai-portal:help-action"); String reseturl = (String)toolSession.getAttribute("sakai-portal:reset-action"); String skinName = null; String skinRepo = null; String iconBase = null; UIComponent titlediv = UIOutput.make(tofill, "titlediv"); // we need to do special CSS for old portal if (helpurl == null) titlediv.decorate(new UIStyleDecorator("oldPortal")); if (helpurl != null || reseturl != null) { // these URLs are defined if we're in the neo portal // in that case we need our own help and reset icons. We want // to take them from the current skin, so find its prefix. // unfortunately the neoportal tacks neo- on front of the skin // name, so this is more complex than you might think. skinRepo = ServerConfigurationService.getString("skin.repo", "/library/skin"); iconBase = skinRepo + "/" + CSSUtils.adjustCssSkinFolder(null) + "/images"; UIVerbatim.make(tofill, "iconstyle", ICONSTYLE.replace("{}", iconBase)); } if (helpurl != null) { UILink.make(tofill, (pageItem.getPageId() == 0 ? "helpbutton" : "helpbutton2")). decorate(new UIFreeAttributeDecorator("onclick", "openWindow('" + helpurl + "', 'Help', 'resizeable=yes,toolbar=no,scrollbars=yes,menubar=yes,width=800,height=600'); return false")). decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.help-button"))); UIOutput.make(tofill, (pageItem.getPageId() == 0 ? "helpimage" : "helpimage2")). decorate(new UIFreeAttributeDecorator("alt", messageLocator.getMessage("simplepage.help-button"))); UIOutput.make(tofill, (pageItem.getPageId() == 0 ? "helpnewwindow" : "helpnewwindow2"), messageLocator.getMessage("simplepage.opens-in-new")); } if (reseturl != null) { UILink.make(tofill, (pageItem.getPageId() == 0 ? "resetbutton" : "resetbutton2")). decorate(new UIFreeAttributeDecorator("onclick", "location.href='" + reseturl + "'; return false")). decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.reset-button"))); UIOutput.make(tofill, (pageItem.getPageId() == 0 ? "resetimage" : "resetimage2")). decorate(new UIFreeAttributeDecorator("alt", messageLocator.getMessage("simplepage.reset-button"))); } // note page accessed. the code checks to see whether all the required // items on it have been finished, and if so marks it complete, else just updates // access date save the path because if user goes to it later we want to restore the // breadcrumbs if(newPath != null) { if(pageItem.getType() != SimplePageItem.STUDENT_CONTENT) { simplePageBean.track(pageItem.getId(), newPath); }else { simplePageBean.track(pageItem.getId(), newPath, currentPage.getPageId()); } } UIOutput.make(tofill, "pagetitle", currentPage.getTitle()); if(currentPage.getOwner() != null && simplePageBean.getEditPrivs() == 0) { SimpleStudentPage student = simplePageToolDao.findStudentPageByPageId(currentPage.getPageId()); // Make sure this is a top level student page if(student != null && pageItem.getGradebookId() != null) { UIOutput.make(tofill, "gradingSpan"); UIOutput.make(tofill, "commentsUUID", String.valueOf(student.getId())); UIOutput.make(tofill, "commentPoints", String.valueOf((student.getPoints() != null? student.getPoints() : ""))); UIOutput pointsBox = UIOutput.make(tofill, "studentPointsBox"); UIOutput.make(tofill, "topmaxpoints", String.valueOf((pageItem.getGradebookPoints() != null? pageItem.getGradebookPoints():""))); if (ownerName != null) pointsBox.decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.grade-for-student").replace("{}",ownerName))); List<SimpleStudentPage> studentPages = simplePageToolDao.findStudentPages(student.getItemId()); Collections.sort(studentPages, new Comparator<SimpleStudentPage>() { public int compare(SimpleStudentPage o1, SimpleStudentPage o2) { String title1 = o1.getTitle(); if (title1 == null) title1 = ""; String title2 = o2.getTitle(); if (title2 == null) title2 = ""; return title1.compareTo(title2); } }); for(int in = 0; in < studentPages.size(); in++) { if(studentPages.get(in).isDeleted()) { studentPages.remove(in); } } int i = -1; for(int in = 0; in < studentPages.size(); in++) { if(student.getId() == studentPages.get(in).getId()) { i = in; break; } } if(i > 0) { GeneralViewParameters eParams = new GeneralViewParameters(ShowPageProducer.VIEW_ID, studentPages.get(i-1).getPageId()); eParams.setItemId(studentPages.get(i-1).getItemId()); eParams.setPath("next"); UIInternalLink.make(tofill, "gradingBack", eParams); } if(i < studentPages.size() - 1) { GeneralViewParameters eParams = new GeneralViewParameters(ShowPageProducer.VIEW_ID, studentPages.get(i+1).getPageId()); eParams.setItemId(studentPages.get(i+1).getItemId()); eParams.setPath("next"); UIInternalLink.make(tofill, "gradingForward", eParams); } printGradingForm(tofill); } } // breadcrumbs if (pageItem.getPageId() != 0) { // Not top-level, so we have to show breadcrumbs List<SimplePageBean.PathEntry> breadcrumbs = simplePageBean.getHierarchy(); int index = 0; if (breadcrumbs.size() > 1 || reseturl != null || helpurl != null) { UIOutput.make(tofill, "crumbdiv"); if (breadcrumbs.size() > 1) for (SimplePageBean.PathEntry e : breadcrumbs) { // don't show current page. We already have a title. This // was too much UIBranchContainer crumb = UIBranchContainer.make(tofill, "crumb:"); GeneralViewParameters view = new GeneralViewParameters(VIEW_ID); view.setSendingPage(e.pageId); view.setItemId(e.pageItemId); view.setPath(Integer.toString(index)); if (index < breadcrumbs.size() - 1) { // Not the last item UIInternalLink.make(crumb, "crumb-link", e.title, view); UIOutput.make(crumb, "crumb-follow", " > "); } else { UIOutput.make(crumb, "crumb-follow", e.title).decorate(new UIStyleDecorator("bold")); } index++; } } } // see if there's a next item in sequence. simplePageBean.addPrevLink(tofill, pageItem); simplePageBean.addNextLink(tofill, pageItem); // swfObject is not currently used boolean shownSwfObject = false; // items to show List<SimplePageItem> itemList = (List<SimplePageItem>) simplePageBean.getItemsOnPage(currentPage.getPageId()); // Move all items with sequence <= 0 to the end of the list. // Count is necessary to guarantee we don't infinite loop over a // list that only has items with sequence <= 0. // Becauses sequence number is < 0, these start out at the beginning int count = 1; while(itemList.size() > count && itemList.get(0).getSequence() <= 0) { itemList.add(itemList.remove(0)); count++; } // Make sure we only add the comments javascript file once, // even if there are multiple comments tools on the page. boolean addedCommentsScript = false; int commentsCount = 0; // Find the most recent comment on the page by current user long postedCommentId = -1; if (params.postedComment) { postedCommentId = findMostRecentComment(); } // // // MAIN list of items // // produce the main table // Is anything visible? // Note that we don't need to check whether any item is available, since the first visible // item is always available. boolean anyItemVisible = false; if (itemList.size() > 0) { UIBranchContainer container = UIBranchContainer.make(tofill, "itemContainer:"); boolean showRefresh = false; int textboxcount = 1; UIBranchContainer tableContainer = UIBranchContainer.make(container, "itemTable:"); // formatting: two columns: // 1: edit buttons, omitted for student // 2: main content // For links, which have status icons, the main content is a flush // left div with the icon // followed by a div with margin-left:30px. That takes it beyond the // icon, and avoids the // wrap-around appearance you'd get without the margin. // Normally the description is shown as a second div with // indentation in the CSS. // That puts it below the link. However with a link that's a button, // we do float left // for the button so the text wraps around it. I think that's // probably what people would expect. UIOutput.make(tableContainer, "colgroup"); if (canEditPage) { UIOutput.make(tableContainer, "col1"); } UIOutput.make(tableContainer, "col2"); // our accessiblity people say not to use TH for except for a data table // the table header is for accessibility tools only, so it's // positioned off screen //if (canEditPage) { // UIOutput.make(tableContainer, "header-edits"); // } // UIOutput.make(tableContainer, "header-items"); for (SimplePageItem i : itemList) { // listitem is mostly historical. it uses some shared HTML, but // if I were // doing it from scratch I wouldn't make this distinction. At // the moment it's // everything that isn't inline. boolean listItem = !(i.getType() == SimplePageItem.TEXT || i.getType() == SimplePageItem.MULTIMEDIA || i.getType() == SimplePageItem.COMMENTS || i.getType() == SimplePageItem.STUDENT_CONTENT || i.getType() == SimplePageItem.QUESTION || i.getType() == SimplePageItem.PEEREVAL); // (i.getType() == SimplePageItem.PAGE && // "button".equals(i.getFormat()))) if (!simplePageBean.isItemVisible(i, currentPage)) { continue; } anyItemVisible = true; UIBranchContainer tableRow = UIBranchContainer.make(tableContainer, "item:"); // set class name showing what the type is, so people can do funky CSS String itemClassName = null; switch (i.getType()) { case SimplePageItem.RESOURCE: itemClassName = "resourceType"; break; case SimplePageItem.PAGE: itemClassName = "pageType"; break; case SimplePageItem.ASSIGNMENT: itemClassName = "assignmentType"; break; case SimplePageItem.ASSESSMENT: itemClassName = "assessmentType"; break; case SimplePageItem.TEXT: itemClassName = "textType"; break; case SimplePageItem.URL: itemClassName = "urlType"; break; case SimplePageItem.MULTIMEDIA: itemClassName = "multimediaType"; break; case SimplePageItem.FORUM: itemClassName = "forumType"; break; case SimplePageItem.COMMENTS: itemClassName = "commentsType"; break; case SimplePageItem.STUDENT_CONTENT: itemClassName = "studentContentType"; break; case SimplePageItem.QUESTION: itemClassName = "question"; break; case SimplePageItem.BLTI: itemClassName = "bltiType"; break; case SimplePageItem.PEEREVAL: itemClassName = "peereval"; break; } if (listItem){ itemClassName = itemClassName + " listType"; } if (canEditPage) { itemClassName = itemClassName + " canEdit"; } tableRow.decorate(new UIFreeAttributeDecorator("class", itemClassName)); // you really need the HTML file open at the same time to make // sense of the following code if (listItem) { // Not an HTML Text, Element or Multimedia // Element if (canEditPage) { UIOutput.make(tableRow, "current-item-id2", String.valueOf(i.getId())); } // users can declare a page item to be navigational. If so // we display // it to the left of the normal list items, and use a // button. This is // used for pages that are "next" pages, i.e. they replace // this page // rather than creating a new level in the breadcrumbs. // Since they can't // be required, they don't need the status image, which is // good because // they're displayed with colspan=2, so there's no space for // the image. boolean navButton = "button".equals(i.getFormat()) && !i.isRequired(); boolean notDone = false; Status status = Status.NOT_REQUIRED; if (!navButton) { status = handleStatusImage(tableRow, i); if (status == Status.REQUIRED) { notDone = true; } } boolean isInline = (i.getType() == SimplePageItem.BLTI && "inline".equals(i.getFormat())); UIOutput linktd = UIOutput.make(tableRow, "item-td"); UIBranchContainer linkdiv = null; if (!isInline) { linkdiv = UIBranchContainer.make(tableRow, "link-div:"); UIOutput itemicon = UIOutput.make(linkdiv,"item-icon"); switch (i.getType()) { case SimplePageItem.FORUM: itemicon.decorate(new UIFreeAttributeDecorator("src", "/library/image/silk/comments.png")); break; case SimplePageItem.ASSIGNMENT: itemicon.decorate(new UIFreeAttributeDecorator("src", "/library/image/silk/page_edit.png")); break; case SimplePageItem.ASSESSMENT: itemicon.decorate(new UIFreeAttributeDecorator("src", "/library/image/silk/pencil.png")); break; case SimplePageItem.BLTI: itemicon.decorate(new UIFreeAttributeDecorator("src", "/library/image/silk/application_go.png")); break; case SimplePageItem.PAGE: itemicon.decorate(new UIFreeAttributeDecorator("src", "/library/image/silk/book_open.png")); break; case SimplePageItem.RESOURCE: String mimeType = i.getHtml(); if("application/octet-stream".equals(mimeType)) { // OS X reports octet stream for things like MS Excel documents. // Force a mimeType lookup so we get a decent icon. mimeType = null; } if (mimeType == null || mimeType.equals("")) { String s = i.getSakaiId(); int j = s.lastIndexOf("."); if (j >= 0) s = s.substring(j+1); mimeType = ContentTypeImageService.getContentType(s); // System.out.println("type " + s + ">" + mimeType); } String src = null; if (!useSakaiIcons) src = imageToMimeMap.get(mimeType); if (src == null) { String image = ContentTypeImageService.getContentTypeImage(mimeType); if (image != null) src = "/library/image/" + image; } if(src != null) { itemicon.decorate(new UIFreeAttributeDecorator("src", src)); } break; } } UIOutput descriptiondiv = null; // refresh isn't actually used anymore. We've changed the // way things are // done so the user never has to request a refresh. // FYI: this actually puts in an IFRAME for inline BLTI items showRefresh = !makeLink(tableRow, "link", i, canSeeAll, currentPage, notDone, status) || showRefresh; UILink.make(tableRow, "copylink", i.getName(), "http://lessonbuilder.sakaiproject.org/" + i.getId() + "/"). decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.copylink2").replace("{}", i.getName()))); // dummy is used when an assignment, quiz, or forum item is // copied // from another site. The way the copy code works, our // import code // doesn't have access to the necessary info to use the item // from the // new site. So we add a dummy, which generates an // explanation that the // author is going to have to choose the item from the // current site if (i.getSakaiId().equals(SimplePageItem.DUMMY)) { String code = null; switch (i.getType()) { case SimplePageItem.ASSIGNMENT: code = "simplepage.copied.assignment"; break; case SimplePageItem.ASSESSMENT: code = "simplepage.copied.assessment"; break; case SimplePageItem.FORUM: code = "simplepage.copied.forum"; break; } descriptiondiv = UIOutput.make(tableRow, "description", messageLocator.getMessage(code)); } else { descriptiondiv = UIOutput.make(tableRow, "description", i.getDescription()); } if (isInline) descriptiondiv.decorate(new UIFreeAttributeDecorator("style", "margin-top: 4px")); if (!isInline) { // nav button gets float left so any description goes to its // right. Otherwise the // description block will display underneath if ("button".equals(i.getFormat())) { linkdiv.decorate(new UIFreeAttributeDecorator("style", "float:none")); } // for accessibility if (navButton) { linkdiv.decorate(new UIFreeAttributeDecorator("role", "navigation")); } } // note that a lot of the info here is used by the // javascript that prepares // the jQuery dialogs if (canEditPage) { UIOutput.make(tableRow, "edit-td"); UILink.make(tableRow, "edit-link", messageLocator.getMessage("simplepage.editItem"), "").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.edit-title.generic").replace("{}", i.getName()))); // the following information is displayed using <INPUT // type=hidden ... // it contains information needed to populate the "edit" // popup dialog UIOutput.make(tableRow, "prerequisite-info", String.valueOf(i.isPrerequisite())); String itemGroupString = null; boolean entityDeleted = false; boolean notPublished = false; if (i.getType() == SimplePageItem.ASSIGNMENT) { // the type indicates whether scoring is letter // grade, number, etc. // the javascript needs this to present the right // choices to the user // types 6 and 8 aren't legal scoring types, so they // are used as // markers for quiz or forum. I ran out of numbers // and started using // text for things that aren't scoring types. That's // better anyway int type = 4; LessonEntity assignment = null; if (!i.getSakaiId().equals(SimplePageItem.DUMMY)) { assignment = assignmentEntity.getEntity(i.getSakaiId(), simplePageBean); if (assignment != null) { type = assignment.getTypeOfGrade(); String editUrl = assignment.editItemUrl(simplePageBean); if (editUrl != null) { UIOutput.make(tableRow, "edit-url", editUrl); } itemGroupString = simplePageBean.getItemGroupString(i, assignment, true); UIOutput.make(tableRow, "item-groups", itemGroupString); if (!assignment.objectExists()) entityDeleted = true; } } UIOutput.make(tableRow, "type", String.valueOf(type)); String requirement = String.valueOf(i.getSubrequirement()); if ((type == SimplePageItem.PAGE || type == SimplePageItem.ASSIGNMENT) && i.getSubrequirement()) { requirement = i.getRequirementText(); } UIOutput.make(tableRow, "requirement-text", requirement); } else if (i.getType() == SimplePageItem.ASSESSMENT) { UIOutput.make(tableRow, "type", "6"); // Not used by // assignments, // so it is // safe to dedicate to assessments UIOutput.make(tableRow, "requirement-text", (i.getSubrequirement() ? i.getRequirementText() : "false")); LessonEntity quiz = quizEntity.getEntity(i.getSakaiId(),simplePageBean); if (quiz != null) { String editUrl = quiz.editItemUrl(simplePageBean); if (editUrl != null) { UIOutput.make(tableRow, "edit-url", editUrl); } editUrl = quiz.editItemSettingsUrl(simplePageBean); if (editUrl != null) { UIOutput.make(tableRow, "edit-settings-url", editUrl); } itemGroupString = simplePageBean.getItemGroupString(i, quiz, true); UIOutput.make(tableRow, "item-groups", itemGroupString); if (!quiz.objectExists()) entityDeleted = true; } else notPublished = quizEntity.notPublished(i.getSakaiId()); } else if (i.getType() == SimplePageItem.BLTI) { UIOutput.make(tableRow, "type", "b"); LessonEntity blti= (bltiEntity == null ? null : bltiEntity.getEntity(i.getSakaiId())); if (blti != null) { String editUrl = blti.editItemUrl(simplePageBean); if (editUrl != null) UIOutput.make(tableRow, "edit-url", editUrl); UIOutput.make(tableRow, "item-format", i.getFormat()); if (i.getHeight() != null) UIOutput.make(tableRow, "item-height", i.getHeight()); itemGroupString = simplePageBean.getItemGroupString(i, null, true); UIOutput.make(tableRow, "item-groups", itemGroupString ); if (!blti.objectExists()) entityDeleted = true; } } else if (i.getType() == SimplePageItem.FORUM) { UIOutput.make(tableRow, "extra-info"); UIOutput.make(tableRow, "type", "8"); LessonEntity forum = forumEntity.getEntity(i.getSakaiId()); if (forum != null) { String editUrl = forum.editItemUrl(simplePageBean); if (editUrl != null) { UIOutput.make(tableRow, "edit-url", editUrl); } itemGroupString = simplePageBean.getItemGroupString(i, forum, true); UIOutput.make(tableRow, "item-groups", itemGroupString); if (!forum.objectExists()) entityDeleted = true; } } else if (i.getType() == SimplePageItem.PAGE) { UIOutput.make(tableRow, "type", "page"); UIOutput.make(tableRow, "page-next", Boolean.toString(i.getNextPage())); UIOutput.make(tableRow, "page-button", Boolean.toString("button".equals(i.getFormat()))); itemGroupString = simplePageBean.getItemGroupString(i, null, true); UIOutput.make(tableRow, "item-groups", itemGroupString); } else if (i.getType() == SimplePageItem.RESOURCE) { try { itemGroupString = simplePageBean.getItemGroupStringOrErr(i, null, true); } catch (IdUnusedException e) { itemGroupString = ""; entityDeleted = true; } if (simplePageBean.getInherited()) UIOutput.make(tableRow, "item-groups", "--inherited--"); else UIOutput.make(tableRow, "item-groups", itemGroupString ); UIOutput.make(tableRow, "item-samewindow", Boolean.toString(i.isSameWindow())); UIVerbatim.make(tableRow, "item-path", getItemPath(i)); } String releaseString = simplePageBean.getReleaseString(i); if (itemGroupString != null || releaseString != null || entityDeleted || notPublished) { if (itemGroupString != null) itemGroupString = simplePageBean.getItemGroupTitles(itemGroupString, i); if (itemGroupString != null) { itemGroupString = " [" + itemGroupString + "]"; if (releaseString != null) itemGroupString = " " + releaseString + itemGroupString; } else if (releaseString != null) itemGroupString = " " + releaseString; if (notPublished) { if (itemGroupString != null) itemGroupString = itemGroupString + " " + messageLocator.getMessage("simplepage.not-published"); else itemGroupString = messageLocator.getMessage("simplepage.not-published"); } if (entityDeleted) { if (itemGroupString != null) itemGroupString = itemGroupString + " " + messageLocator.getMessage("simplepage.deleted-entity"); else itemGroupString = messageLocator.getMessage("simplepage.deleted-entity"); } if (itemGroupString != null) UIOutput.make(tableRow, (isInline ? "item-group-titles-div" : "item-group-titles"), itemGroupString); } } // the following are for the inline item types. Multimedia // is the most complex because // it can be IMG, IFRAME, or OBJECT, and Youtube is treated // separately } else if (i.getType() == SimplePageItem.MULTIMEDIA) { // This code should be read together with the code in SimplePageBean // that sets up this data, method addMultimedia. Most display is set // up here, but note that show-page.js invokes the jquery oembed on all // <A> items with class="oembed". // historically this code was to display files ,and urls leading to things // like MP4. as backup if we couldn't figure out what to do we'd put something // in an iframe. The one exception is youtube, which we supposed explicitly. // However we now support several ways to embed content. We use the // multimediaDisplayType code to indicate which. The codes are // 1 -- embed code, 2 -- av type, 3 -- oembed, 4 -- iframe // 2 is the original code: MP4, image, and as a special case youtube urls // since we have old entries with no type code, and that behave the same as // 2, we start by converting 2 to null. // then the logic is // if type == null & youtube, do youtube // if type == null & image, do iamge // if type == null & not HTML do MP4 or other player for file // final fallthrough to handel the new types, with IFRAME if all else fails // the old code creates ojbects in ContentHosting for both files and URLs. // The new code saves the embed code or URL itself as an atteibute of the item // If I were doing it again, I wouldn't create the ContebtHosting item // Note that IFRAME is only used for something where the far end claims the MIME // type is HTML. For weird stuff like MS Word files I use the file display code, which // will end up producing <OBJECT>. // the reason this code is complex is that we try to choose // the best // HTML for displaying the particular type of object. We've // added complexities // over time as we get more experience with different // object types and browsers. String itemGroupString = null; String itemGroupTitles = null; boolean entityDeleted = false; // new format explicit display indication String mmDisplayType = i.getAttribute("multimediaDisplayType"); // 2 is the generic "use old display" so treat it as null if ("".equals(mmDisplayType) || "2".equals(mmDisplayType)) mmDisplayType = null; if (canSeeAll) { try { itemGroupString = simplePageBean.getItemGroupStringOrErr(i, null, true); } catch (IdUnusedException e) { itemGroupString = ""; entityDeleted = true; } itemGroupTitles = simplePageBean.getItemGroupTitles(itemGroupString, i); if (entityDeleted) { if (itemGroupTitles != null) itemGroupTitles = itemGroupTitles + " " + messageLocator.getMessage("simplepage.deleted-entity"); else itemGroupTitles = messageLocator.getMessage("simplepage.deleted-entity"); } if (itemGroupTitles != null) { itemGroupTitles = "[" + itemGroupTitles + "]"; } UIOutput.make(tableRow, "item-groups", itemGroupString); } else if (entityDeleted) continue; if (!"1".equals(mmDisplayType) && !"3".equals(mmDisplayType)) UIVerbatim.make(tableRow, "item-path", getItemPath(i)); // the reason this code is complex is that we try to choose // the best // HTML for displaying the particular type of object. We've // added complexities // over time as we get more experience with different // object types and browsers. StringTokenizer token = new StringTokenizer(i.getSakaiId(), "."); String extension = ""; while (token.hasMoreTokens()) { extension = token.nextToken().toLowerCase(); } // the extension is almost never used. Normally we have // the MIME type and use it. Extension is used only if // for some reason we don't have the MIME type UIComponent item; String youtubeKey; Length width = null; if (i.getWidth() != null) { width = new Length(i.getWidth()); } Length height = null; if (i.getHeight() != null) { height = new Length(i.getHeight()); } // Get the MIME type. For multimedia types is should be in // the html field. // The old code saved the URL there. So if it looks like a // URL ignore it. String mimeType = i.getHtml(); if (mimeType != null && (mimeType.startsWith("http") || mimeType.equals(""))) { mimeType = null; } // here goes. dispatch on the type and produce the right tag // type, // followed by the hidden INPUT tags with information for the // edit dialog if (mmDisplayType == null && simplePageBean.isImageType(i)) { if(canSeeAll || simplePageBean.isItemAvailable(i)) { UIOutput.make(tableRow, "imageSpan"); if (itemGroupString != null) { UIOutput.make(tableRow, "item-group-titles3", itemGroupTitles); UIOutput.make(tableRow, "item-groups3", itemGroupString); } String imageName = i.getAlt(); if (imageName == null || imageName.equals("")) { imageName = abbrevUrl(i.getURL()); } item = UIOutput.make(tableRow, "image").decorate(new UIFreeAttributeDecorator("src", i.getItemURL(simplePageBean.getCurrentSiteId(),currentPage.getOwner()))).decorate(new UIFreeAttributeDecorator("alt", imageName)); if (lengthOk(width)) { item.decorate(new UIFreeAttributeDecorator("width", width.getOld())); } if(lengthOk(height)) { item.decorate(new UIFreeAttributeDecorator("height", height.getOld())); } } else { UIComponent notAvailableText = UIOutput.make(tableRow, "notAvailableText", messageLocator.getMessage("simplepage.textItemUnavailable")); // Grey it out notAvailableText.decorate(new UIFreeAttributeDecorator("class", "disabled-text-item")); } // stuff for the jquery dialog if (canEditPage) { UIOutput.make(tableRow, "imageHeight", getOrig(height)); UIOutput.make(tableRow, "imageWidth", getOrig(width)); UIOutput.make(tableRow, "mimetype2", mimeType); UIOutput.make(tableRow, "current-item-id4", Long.toString(i.getId())); UIOutput.make(tableRow, "editmm-td"); UILink.make(tableRow, "iframe-edit", messageLocator.getMessage("simplepage.editItem"), "").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.edit-title.url").replace("{}", abbrevUrl(i.getURL())))); } UIOutput.make(tableRow, "description2", i.getDescription()); } else if (mmDisplayType == null && (youtubeKey = simplePageBean.getYoutubeKey(i)) != null) { String youtubeUrl = SimplePageBean.getYoutubeUrlFromKey(youtubeKey); if(canSeeAll || simplePageBean.isItemAvailable(i)) { UIOutput.make(tableRow, "youtubeSpan"); if (itemGroupString != null) { UIOutput.make(tableRow, "item-group-titles4", itemGroupTitles); UIOutput.make(tableRow, "item-groups4", itemGroupString); } // if width is blank or 100% scale the height if (width != null && height != null && !height.number.equals("")) { if (width.number.equals("") && width.unit.equals("") || width.number.equals("100") && width.unit.equals("%")) { int h = Integer.parseInt(height.number); if (h > 0) { width.number = Integer.toString((int) Math.round(h * 1.641025641)); width.unit = height.unit; } } } // <object style="height: 390px; width: 640px"><param // name="movie" // value="http://www.youtube.com/v/AKIC7OQqBrA?version=3"><param // name="allowFullScreen" value="true"><param // name="allowScriptAccess" value="always"><embed // src="http://www.youtube.com/v/AKIC7OQqBrA?version=3" // type="application/x-shockwave-flash" // allowfullscreen="true" allowScriptAccess="always" // width="640" height="390"></object> item = UIOutput.make(tableRow, "youtubeIFrame"); // youtube seems ok with length and width if(lengthOk(height)) { item.decorate(new UIFreeAttributeDecorator("height", height.getOld())); } if(lengthOk(width)) { item.decorate(new UIFreeAttributeDecorator("width", width.getOld())); } item.decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.youtube_player"))); item.decorate(new UIFreeAttributeDecorator("src", youtubeUrl)); } else { UIComponent notAvailableText = UIOutput.make(tableRow, "notAvailableText", messageLocator.getMessage("simplepage.textItemUnavailable")); // Grey it out notAvailableText.decorate(new UIFreeAttributeDecorator("class", "disabled-text-item")); } if (canEditPage) { UIOutput.make(tableRow, "youtubeId", String.valueOf(i.getId())); UIOutput.make(tableRow, "currentYoutubeURL", youtubeUrl); UIOutput.make(tableRow, "currentYoutubeHeight", getOrig(height)); UIOutput.make(tableRow, "currentYoutubeWidth", getOrig(width)); UIOutput.make(tableRow, "current-item-id5", Long.toString(i.getId())); UIOutput.make(tableRow, "youtube-td"); UILink.make(tableRow, "youtube-edit", messageLocator.getMessage("simplepage.editItem"), "").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.edit-title.youtube"))); } UIOutput.make(tableRow, "description4", i.getDescription()); // as of Oct 28, 2010, we store the mime type. mimeType // null is an old entry. // For that use the old approach of checking the // extension. // Otherwise we want to use iframes for HTML and OBJECT // for everything else // We need the iframes because IE up through 8 doesn't // reliably display // HTML with OBJECT. Experiments show that everything // else works with OBJECT // for most browsers. Unfortunately IE, even IE 9, // doesn't reliably call the // right player with OBJECT. EMBED works. But it's not // as nice because you can't // nest error recovery code. So we use OBJECT for // everything except IE, where we // use EMBED. OBJECT does work with Flash. // application/xhtml+xml is XHTML. } else if (mmDisplayType == null && ((mimeType != null && !mimeType.equals("text/html") && !mimeType.equals("application/xhtml+xml")) || // ((mimeType != null && (mimeType.startsWith("audio/") || mimeType.startsWith("video/"))) || (mimeType == null && !(Arrays.binarySearch(htmlTypes, extension) >= 0)))) { // except where explicit display is set, // this code is used for everything that isn't an image, // Youtube, or HTML // This could be audio, video, flash, or something random like MS word. // Random stuff will turn into an object. // HTML is done with an IFRAME in the next "if" case // The explicit display types are handled there as well // in theory the things that fall through to iframe are // html and random stuff without a defined mime type // random stuff with mime type is displayed with object if (mimeType == null) { mimeType = ""; } String oMimeType = mimeType; // in case we change it for // FLV or others if (itemGroupString != null) { UIOutput.make(tableRow, "item-group-titles5", itemGroupTitles); UIOutput.make(tableRow, "item-groups5", itemGroupString); } UIOutput.make(tableRow, "movieSpan"); if(canSeeAll || simplePageBean.isItemAvailable(i)) { UIComponent item2; String movieUrl = i.getItemURL(simplePageBean.getCurrentSiteId(),currentPage.getOwner()); // movieUrl = "https://heidelberg.rutgers.edu" + movieUrl; // Safari doens't always pass cookies to plugins, so we have to pass the arg // this requires session.parameter.allow=true in sakai.properties // don't pass the arg unless that is set, since the whole point of defaulting // off is to not expose the session id String sessionParameter = getSessionParameter(movieUrl); if (sessionParameter != null) movieUrl = movieUrl + "?lb.session=" + sessionParameter; UIOutput.make(tableRow, "movie-link-div"); UILink.make(tableRow, "movie-link-link", messageLocator.getMessage("simplepage.download_file"), movieUrl); // if (allowSessionId) // movieUrl = movieUrl + "?sakai.session=" + SessionManager.getCurrentSession().getId(); boolean useFlvPlayer = false; // isMp4 means we try the flash player (if not HTML5) // we also try the flash player for FLV but for mp4 we do an // additional backup if flash fails, but that doesn't make sense for FLV boolean isMp4 = Arrays.binarySearch(mp4Types, mimeType) >= 0; boolean isHtml5 = Arrays.binarySearch(html5Types, mimeType) >= 0; // wrap whatever stuff we decide to put out in HTML5 if appropriate // javascript is used to do the wrapping, because RSF can't really handle this if (isHtml5) { boolean isAudio = mimeType.startsWith("audio/"); UIComponent h5video = UIOutput.make(tableRow, (isAudio? "h5audio" : "h5video")); UIComponent h5source = UIOutput.make(tableRow, (isAudio? "h5asource" : "h5source")); if (lengthOk(height) && height.getOld().indexOf("%") < 0) h5video.decorate(new UIFreeAttributeDecorator("height", height.getOld())); if (lengthOk(width) && width.getOld().indexOf("%") < 0) h5video.decorate(new UIFreeAttributeDecorator("width", width.getOld())); h5source.decorate(new UIFreeAttributeDecorator("src", movieUrl)). decorate(new UIFreeAttributeDecorator("type", mimeType)); } // FLV is special. There's no player for flash video in // the browser // it shows with a special flash program, which I // supply. For the moment MP4 is // shown with the same player so it uses much of the // same code if (mimeType != null && (mimeType.equals("video/x-flv") || mimeType.equals("video/flv") || isMp4)) { mimeType = "application/x-shockwave-flash"; movieUrl = "/lessonbuilder-tool/templates/StrobeMediaPlayback.swf"; useFlvPlayer = true; } // for IE, if we're not supplying a player it's safest // to use embed // otherwise Quicktime won't work. Oddly, with IE 9 only // it works if you set CLASSID to the MIME type, // but that's so unexpected that I hate to rely on it. // EMBED is in HTML 5, so I think we're OK // using it permanently for IE. // I prefer OBJECT where possible because of the nesting // ability. boolean useEmbed = ieVersion > 0 && !mimeType.equals("application/x-shockwave-flash"); if (useEmbed) { item2 = UIOutput.make(tableRow, "movieEmbed").decorate(new UIFreeAttributeDecorator("src", movieUrl)).decorate(new UIFreeAttributeDecorator("alt", messageLocator.getMessage("simplepage.mm_player").replace("{}", abbrevUrl(i.getURL())))); } else { item2 = UIOutput.make(tableRow, "movieObject").decorate(new UIFreeAttributeDecorator("data", movieUrl)).decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.mm_player").replace("{}", abbrevUrl(i.getURL())))); } if (mimeType != null) { item2.decorate(new UIFreeAttributeDecorator("type", mimeType)); } if (canEditPage) { //item2.decorate(new UIFreeAttributeDecorator("style", "border: 1px solid black")); } // some object types seem to need a specification, so supply our default if necessary if (lengthOk(height) && lengthOk(width)) { item2.decorate(new UIFreeAttributeDecorator("height", height.getOld())).decorate(new UIFreeAttributeDecorator("width", width.getOld())); } else { if (oMimeType.startsWith("audio/")) item2.decorate(new UIFreeAttributeDecorator("height", "100")).decorate(new UIFreeAttributeDecorator("width", "400")); else item2.decorate(new UIFreeAttributeDecorator("height", "300")).decorate(new UIFreeAttributeDecorator("width", "400")); } if (!useEmbed) { if (useFlvPlayer) { UIOutput.make(tableRow, "flashvars").decorate(new UIFreeAttributeDecorator("value", "src=" + URLEncoder.encode(myUrl() + i.getItemURL(simplePageBean.getCurrentSiteId(),currentPage.getOwner())))); // need wmode=opaque for player to stack properly with dialogs, etc. // there is a performance impact, but I'm guessing in our application we don't // need ultimate performance for embedded video. I'm setting it only for // the player, so flash games and other applications will still get wmode=window UIOutput.make(tableRow, "wmode"); } else if (mimeType.equals("application/x-shockwave-flash")) UIOutput.make(tableRow, "wmode"); UIOutput.make(tableRow, "movieURLInject").decorate(new UIFreeAttributeDecorator("value", movieUrl)); if (!isMp4) { UIOutput.make(tableRow, "noplugin-p", messageLocator.getMessage("simplepage.noplugin")); UIOutput.make(tableRow, "noplugin-br"); UILink.make(tableRow, "noplugin", i.getName(), movieUrl); } } if (isMp4) { // do fallback. for ie use EMBED if (ieVersion > 0) { item2 = UIOutput.make(tableRow, "mp4-embed").decorate(new UIFreeAttributeDecorator("src", i.getItemURL(simplePageBean.getCurrentSiteId(),currentPage.getOwner()))).decorate(new UIFreeAttributeDecorator("alt", messageLocator.getMessage("simplepage.mm_player").replace("{}", abbrevUrl(i.getURL())))); } else { item2 = UIOutput.make(tableRow, "mp4-object").decorate(new UIFreeAttributeDecorator("data", i.getItemURL(simplePageBean.getCurrentSiteId(),currentPage.getOwner()))).decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.mm_player").replace("{}", abbrevUrl(i.getURL())))); } if (oMimeType != null) { item2.decorate(new UIFreeAttributeDecorator("type", oMimeType)); } // some object types seem to need a specification, so give a default if needed if (lengthOk(height) && lengthOk(width)) { item2.decorate(new UIFreeAttributeDecorator("height", height.getOld())).decorate(new UIFreeAttributeDecorator("width", width.getOld())); } else { if (oMimeType.startsWith("audio/")) item2.decorate(new UIFreeAttributeDecorator("height", "100")).decorate(new UIFreeAttributeDecorator("width", "100%")); else item2.decorate(new UIFreeAttributeDecorator("height", "300")).decorate(new UIFreeAttributeDecorator("width", "100%")); } if (!useEmbed) { UIOutput.make(tableRow, "mp4-inject").decorate(new UIFreeAttributeDecorator("value", i.getItemURL(simplePageBean.getCurrentSiteId(),currentPage.getOwner()))); UIOutput.make(tableRow, "mp4-noplugin-p", messageLocator.getMessage("simplepage.noplugin")); UILink.make(tableRow, "mp4-noplugin", i.getName(), i.getItemURL(simplePageBean.getCurrentSiteId(),currentPage.getOwner())); } } } else { UIVerbatim notAvailableText = UIVerbatim.make(tableRow, "notAvailableText", messageLocator.getMessage("simplepage.multimediaItemUnavailable")); // Grey it out notAvailableText.decorate(new UIFreeAttributeDecorator("class", "disabled-multimedia-item")); } if (canEditPage) { UIOutput.make(tableRow, "movieId", String.valueOf(i.getId())); UIOutput.make(tableRow, "movieHeight", getOrig(height)); UIOutput.make(tableRow, "movieWidth", getOrig(width)); UIOutput.make(tableRow, "mimetype5", oMimeType); UIOutput.make(tableRow, "prerequisite", (i.isPrerequisite()) ? "true" : "false"); UIOutput.make(tableRow, "current-item-id6", Long.toString(i.getId())); UIOutput.make(tableRow, "movie-td"); UILink.make(tableRow, "edit-movie", messageLocator.getMessage("simplepage.editItem"), "").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.edit-title.url").replace("{}", abbrevUrl(i.getURL())))); } UIOutput.make(tableRow, "description3", i.getDescription()); } else { // this is fallthrough for html or an explicit mm display type (i.e. embed code) // odd types such as MS word will be handled by the AV code, and presented as <OBJECT> // definition of resizeiframe, at top of page if (!iframeJavascriptDone && getOrig(height).equals("auto")) { UIOutput.make(tofill, "iframeJavascript"); iframeJavascriptDone = true; } UIOutput.make(tableRow, "iframeSpan"); if (itemGroupString != null) { UIOutput.make(tableRow, "item-group-titles2", itemGroupTitles); UIOutput.make(tableRow, "item-groups2", itemGroupString); } String itemUrl = i.getItemURL(simplePageBean.getCurrentSiteId(),currentPage.getOwner()); if ("1".equals(mmDisplayType)) { // embed item = UIVerbatim.make(tableRow, "mm-embed", i.getAttribute("multimediaEmbedCode")); //String style = getStyle(width, height); //if (style != null) //item.decorate(new UIFreeAttributeDecorator("style", style)); } else if ("3".equals(mmDisplayType)) { item = UILink.make(tableRow, "mm-oembed", i.getAttribute("multimediaUrl"), i.getAttribute("multimediaUrl")); if (lengthOk(width)) item.decorate(new UIFreeAttributeDecorator("maxWidth", width.getOld())); if (lengthOk(height)) item.decorate(new UIFreeAttributeDecorator("maxHeight", height.getOld())); // oembed } else { UIOutput.make(tableRow, "iframe-link-div"); UILink.make(tableRow, "iframe-link-link", messageLocator.getMessage("simplepage.open_new_window"), itemUrl); item = UIOutput.make(tableRow, "iframe").decorate(new UIFreeAttributeDecorator("src", itemUrl)); // if user specifies auto, use Javascript to resize the // iframe when the // content changes. This only works for URLs with the // same origin, i.e. // URLs in this sakai system if (getOrig(height).equals("auto")) { item.decorate(new UIFreeAttributeDecorator("onload", "resizeiframe('" + item.getFullID() + "')")); if (lengthOk(width)) { item.decorate(new UIFreeAttributeDecorator("width", width.getOld())); } item.decorate(new UIFreeAttributeDecorator("height", "300")); } else { // we seem OK without a spec if (lengthOk(height) && lengthOk(width)) { item.decorate(new UIFreeAttributeDecorator("height", height.getOld())).decorate(new UIFreeAttributeDecorator("width", width.getOld())); } } } item.decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.web_content").replace("{}", abbrevUrl(i.getURL())))); if (canEditPage) { UIOutput.make(tableRow, "iframeHeight", getOrig(height)); UIOutput.make(tableRow, "iframeWidth", getOrig(width)); UIOutput.make(tableRow, "mimetype3", mimeType); UIOutput.make(tableRow, "current-item-id3", Long.toString(i.getId())); UIOutput.make(tableRow, "editmm-td"); UILink.make(tableRow, "iframe-edit", messageLocator.getMessage("simplepage.editItem"), "").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.edit-title.url").replace("{}", abbrevUrl(i.getURL())))); } UIOutput.make(tableRow, "description5", i.getDescription()); } // end of multimedia object } else if (i.getType() == SimplePageItem.COMMENTS) { // Load later using AJAX and CommentsProducer UIOutput.make(tableRow, "commentsSpan"); boolean isAvailable = simplePageBean.isItemAvailable(i); // faculty missing preqs get warning but still see the comments if (!isAvailable && canSeeAll) UIOutput.make(tableRow, "missing-prereqs", messageLocator.getMessage("simplepage.fake-missing-prereqs")); // students get warning and not the content if (!isAvailable && !canSeeAll) { UIOutput.make(tableRow, "missing-prereqs", messageLocator.getMessage("simplepage.missing-prereqs")); }else { UIOutput.make(tableRow, "commentsDiv"); Placement placement = toolManager.getCurrentPlacement(); UIOutput.make(tableRow, "placementId", placement.getId()); // note: the URL will be rewritten in comments.js to look like // /lessonbuilder-tool/faces/Comments... CommentsViewParameters eParams = new CommentsViewParameters(CommentsProducer.VIEW_ID); eParams.itemId = i.getId(); eParams.placementId = placement.getId(); if (params.postedComment) { eParams.postedComment = postedCommentId; } eParams.siteId = simplePageBean.getCurrentSiteId(); eParams.pageId = currentPage.getPageId(); if(params.author != null && !params.author.equals("")) { eParams.author = params.author; eParams.showAllComments = true; } UIInternalLink.make(tableRow, "commentsLink", eParams); if (!addedCommentsScript) { UIOutput.make(tofill, "comments-script"); UIOutput.make(tofill, "fckScript"); addedCommentsScript = true; UIOutput.make(tofill, "delete-dialog"); } // forced comments have to be edited on the main page if (canEditPage) { // Checks to make sure that the comments item isn't on a student page. // That it is graded. And that we didn't just come from the grading pane. if(i.getPageId() > 0 && i.getGradebookId() != null && !cameFromGradingPane) { CommentsGradingPaneViewParameters gp = new CommentsGradingPaneViewParameters(CommentGradingPaneProducer.VIEW_ID); gp.placementId = toolManager.getCurrentPlacement().getId(); gp.commentsItemId = i.getId(); gp.pageId = currentPage.getPageId(); gp.pageItemId = pageItem.getId(); gp.siteId = simplePageBean.getCurrentSiteId(); UIInternalLink.make(tableRow, "gradingPaneLink", messageLocator.getMessage("simplepage.show-grading-pane-comments"), gp) .decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.show-grading-pane-comments"))); } UIOutput.make(tableRow, "comments-td"); if (i.getSequence() > 0) { UILink.make(tableRow, "edit-comments", messageLocator.getMessage("simplepage.editItem"), "") .decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.edit-title.comments"))); UIOutput.make(tableRow, "commentsId", String.valueOf(i.getId())); UIOutput.make(tableRow, "commentsAnon", String.valueOf(i.isAnonymous())); UIOutput.make(tableRow, "commentsitem-required", String.valueOf(i.isRequired())); UIOutput.make(tableRow, "commentsitem-prerequisite", String.valueOf(i.isPrerequisite())); UIOutput.make(tableRow, "commentsGrade", String.valueOf(i.getGradebookId() != null)); UIOutput.make(tableRow, "commentsMaxPoints", String.valueOf(i.getGradebookPoints())); String itemGroupString = simplePageBean.getItemGroupString(i, null, true); if (itemGroupString != null) { String itemGroupTitles = simplePageBean.getItemGroupTitles(itemGroupString, i); if (itemGroupTitles != null) { itemGroupTitles = "[" + itemGroupTitles + "]"; } UIOutput.make(tableRow, "comments-groups", itemGroupString); UIOutput.make(tableRow, "item-group-titles6", itemGroupTitles); } } // Allows AJAX posting of comment grades printGradingForm(tofill); } UIForm form = UIForm.make(tableRow, "comment-form"); UIInput.make(form, "comment-item-id", "#{simplePageBean.itemId}", String.valueOf(i.getId())); UIInput.make(form, "comment-edit-id", "#{simplePageBean.editId}"); // usage * image is required and not done if (i.isRequired() && !simplePageBean.isItemComplete(i)) UIOutput.make(tableRow, "comment-required-image"); UIOutput.make(tableRow, "add-comment-link"); UIOutput.make(tableRow, "add-comment-text", messageLocator.getMessage("simplepage.add-comment")); UIInput fckInput = UIInput.make(form, "comment-text-area-evolved:", "#{simplePageBean.formattedComment}"); fckInput.decorate(new UIFreeAttributeDecorator("height", "175")); fckInput.decorate(new UIFreeAttributeDecorator("width", "800")); fckInput.decorate(new UIStyleDecorator("evolved-box")); fckInput.decorate(new UIFreeAttributeDecorator("aria-label", messageLocator.getMessage("simplepage.editor"))); fckInput.decorate(new UIFreeAttributeDecorator("role", "dialog")); if (!noEditor) { fckInput.decorate(new UIStyleDecorator("using-editor")); // javascript needs to know ((SakaiFCKTextEvolver) richTextEvolver).evolveTextInput(fckInput, "" + commentsCount); } UICommand.make(form, "add-comment", "#{simplePageBean.addComment}"); } }else if (i.getType() == SimplePageItem.PEEREVAL){ String owner=currentPage.getOwner(); String currentUser=UserDirectoryService.getCurrentUser().getId(); Long pageId=currentPage.getPageId(); UIOutput.make(tableRow, "peerReviewRubricStudent"); UIOutput.make(tableRow, "peer-review-form"); makePeerRubric(tableRow,i, makeStudentRubric); boolean isOpen = false; boolean isPastDue = false; String peerEvalDateOpenStr = i.getAttribute("rubricOpenDate"); String peerEvalDateDueStr = i.getAttribute("rubricDueDate"); boolean peerEvalAllowSelfGrade = Boolean.parseBoolean(i.getAttribute("rubricAllowSelfGrade")); boolean gradingSelf = owner.equals(currentUser) && peerEvalAllowSelfGrade; if (peerEvalDateOpenStr != null && peerEvalDateDueStr != null) { Date peerEvalNow = new Date(); Date peerEvalOpen = new Date(Long.valueOf(peerEvalDateOpenStr)); Date peerEvalDue = new Date(Long.valueOf(peerEvalDateDueStr)); isOpen = peerEvalNow.after(peerEvalOpen); isPastDue = peerEvalNow.after(peerEvalDue); } if(isOpen){ if(owner.equals(currentUser)){ //owner gets their own data class PeerEvaluation{ String category; public int grade, count; public PeerEvaluation(String category, int grade){this.category=category;this.grade=grade;count=1;} public void increment(){count++;} public boolean equals(Object o){ if ( !(o instanceof PeerEvaluation) ) return false; PeerEvaluation pe = (PeerEvaluation)o; return category.equals(pe.category) && grade==pe.grade; } public String toString(){return category + " " + grade + " [" + count + "]";} } ArrayList<PeerEvaluation> myEvaluations = new ArrayList<PeerEvaluation>(); List<SimplePagePeerEvalResult> evaluations = simplePageToolDao.findPeerEvalResultByOwner(pageId.longValue(), owner); if(evaluations!=null) for(SimplePagePeerEvalResult eval : evaluations){ PeerEvaluation target=new PeerEvaluation(eval.getRowText(), eval.getColumnValue()); int targetIndex=myEvaluations.indexOf(target); if(targetIndex!=-1){ myEvaluations.get(targetIndex).increment(); } else myEvaluations.add(target); } UIOutput.make(tableRow, "my-existing-peer-eval-data"); for(PeerEvaluation eval: myEvaluations){ UIBranchContainer evalData = UIBranchContainer.make(tableRow, "my-peer-eval-data:"); UIOutput.make(evalData, "peer-eval-row-text", eval.category); UIOutput.make(evalData, "peer-eval-grade", String.valueOf(eval.grade)); UIOutput.make(evalData, "peer-eval-count", String.valueOf(eval.count)); } } if(!owner.equals(currentUser) || gradingSelf){ List<SimplePagePeerEvalResult> evaluations = simplePageToolDao.findPeerEvalResult(pageId, currentUser, owner); //existing evaluation data if(evaluations!=null && evaluations.size()!=0){ UIOutput.make(tableRow, "existing-peer-eval-data"); for(SimplePagePeerEvalResult eval : evaluations){ UIBranchContainer evalData = UIBranchContainer.make(tableRow, "peer-eval-data:"); UIOutput.make(evalData, "peer-eval-row-text", eval.getRowText()); UIOutput.make(evalData, "peer-eval-grade", String.valueOf(eval.getColumnValue())); } } //form for peer evaluation results UIForm form = UIForm.make(tofill, "rubricSelection"); UIInput.make(form, "rubricPeerGrade", "#{simplePageBean.rubricPeerGrade}"); UICommand.make(form, "update-peer-eval-grade", messageLocator.getMessage("simplepage.edit"), "#{simplePageBean.savePeerEvalResult}"); } //buttons UIOutput.make(tableRow, "add-peereval-link"); UIOutput.make(tableRow, "add-peereval-text", messageLocator.getMessage("simplepage.view-peereval")); if(isPastDue){ UIOutput.make(tableRow, "peer-eval-grade-directions", messageLocator.getMessage("simplepage.peer-eval.past-due-date")); }else if(!owner.equals(currentUser) || gradingSelf){ UIOutput.make(tableRow, "save-peereval-link"); UIOutput.make(tableRow, "save-peereval-text", messageLocator.getMessage("simplepage.save")); UIOutput.make(tableRow, "cancel-peereval-link"); UIOutput.make(tableRow, "cancel-peereval-text", messageLocator.getMessage("simplepage.cancel")); UIOutput.make(tableRow, "peer-eval-grade-directions", messageLocator.getMessage("simplepage.peer-eval.click-on-cell")); }else{ //owner who cannot grade himself UIOutput.make(tableRow, "peer-eval-grade-directions", messageLocator.getMessage("simplepage.peer-eval.cant-eval-yourself")); } if(canEditPage) UIOutput.make(tableRow, "peerReviewRubricStudent-edit");//lines up rubric with edit btn column for users with editing privs } }else if(i.getType() == SimplePageItem.STUDENT_CONTENT) { UIOutput.make(tableRow, "studentSpan"); boolean isAvailable = simplePageBean.isItemAvailable(i); // faculty missing preqs get warning but still see the comments if (!isAvailable && canSeeAll) UIOutput.make(tableRow, "student-missing-prereqs", messageLocator.getMessage("simplepage.student-fake-missing-prereqs")); if (!isAvailable && !canSeeAll) UIOutput.make(tableRow, "student-missing-prereqs", messageLocator.getMessage("simplepage.student-missing-prereqs")); else { UIOutput.make(tableRow, "studentDiv"); HashMap<Long, SimplePageLogEntry> cache = simplePageBean.cacheStudentPageLogEntries(i.getId()); List<SimpleStudentPage> studentPages = simplePageToolDao.findStudentPages(i.getId()); boolean hasOwnPage = false; String userId = UserDirectoryService.getCurrentUser().getId(); Collections.sort(studentPages, new Comparator<SimpleStudentPage>() { public int compare(SimpleStudentPage o1, SimpleStudentPage o2) { String title1 = o1.getTitle(); if (title1 == null) title1 = ""; String title2 = o2.getTitle(); if (title2 == null) title2 = ""; return title1.compareTo(title2); } }); UIOutput contentList = UIOutput.make(tableRow, "studentContentTable"); UIOutput contentTitle = UIOutput.make(tableRow, "studentContentTitle", messageLocator.getMessage("simplepage.student")); contentList.decorate(new UIFreeAttributeDecorator("aria-labelledby", contentTitle.getFullID())); // Print each row in the table for(SimpleStudentPage page : studentPages) { if(page.isDeleted()) continue; SimplePageLogEntry entry = cache.get(page.getPageId()); UIBranchContainer row = UIBranchContainer.make(tableRow, "studentRow:"); // There's content they haven't seen if(entry == null || entry.getLastViewed().compareTo(page.getLastUpdated()) < 0) { UIOutput.make(row, "newContentImg").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.new-student-content"))); } else UIOutput.make(row, "newContentImgT"); // The comments tool exists, so we might have to show the icon if(i.getShowComments() != null && i.getShowComments()) { // New comments have been added since they last viewed the page if(page.getLastCommentChange() != null && (entry == null || entry.getLastViewed().compareTo(page.getLastCommentChange()) < 0)) { UIOutput.make(row, "newCommentsImg").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.new-student-comments"))); } else UIOutput.make(row, "newCommentsImgT"); } // Never visited page if(entry == null) { UIOutput.make(row, "newPageImg").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.new-student-page"))); } else UIOutput.make(row, "newPageImgT"); GeneralViewParameters eParams = new GeneralViewParameters(ShowPageProducer.VIEW_ID, page.getPageId()); eParams.setItemId(i.getId()); eParams.setPath("push"); String studentTitle = page.getTitle(); String sownerName = null; try { if(!i.isAnonymous() || canEditPage) { if (page.getGroup() != null) sownerName = simplePageBean.getCurrentSite().getGroup(page.getGroup()).getTitle(); else sownerName = UserDirectoryService.getUser(page.getOwner()).getDisplayName(); if (sownerName != null && sownerName.equals(studentTitle)) studentTitle = "(" + sownerName + ")"; else studentTitle += " (" + sownerName + ")"; }else if (simplePageBean.isPageOwner(page)) { studentTitle += " (" + messageLocator.getMessage("simplepage.comment-you") + ")"; } } catch (UserNotDefinedException e) { } UIInternalLink.make(row, "studentLink", studentTitle, eParams); if(simplePageBean.isPageOwner(page)) { hasOwnPage = true; } if(i.getGradebookId() != null && simplePageBean.getEditPrivs() == 0) { UIOutput.make(row, "studentGradingCell", String.valueOf((page.getPoints() != null? page.getPoints() : ""))); } } if(!hasOwnPage) { UIBranchContainer row = UIBranchContainer.make(tableRow, "studentRow:"); UIOutput.make(row, "linkRow"); UIOutput.make(row, "linkCell"); if (i.isRequired() && !simplePageBean.isItemComplete(i)) UIOutput.make(row, "student-required-image"); GeneralViewParameters eParams = new GeneralViewParameters(ShowPageProducer.VIEW_ID); eParams.addTool = GeneralViewParameters.STUDENT_PAGE; eParams.studentItemId = i.getId(); UIInternalLink.make(row, "linkLink", messageLocator.getMessage("simplepage.add-page"), eParams); } if(canEditPage) { // Checks to make sure that the comments are graded and that we didn't // just come from a grading pane (would be confusing) if(i.getAltGradebook() != null && !cameFromGradingPane) { CommentsGradingPaneViewParameters gp = new CommentsGradingPaneViewParameters(CommentGradingPaneProducer.VIEW_ID); gp.placementId = toolManager.getCurrentPlacement().getId(); gp.commentsItemId = i.getId(); gp.pageId = currentPage.getPageId(); gp.pageItemId = pageItem.getId(); gp.studentContentItem = true; UIInternalLink.make(tableRow, "studentGradingPaneLink", messageLocator.getMessage("simplepage.show-grading-pane-content"), gp) .decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.show-grading-pane-content"))); } UIOutput.make(tableRow, "student-td"); UILink.make(tableRow, "edit-student", messageLocator.getMessage("simplepage.editItem"), "") .decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.edit-title.student"))); UIOutput.make(tableRow, "studentId", String.valueOf(i.getId())); UIOutput.make(tableRow, "studentAnon", String.valueOf(i.isAnonymous())); UIOutput.make(tableRow, "studentComments", String.valueOf(i.getShowComments())); UIOutput.make(tableRow, "forcedAnon", String.valueOf(i.getForcedCommentsAnonymous())); UIOutput.make(tableRow, "studentGrade", String.valueOf(i.getGradebookId() != null)); UIOutput.make(tableRow, "studentMaxPoints", String.valueOf(i.getGradebookPoints())); UIOutput.make(tableRow, "studentGrade2", String.valueOf(i.getAltGradebook() != null)); UIOutput.make(tableRow, "studentMaxPoints2", String.valueOf(i.getAltPoints())); UIOutput.make(tableRow, "studentitem-required", String.valueOf(i.isRequired())); UIOutput.make(tableRow, "studentitem-prerequisite", String.valueOf(i.isPrerequisite())); UIOutput.make(tableRow, "peer-eval", String.valueOf(i.getShowPeerEval())); makePeerRubric(tableRow,i, makeMaintainRubric); makeSamplePeerEval(tableRow); String peerEvalDate = i.getAttribute("rubricOpenDate"); String peerDueDate = i.getAttribute("rubricDueDate"); SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy"); SimpleDateFormat stf = new SimpleDateFormat("hh:mm a"); Calendar peerevalcal = Calendar.getInstance(); if (peerEvalDate != null && peerDueDate != null) { DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, M_locale); //Open date from attribute string peerevalcal.setTimeInMillis(Long.valueOf(peerEvalDate)); String dateStr = sdf.format(peerevalcal.getTime()); String timeStr = stf.format(peerevalcal.getTime()); UIOutput.make(tableRow, "peer-eval-open-date", dateStr); UIOutput.make(tableRow, "peer-eval-open-time", timeStr); //Due date from attribute string peerevalcal.setTimeInMillis(Long.valueOf(peerDueDate)); dateStr = sdf.format(peerevalcal.getTime()); timeStr = stf.format(peerevalcal.getTime()); UIOutput.make(tableRow, "peer-eval-due-date", dateStr); UIOutput.make(tableRow, "peer-eval-due-time", timeStr); UIOutput.make(tableRow, "peer-eval-allow-self", i.getAttribute("rubricAllowSelfGrade")); }else{ //Default open and due date Date now = new Date(); peerevalcal.setTime(now); //Default open date: now String dateStr = sdf.format(peerevalcal.getTime()); String timeStr = stf.format(peerevalcal.getTime()); UIOutput.make(tableRow, "peer-eval-open-date", dateStr); UIOutput.make(tableRow, "peer-eval-open-time", timeStr); //Default due date: 7 days from now Date later = new Date(peerevalcal.getTimeInMillis() + 604800000); peerevalcal.setTime(later); dateStr = sdf.format(peerevalcal.getTime()); timeStr = stf.format(peerevalcal.getTime()); //System.out.println("Setting date to " + dateStr + " and time to " + timeStr); UIOutput.make(tableRow, "peer-eval-due-date", dateStr); UIOutput.make(tableRow, "peer-eval-due-time", timeStr); UIOutput.make(tableRow, "peer-eval-allow-self", i.getAttribute("rubricAllowSelfGrade")); } //Peer Eval Stats link GeneralViewParameters view = new GeneralViewParameters(PeerEvalStatsProducer.VIEW_ID); view.setSendingPage(currentPage.getPageId()); view.setItemId(i.getId()); if(i.getShowPeerEval()){ UILink link = UIInternalLink.make(tableRow, "peer-eval-stats-link", view); } String itemGroupString = simplePageBean.getItemGroupString(i, null, true); if (itemGroupString != null) { String itemGroupTitles = simplePageBean.getItemGroupTitles(itemGroupString, i); if (itemGroupTitles != null) { itemGroupTitles = "[" + itemGroupTitles + "]"; } UIOutput.make(tableRow, "student-groups", itemGroupString); UIOutput.make(tableRow, "item-group-titles7", itemGroupTitles); } UIOutput.make(tableRow, "student-owner-groups", simplePageBean.getItemOwnerGroupString(i)); UIOutput.make(tableRow, "student-group-owned", (i.isGroupOwned()?"true":"false")); } } }else if(i.getType() == SimplePageItem.QUESTION) { SimplePageQuestionResponse response = simplePageToolDao.findQuestionResponse(i.getId(), simplePageBean.getCurrentUserId()); UIOutput.make(tableRow, "questionSpan"); boolean isAvailable = simplePageBean.isItemAvailable(i) || canSeeAll; UIOutput.make(tableRow, "questionDiv"); UIOutput.make(tableRow, "questionText", i.getAttribute("questionText")); List<SimplePageQuestionAnswer> answers = new ArrayList<SimplePageQuestionAnswer>(); if("multipleChoice".equals(i.getAttribute("questionType"))) { answers = simplePageToolDao.findAnswerChoices(i); UIOutput.make(tableRow, "multipleChoiceDiv"); UIForm questionForm = UIForm.make(tableRow, "multipleChoiceForm"); UIInput.make(questionForm, "multipleChoiceId", "#{simplePageBean.questionId}", String.valueOf(i.getId())); String[] options = new String[answers.size()]; String initValue = null; for(int j = 0; j < answers.size(); j++) { options[j] = String.valueOf(answers.get(j).getId()); if(response != null && answers.get(j).getId() == response.getMultipleChoiceId()) { initValue = String.valueOf(answers.get(j).getId()); } } UISelect multipleChoiceSelect = UISelect.make(questionForm, "multipleChoiceSelect:", options, "#{simplePageBean.questionResponse}", initValue); if(!isAvailable || response != null) { multipleChoiceSelect.decorate(new UIDisabledDecorator()); } for(int j = 0; j < answers.size(); j++) { UIBranchContainer answerContainer = UIBranchContainer.make(questionForm, "multipleChoiceAnswer:", String.valueOf(j)); UISelectChoice multipleChoiceInput = UISelectChoice.make(answerContainer, "multipleChoiceAnswerRadio", multipleChoiceSelect.getFullID(), j); multipleChoiceInput.decorate(new UIFreeAttributeDecorator("id", multipleChoiceInput.getFullID())); UIOutput.make(answerContainer, "multipleChoiceAnswerText", answers.get(j).getText()) .decorate(new UIFreeAttributeDecorator("for", multipleChoiceInput.getFullID())); if(!isAvailable || response != null) { multipleChoiceInput.decorate(new UIDisabledDecorator()); } } UICommand answerButton = UICommand.make(questionForm, "answerMultipleChoice", messageLocator.getMessage("simplepage.answer_question"), "#{simplePageBean.answerMultipleChoiceQuestion}"); if(!isAvailable || response != null) { answerButton.decorate(new UIDisabledDecorator()); } }else if("shortanswer".equals(i.getAttribute("questionType"))) { UIOutput.make(tableRow, "shortanswerDiv"); UIForm questionForm = UIForm.make(tableRow, "shortanswerForm"); UIInput.make(questionForm, "shortanswerId", "#{simplePageBean.questionId}", String.valueOf(i.getId())); UIInput shortanswerInput = UIInput.make(questionForm, "shortanswerInput", "#{simplePageBean.questionResponse}"); if(!isAvailable || response != null) { shortanswerInput.decorate(new UIDisabledDecorator()); if(response.getShortanswer() != null) { shortanswerInput.setValue(response.getShortanswer()); } } UICommand answerButton = UICommand.make(questionForm, "answerShortanswer", messageLocator.getMessage("simplepage.answer_question"), "#{simplePageBean.answerShortanswerQuestion}"); if(!isAvailable || response != null) { answerButton.decorate(new UIDisabledDecorator()); } } Status questionStatus = getQuestionStatus(i, response); addStatusImage(questionStatus, tableRow, "questionStatus", null); String statusNote = getStatusNote(questionStatus); if (statusNote != null) // accessibility version of icon UIOutput.make(tableRow, "questionNote", statusNote); String statusText = null; if(questionStatus == Status.COMPLETED) statusText = i.getAttribute("questionCorrectText"); else if(questionStatus == Status.FAILED) statusText = i.getAttribute("questionIncorrectText"); if (statusText != null && !"".equals(statusText.trim())) UIOutput.make(tableRow, "questionStatusText", statusText); // Output the poll data if("multipleChoice".equals(i.getAttribute("questionType")) && (canEditPage || ("true".equals(i.getAttribute("questionShowPoll")) && (questionStatus == Status.COMPLETED || questionStatus == Status.FAILED)))) { UIOutput.make(tableRow, "showPollGraph", messageLocator.getMessage("simplepage.show-poll")); UIOutput questionGraph = UIOutput.make(tableRow, "questionPollGraph"); questionGraph.decorate(new UIFreeAttributeDecorator("id", "poll" + i.getId())); List<SimplePageQuestionResponseTotals> totals = simplePageToolDao.findQRTotals(i.getId()); HashMap<Long, Long> responseCounts = new HashMap<Long, Long>(); // in theory we don't need the first loop, as there should be a total // entry for all possible answers. But in case things are out of sync ... for(SimplePageQuestionAnswer answer : answers) responseCounts.put(answer.getId(), 0L); for(SimplePageQuestionResponseTotals total : totals) responseCounts.put(total.getResponseId(), total.getCount()); for(int j = 0; j < answers.size(); j++) { UIBranchContainer pollContainer = UIBranchContainer.make(tableRow, "questionPollData:", String.valueOf(j)); UIOutput.make(pollContainer, "questionPollText", answers.get(j).getText()); UIOutput.make(pollContainer, "questionPollNumber", String.valueOf(responseCounts.get(answers.get(j).getId()))); } } if(canEditPage) { UIOutput.make(tableRow, "question-td"); // always show grading panel. Currently this is the only way to get feedback if( !cameFromGradingPane) { QuestionGradingPaneViewParameters gp = new QuestionGradingPaneViewParameters(QuestionGradingPaneProducer.VIEW_ID); gp.placementId = toolManager.getCurrentPlacement().getId(); gp.questionItemId = i.getId(); gp.pageId = currentPage.getPageId(); gp.pageItemId = pageItem.getId(); UIInternalLink.make(tableRow, "questionGradingPaneLink", messageLocator.getMessage("simplepage.show-grading-pane"), gp) .decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.show-grading-pane"))); } UILink.make(tableRow, "edit-question", messageLocator.getMessage("simplepage.editItem"), "") .decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.edit-title.question"))); UIOutput.make(tableRow, "questionId", String.valueOf(i.getId())); boolean graded = "true".equals(i.getAttribute("questionGraded")) || i.getGradebookId() != null; UIOutput.make(tableRow, "questionGrade", String.valueOf(graded)); UIOutput.make(tableRow, "questionMaxPoints", String.valueOf(i.getGradebookPoints())); UIOutput.make(tableRow, "questionGradebookTitle", String.valueOf(i.getGradebookTitle())); UIOutput.make(tableRow, "questionitem-required", String.valueOf(i.isRequired())); UIOutput.make(tableRow, "questionitem-prerequisite", String.valueOf(i.isPrerequisite())); UIOutput.make(tableRow, "questionCorrectText", String.valueOf(i.getAttribute("questionCorrectText"))); UIOutput.make(tableRow, "questionIncorrectText", String.valueOf(i.getAttribute("questionIncorrectText"))); if("shortanswer".equals(i.getAttribute("questionType"))) { UIOutput.make(tableRow, "questionType", "shortanswer"); UIOutput.make(tableRow, "questionAnswer", i.getAttribute("questionAnswer")); }else { UIOutput.make(tableRow, "questionType", "multipleChoice"); for(int j = 0; j < answers.size(); j++) { UIBranchContainer answerContainer = UIBranchContainer.make(tableRow, "questionMultipleChoiceAnswer:", String.valueOf(j)); UIOutput.make(answerContainer, "questionMultipleChoiceAnswerId", String.valueOf(answers.get(j).getId())); UIOutput.make(answerContainer, "questionMultipleChoiceAnswerText", answers.get(j).getText()); UIOutput.make(answerContainer, "questionMultipleChoiceAnswerCorrect", String.valueOf(answers.get(j).isCorrect())); } UIOutput.make(tableRow, "questionShowPoll", String.valueOf(i.getAttribute("questionShowPoll"))); } } } else { // remaining type must be a block of HTML UIOutput.make(tableRow, "itemSpan"); if (canSeeAll) { String itemGroupString = simplePageBean.getItemGroupString(i, null, true); String itemGroupTitles = simplePageBean.getItemGroupTitles(itemGroupString, i); if (itemGroupTitles != null) { itemGroupTitles = "[" + itemGroupTitles + "]"; } UIOutput.make(tableRow, "item-groups-titles-text", itemGroupTitles); } if(canSeeAll || simplePageBean.isItemAvailable(i)) { UIVerbatim.make(tableRow, "content", (i.getHtml() == null ? "" : i.getHtml())); } else { UIComponent unavailableText = UIOutput.make(tableRow, "content", messageLocator.getMessage("simplepage.textItemUnavailable")); unavailableText.decorate(new UIFreeAttributeDecorator("class", "disabled-text-item")); } // editing is done using a special producer that calls FCK. if (canEditPage) { GeneralViewParameters eParams = new GeneralViewParameters(); eParams.setSendingPage(currentPage.getPageId()); eParams.setItemId(i.getId()); eParams.viewID = EditPageProducer.VIEW_ID; UIOutput.make(tableRow, "edittext-td"); UIInternalLink.make(tableRow, "edit-link", messageLocator.getMessage("simplepage.editItem"), eParams).decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.edit-title.textbox").replace("{}", Integer.toString(textboxcount)))); textboxcount++; } } } // end of items. This is the end for normal users. Following is // special // checks and putting out the dialogs for the popups, for // instructors. boolean showBreak = false; // I believe refresh is now done automatically in all cases // if (showRefresh) { // UIOutput.make(tofill, "refreshAlert"); // // // Should simply refresh // GeneralViewParameters p = new GeneralViewParameters(VIEW_ID); // p.setSendingPage(currentPage.getPageId()); // UIInternalLink.make(tofill, "refreshLink", p); // showBreak = true; // } // stuff goes on the page in the order in the HTML file. So the fact // that it's here doesn't mean it shows // up at the end. This code produces errors and other odd stuff. if (canSeeAll) { // if the page is hidden, warn the faculty [students get stopped // at // the top] if (currentPage.isHidden()) { UIOutput.make(tofill, "hiddenAlert").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.pagehidden"))); UIVerbatim.make(tofill, "hidden-text", messageLocator.getMessage("simplepage.pagehidden.text")); showBreak = true; // similarly warn them if it isn't released yet } else if (currentPage.getReleaseDate() != null && currentPage.getReleaseDate().after(new Date())) { DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, M_locale); TimeZone tz = timeService.getLocalTimeZone(); df.setTimeZone(tz); String releaseDate = df.format(currentPage.getReleaseDate()); UIOutput.make(tofill, "hiddenAlert").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.notreleased"))); UIVerbatim.make(tofill, "hidden-text", messageLocator.getMessage("simplepage.notreleased.text").replace("{}", releaseDate)); showBreak = true; } } if (showBreak) { UIOutput.make(tofill, "breakAfterWarnings"); } } // more warnings: if no item on the page, give faculty instructions, // students an error if (!anyItemVisible) { if (canEditPage) { UIOutput.make(tofill, "startupHelp") .decorate(new UIFreeAttributeDecorator("src", getLocalizedURL((currentPage.getOwner() != null) ? "student.html" : "general.html"))) .decorate(new UIFreeAttributeDecorator("id", "iframe")); if (!iframeJavascriptDone) { UIOutput.make(tofill, "iframeJavascript"); iframeJavascriptDone = true; } } else { UIOutput.make(tofill, "error-div"); UIOutput.make(tofill, "error", messageLocator.getMessage("simplepage.noitems_error_user")); } } // now output the dialogs. but only for faculty (to avoid making the // file bigger) if (canEditPage) { createSubpageDialog(tofill, currentPage); } createDialogs(tofill, currentPage, pageItem); } public void createDialogs(UIContainer tofill, SimplePage currentPage, SimplePageItem pageItem) { createEditItemDialog(tofill, currentPage, pageItem); createAddMultimediaDialog(tofill, currentPage); createEditMultimediaDialog(tofill, currentPage); createEditTitleDialog(tofill, currentPage, pageItem); createNewPageDialog(tofill, currentPage, pageItem); createRemovePageDialog(tofill, currentPage, pageItem); createImportCcDialog(tofill); createExportCcDialog(tofill); createYoutubeDialog(tofill, currentPage); createMovieDialog(tofill, currentPage); createCommentsDialog(tofill); createStudentContentDialog(tofill, currentPage); createQuestionDialog(tofill, currentPage); } // get encrypted version of session id. This is our equivalent of session.id, except that we // encrypt it so it can't be used for anything else in case someone captures it. // we can't use time to avoid replay, as some time can go by between display of the page and // when the user clicks. The only thing this can be used for is reading multimedia files. I // think we're willing to risk that. I used to use session.id, but by default that's now off, // and turning it on to use it here would expose us to more serious risks. Cache the encryption. // we could include the whole URL in the encryption if it was worth the additional over head. // I think it's not. // url is /access/lessonbuilder/item/NNN/url. Because the server side // sees a reference starting with /item, we send that. public String getSessionParameter(String url) { UsageSession session = UsageSessionService.getSession(); if (!url.startsWith("/access/lessonbuilder")) return null; url = url.substring("/access/lessonbuilder".length()); try { Cipher sessionCipher = Cipher.getInstance("Blowfish"); sessionCipher.init(Cipher.ENCRYPT_MODE, lessonBuilderAccessService.getSessionKey()); String sessionParam = session.getId() + ":" + url; byte[] sessionBytes = sessionParam.getBytes("UTF8"); sessionBytes = sessionCipher.doFinal(sessionBytes); sessionParam = DatatypeConverter.printHexBinary(sessionBytes); return sessionParam; } catch (Exception e) { System.out.println("unable to generate encrypted session id " + e); return null; } } public void setSimplePageBean(SimplePageBean simplePageBean) { this.simplePageBean = simplePageBean; } public void setHttpServletRequest(HttpServletRequest httpServletRequest) { this.httpServletRequest = httpServletRequest; } public void setHttpServletResponse(HttpServletResponse httpServletResponse) { this.httpServletResponse = httpServletResponse; } private boolean makeLink(UIContainer container, String ID, SimplePageItem i, boolean canEditPage, SimplePage currentPage, boolean notDone, Status status) { return makeLink(container, ID, i, simplePageBean, simplePageToolDao, messageLocator, canEditPage, currentPage, notDone, status); } /** * * @param container * @param ID * @param i * @param simplePageBean * @param simplePageToolDao * @return Whether or not this item is available. */ protected static boolean makeLink(UIContainer container, String ID, SimplePageItem i, SimplePageBean simplePageBean, SimplePageToolDao simplePageToolDao, MessageLocator messageLocator, boolean canEditPage, SimplePage currentPage, boolean notDone, Status status) { String URL = ""; boolean available = simplePageBean.isItemAvailable(i); boolean fake = !available; // by default, fake output if not available String itemString = Long.toString(i.getId()); if (i.getSakaiId().equals(SimplePageItem.DUMMY)) { fake = true; // dummy is fake, but still available } else if (i.getType() == SimplePageItem.RESOURCE || i.getType() == SimplePageItem.URL) { if (i.getType() == SimplePageItem.RESOURCE && i.isSameWindow()) { if (available) { GeneralViewParameters params = new GeneralViewParameters(ShowItemProducer.VIEW_ID); params.setSendingPage(currentPage.getPageId()); params.setSource(i.getItemURL(simplePageBean.getCurrentSiteId(),currentPage.getOwner())); params.setItemId(i.getId()); UILink link = UIInternalLink.make(container, "link", params); link.decorate(new UIFreeAttributeDecorator("lessonbuilderitem", itemString)); } } else { if (available) { URL = i.getItemURL(simplePageBean.getCurrentSiteId(),currentPage.getOwner()); UIInternalLink link = LinkTrackerProducer.make(container, ID, i.getName(), URL, i.getId(), notDone); link.decorate(new UIFreeAttributeDecorator("lessonbuilderitem", itemString)); link.decorate(new UIFreeAttributeDecorator("target", "_blank")); }; } } else if (i.getType() == SimplePageItem.PAGE) { SimplePage p = simplePageToolDao.getPage(Long.valueOf(i.getSakaiId())); if(p != null) { GeneralViewParameters eParams = new GeneralViewParameters(ShowPageProducer.VIEW_ID, p.getPageId()); eParams.setItemId(i.getId()); // nextpage indicates whether it should be pushed onto breadcrumbs // or replace the top item if (i.getNextPage()) { eParams.setPath("next"); } else { eParams.setPath("push"); } boolean isbutton = false; // button says to display the link as a button. use navIntrTool, // which is standard // Sakai CSS that generates the type of button used in toolbars. We // have to override // with background:transparent or we get remnants of the gray if ("button".equals(i.getFormat())) { isbutton = true; UIOutput span = UIOutput.make(container, ID + "-button-span"); ID = ID + "-button"; if (!i.isRequired()) { span.decorate(new UIFreeAttributeDecorator("class", "navIntraTool buttonItem")); } isbutton = true; } UILink link; if (available) { link = UIInternalLink.make(container, ID, eParams); link.decorate(new UIFreeAttributeDecorator("lessonbuilderitem", itemString)); if (i.isPrerequisite()) { simplePageBean.checkItemPermissions(i, true); } // at this point we know the page isn't available, i.e. user // hasn't // met all the prerequistes. Normally we give them a nonworking // grayed out link. But if they are the author, we want to // give them a real link. Otherwise if it's a subpage they have // no way to get to it (currently -- we'll fix that) // but we make it look like it's disabled so they can see what // students see } else if (canEditPage) { // for author, need to be able to get to the subpage to edit it // so put out a function button, but make it look disabled fake = false; // so we don't get an fake button as well link = UIInternalLink.make(container, ID, eParams); link.decorate(new UIFreeAttributeDecorator("lessonbuilderitem", itemString)); fakeDisableLink(link, messageLocator); } // otherwise fake }else { log.warn("Lesson Builder Item #" + i.getId() + " does not have an associated page."); return false; } } else if (i.getType() == SimplePageItem.ASSIGNMENT) { LessonEntity lessonEntity = assignmentEntity.getEntity(i.getSakaiId(), simplePageBean); if (available && lessonEntity != null) { if (i.isPrerequisite()) { simplePageBean.checkItemPermissions(i, true); } GeneralViewParameters params = new GeneralViewParameters(ShowItemProducer.VIEW_ID); params.setSendingPage(currentPage.getPageId()); params.setSource((lessonEntity == null) ? "dummy" : lessonEntity.getUrl()); params.setItemId(i.getId()); UILink link = UIInternalLink.make(container, "link", params); link.decorate(new UIFreeAttributeDecorator("lessonbuilderitem", itemString)); } else { if (i.isPrerequisite()) { simplePageBean.checkItemPermissions(i, false); } fake = true; // need to set this in case it's available for missing entity } } else if (i.getType() == SimplePageItem.ASSESSMENT) { LessonEntity lessonEntity = quizEntity.getEntity(i.getSakaiId(),simplePageBean); if (available && lessonEntity != null) { if (i.isPrerequisite()) { simplePageBean.checkItemPermissions(i, true); } // we've hacked Samigo to look at a special lesson builder // session // attribute. otherwise at the end of the test, Samigo replaces // the // whole screen, exiting form our iframe. The other tools don't // do this. GeneralViewParameters view = new GeneralViewParameters(ShowItemProducer.VIEW_ID); view.setSendingPage(currentPage.getPageId()); view.setClearAttr("LESSONBUILDER_RETURNURL_SAMIGO"); view.setSource((lessonEntity == null) ? "dummy" : lessonEntity.getUrl()); view.setItemId(i.getId()); UILink link = UIInternalLink.make(container, "link", view); link.decorate(new UIFreeAttributeDecorator("lessonbuilderitem", itemString)); } else { if (i.isPrerequisite()) { simplePageBean.checkItemPermissions(i, false); } fake = true; // need to set this in case it's available for missing entity } } else if (i.getType() == SimplePageItem.FORUM) { LessonEntity lessonEntity = forumEntity.getEntity(i.getSakaiId()); if (available && lessonEntity != null) { if (i.isPrerequisite()) { simplePageBean.checkItemPermissions(i, true); } GeneralViewParameters view = new GeneralViewParameters(ShowItemProducer.VIEW_ID); view.setSendingPage(currentPage.getPageId()); view.setItemId(i.getId()); view.setSource((lessonEntity == null) ? "dummy" : lessonEntity.getUrl()); UILink link = UIInternalLink.make(container, "link", view); link.decorate(new UIFreeAttributeDecorator("lessonbuilderitem", itemString)); } else { if (i.isPrerequisite()) { simplePageBean.checkItemPermissions(i, false); } fake = true; // need to set this in case it's available for missing entity } } else if (i.getType() == SimplePageItem.BLTI) { LessonEntity lessonEntity = (bltiEntity == null ? null : bltiEntity.getEntity(i.getSakaiId())); if ("inline".equals(i.getFormat())) { // no availability String height=null; if (i.getHeight() != null && !i.getHeight().equals("")) height = i.getHeight().replace("px",""); // just in case UIComponent iframe = UIOutput.make(container, "blti-iframe"); if (lessonEntity != null) iframe.decorate(new UIFreeAttributeDecorator("src", lessonEntity.getUrl())); String h = "300"; if (height != null && !height.trim().equals("")) h = height; iframe.decorate(new UIFreeAttributeDecorator("height", h)); iframe.decorate(new UIFreeAttributeDecorator("title", i.getName())); // normally we get the name from the link text, but there's no link text here UIOutput.make(container, "item-name", i.getName()); } else if (!"window".equals(i.getFormat())) { // this is the default if format isn't valid or is missing if (available && lessonEntity != null) { if (i.isPrerequisite()) { simplePageBean.checkItemPermissions(i, true); } GeneralViewParameters view = new GeneralViewParameters(ShowItemProducer.VIEW_ID); view.setSendingPage(currentPage.getPageId()); view.setItemId(i.getId()); view.setSource((lessonEntity==null)?"dummy":lessonEntity.getUrl()); UIComponent link = UIInternalLink.make(container, "link", view) .decorate(new UIFreeAttributeDecorator("lessonbuilderitem", itemString)); } else { if (i.isPrerequisite()) { simplePageBean.checkItemPermissions(i, false); } fake = true; // need to set this in case it's available for missing entity } } else { if (available && lessonEntity != null) { URL = lessonEntity.getUrl(); UIInternalLink link = LinkTrackerProducer.make(container, ID, i.getName(), URL, i.getId(), notDone); link.decorate(new UIFreeAttributeDecorator("lessonbuilderitem", itemString)); link.decorate(new UIFreeAttributeDecorator("target", "_blank")); } else fake = true; // need to set this in case it's available for missing entity } } String note = null; if (status == Status.COMPLETED) { note = messageLocator.getMessage("simplepage.status.completed"); } if (status == Status.REQUIRED) { note = messageLocator.getMessage("simplepage.status.required"); } if (fake) { ID = ID + "-fake"; UIOutput link = UIOutput.make(container, ID, i.getName()); link.decorate(new UIFreeAttributeDecorator("lessonbuilderitem", itemString)); if (!available) link.decorate(new UITooltipDecorator(messageLocator.getMessage("simplepage.complete_required"))); } else UIOutput.make(container, ID + "-text", i.getName()); if (note != null) { UIOutput.make(container, ID + "-note", note + " "); } return available; } private static void disableLink(UIComponent link, MessageLocator messageLocator) { link.decorate(new UIFreeAttributeDecorator("onclick", "return false")); link.decorate(new UIDisabledDecorator()); link.decorate(new UIStyleDecorator("disabled")); link.decorate(new UIFreeAttributeDecorator("style", "color:#999 !important")); link.decorate(new UITooltipDecorator(messageLocator.getMessage("simplepage.complete_required"))); } // show is if it was disabled but don't actually private static void fakeDisableLink(UILink link, MessageLocator messageLocator) { link.decorate(new UIFreeAttributeDecorator("style", "color:#999 !important")); link.decorate(new UITooltipDecorator(messageLocator.getMessage("simplepage.complete_required"))); } public void setSimplePageToolDao(SimplePageToolDao s) { simplePageToolDao = s; } public void setDateEvolver(FormatAwareDateInputEvolver dateevolver) { this.dateevolver = dateevolver; } public void setTimeService(TimeService ts) { timeService = ts; } public void setLocaleGetter(LocaleGetter localegetter) { this.localegetter = localegetter; } public void setForumEntity(LessonEntity e) { // forumEntity is static, so it may already have been set // there is a possible race condition, but since the bean is // a singleton both people in the race will be trying to set // the same value. So it shouldn't matter if (forumEntity == null) { forumEntity = e; } } public void setQuizEntity(LessonEntity e) { // forumEntity is static, so it may already have been set // there is a possible race condition, but since the bean is // a singleton both people in the race will be trying to set // the same value. So it shouldn't matter if (quizEntity == null) { quizEntity = e; } } public void setAssignmentEntity(LessonEntity e) { // forumEntity is static, so it may already have been set // there is a possible race condition, but since the bean is // a singleton both people in the race will be trying to set // the same value. So it shouldn't matter if (assignmentEntity == null) { assignmentEntity = e; } } public void setBltiEntity(LessonEntity e) { if (bltiEntity == null) bltiEntity = e; } public void setToolManager(ToolManager m) { toolManager = m; } public void setLessonBuilderAccessService (LessonBuilderAccessService a) { lessonBuilderAccessService = a; } public ViewParameters getViewParameters() { return new GeneralViewParameters(); } /** * Checks for the version of IE. Returns 0 if we're not running IE. * But there's a problem. IE 11 doesn't have the MSIE tag. But it stiill * needs to be treated as IE, because the OBJECT tag won't work with Quicktime * Since all I test is > 0, I use a simplified version that returns 0 or 1 * @return */ public int checkIEVersion() { UsageSession usageSession = UsageSessionService.getSession(); if (usageSession == null) return 0; browserString = usageSession.getUserAgent(); if (browserString == null) return 0; int ieIndex = browserString.indexOf("Trident/"); if (ieIndex >= 0) return 1; else return 0; // int ieVersion = 0; // if (ieIndex >= 0) { // String ieV = browserString.substring(ieIndex + 6); // int i = 0; // int e = ieV.length(); // while (i < e) { // if (Character.isDigit(ieV.charAt(i))) { // i++; // } else { // break; // } // } // if (i > 0) { // ieV = ieV.substring(0, i); // ieVersion = Integer.parseInt(ieV); //} } // // return ieVersion; } private void createToolBar(UIContainer tofill, SimplePage currentPage, boolean isStudent) { UIBranchContainer toolBar = UIBranchContainer.make(tofill, "tool-bar:"); boolean studentPage = currentPage.getOwner() != null; // toolbar // dropdowns UIOutput.make(toolBar, "icondropc"); UIOutput.make(toolBar, "icondrop"); // right side createToolBarLink(ReorderProducer.VIEW_ID, toolBar, "reorder", "simplepage.reorder", currentPage, "simplepage.reorder-tooltip"); UILink.make(toolBar, "help", messageLocator.getMessage("simplepage.help"), getLocalizedURL( isStudent ? "student.html" : "general.html")); if (!studentPage) createToolBarLink(PermissionsHelperProducer.VIEW_ID, toolBar, "permissions", "simplepage.permissions", currentPage, "simplepage.permissions.tooltip"); // add content menu createToolBarLink(EditPageProducer.VIEW_ID, tofill, "add-text", "simplepage.text", currentPage, "simplepage.text.tooltip").setItemId(null); createFilePickerToolBarLink(ResourcePickerProducer.VIEW_ID, tofill, "add-multimedia", "simplepage.multimedia", true, false, currentPage, "simplepage.multimedia.tooltip"); createFilePickerToolBarLink(ResourcePickerProducer.VIEW_ID, tofill, "add-resource", "simplepage.resource", false, false, currentPage, "simplepage.resource.tooltip"); UILink subpagelink = UIInternalLink.makeURL(tofill, "subpage-link", "#"); // content menu not on students if (!studentPage) { // add website. // Are we running a kernel with KNL-273? Class contentHostingInterface = ContentHostingService.class; try { Method expandMethod = contentHostingInterface.getMethod("expandZippedResource", new Class[] { String.class }); UIOutput.make(tofill, "addwebsite-li"); createFilePickerToolBarLink(ResourcePickerProducer.VIEW_ID, tofill, "add-website", "simplepage.website", false, true, currentPage, "simplepage.website.tooltip"); } catch (NoSuchMethodException nsme) { // A: No } catch (Exception e) { // A: Not sure log.warn("SecurityException thrown by expandZippedResource method lookup", e); } UIOutput.make(tofill, "assignment-li"); createToolBarLink(AssignmentPickerProducer.VIEW_ID, tofill, "add-assignment", "simplepage.assignment", currentPage, "simplepage.assignment"); UIOutput.make(tofill, "quiz-li"); createToolBarLink(QuizPickerProducer.VIEW_ID, tofill, "add-quiz", "simplepage.quiz", currentPage, "simplepage.quiz"); UIOutput.make(tofill, "forum-li"); createToolBarLink(ForumPickerProducer.VIEW_ID, tofill, "add-forum", "simplepage.forum", currentPage, "simplepage.forum.tooltip"); UIOutput.make(tofill, "question-li"); UILink questionlink = UIInternalLink.makeURL(tofill, "question-link", "#"); GeneralViewParameters eParams = new GeneralViewParameters(VIEW_ID); eParams.addTool = GeneralViewParameters.COMMENTS; UIOutput.make(tofill, "student-li"); UIInternalLink.make(tofill, "add-comments", messageLocator.getMessage("simplepage.comments"), eParams). decorate(new UITooltipDecorator(messageLocator.getMessage("simplepage.comments.tooltip"))); eParams = new GeneralViewParameters(VIEW_ID); eParams.addTool = GeneralViewParameters.STUDENT_CONTENT; UIOutput.make(tofill, "studentcontent-li"); UIInternalLink.make(tofill, "add-content", messageLocator.getMessage("simplepage.add-student-content"), eParams). decorate(new UITooltipDecorator(messageLocator.getMessage("simplepage.student-descrip"))); // in case we're on an old system without current BLTI if (bltiEntity != null && ((BltiInterface)bltiEntity).servicePresent()) { UIOutput.make(tofill, "blti-li"); createToolBarLink(BltiPickerProducer.VIEW_ID, tofill, "add-blti", "simplepage.blti", currentPage, "simplepage.blti.tooltip"); } } } private GeneralViewParameters createToolBarLink(String viewID, UIContainer tofill, String ID, String message, SimplePage currentPage, String tooltip) { GeneralViewParameters params = new GeneralViewParameters(); params.setSendingPage(currentPage.getPageId()); createStandardToolBarLink(viewID, tofill, ID, message, params, tooltip); return params; } private FilePickerViewParameters createFilePickerToolBarLink(String viewID, UIContainer tofill, String ID, String message, boolean resourceType, boolean website, SimplePage currentPage, String tooltip) { FilePickerViewParameters fileparams = new FilePickerViewParameters(); fileparams.setSender(currentPage.getPageId()); fileparams.setResourceType(resourceType); fileparams.setWebsite(website); createStandardToolBarLink(viewID, tofill, ID, message, fileparams, tooltip); return fileparams; } private void createStandardToolBarLink(String viewID, UIContainer tofill, String ID, String message, SimpleViewParameters params, String tooltip) { params.viewID = viewID; UILink link = UIInternalLink.make(tofill, ID, messageLocator.getMessage(message), params); link.decorate(new UITooltipDecorator(messageLocator.getMessage(tooltip))); } public List reportNavigationCases() { List<NavigationCase> togo = new ArrayList<NavigationCase>(); togo.add(new NavigationCase(null, new SimpleViewParameters(ShowPageProducer.VIEW_ID))); togo.add(new NavigationCase("success", new SimpleViewParameters(ShowPageProducer.VIEW_ID))); togo.add(new NavigationCase("cancel", new SimpleViewParameters(ShowPageProducer.VIEW_ID))); togo.add(new NavigationCase("failure", new SimpleViewParameters(ReloadPageProducer.VIEW_ID))); togo.add(new NavigationCase("reload", new SimpleViewParameters(ReloadPageProducer.VIEW_ID))); togo.add(new NavigationCase("removed", new SimpleViewParameters(RemovePageProducer.VIEW_ID))); return togo; } private void createSubpageDialog(UIContainer tofill, SimplePage currentPage) { UIOutput.make(tofill, "subpage-dialog").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.subpage"))); UIForm form = UIForm.make(tofill, "subpage-form"); UIOutput.make(form, "subpage-label", messageLocator.getMessage("simplepage.pageTitle_label")); UIInput.make(form, "subpage-title", "#{simplePageBean.subpageTitle}"); GeneralViewParameters view = new GeneralViewParameters(PagePickerProducer.VIEW_ID); view.setSendingPage(currentPage.getPageId()); if(currentPage.getOwner() == null) { UIInternalLink.make(form, "subpage-choose", messageLocator.getMessage("simplepage.choose_existing_page"), view); } UIBoundBoolean.make(form, "subpage-next", "#{simplePageBean.subpageNext}", false); UIBoundBoolean.make(form, "subpage-button", "#{simplePageBean.subpageButton}", false); UICommand.make(form, "create-subpage", messageLocator.getMessage("simplepage.create"), "#{simplePageBean.createSubpage}"); UICommand.make(form, "cancel-subpage", messageLocator.getMessage("simplepage.cancel"), null); } private void createEditItemDialog(UIContainer tofill, SimplePage currentPage, SimplePageItem pageItem) { UIOutput.make(tofill, "edit-item-dialog").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.edititem_header"))); UIForm form = UIForm.make(tofill, "edit-form"); UIOutput.make(form, "name-label", messageLocator.getMessage("simplepage.name_label")); UIInput.make(form, "name", "#{simplePageBean.name}"); UIOutput.make(form, "description-label", messageLocator.getMessage("simplepage.description_label")); UIInput.make(form, "description", "#{simplePageBean.description}"); UIOutput changeDiv = UIOutput.make(form, "changeDiv"); if(currentPage.getOwner() != null) changeDiv.decorate(new UIStyleDecorator("noDisplay")); GeneralViewParameters params = new GeneralViewParameters(); params.setSendingPage(currentPage.getPageId()); params.viewID = AssignmentPickerProducer.VIEW_ID; UIInternalLink.make(form, "change-assignment", messageLocator.getMessage("simplepage.change_assignment"), params); params = new GeneralViewParameters(); params.setSendingPage(currentPage.getPageId()); params.viewID = QuizPickerProducer.VIEW_ID; UIInternalLink.make(form, "change-quiz", messageLocator.getMessage("simplepage.change_quiz"), params); params = new GeneralViewParameters(); params.setSendingPage(currentPage.getPageId()); params.viewID = ForumPickerProducer.VIEW_ID; UIInternalLink.make(form, "change-forum", messageLocator.getMessage("simplepage.change_forum"), params); params = new GeneralViewParameters(); params.setSendingPage(currentPage.getPageId()); params.viewID = BltiPickerProducer.VIEW_ID; UIInternalLink.make(form, "change-blti", messageLocator.getMessage("simplepage.change_blti"), params); FilePickerViewParameters fileparams = new FilePickerViewParameters(); fileparams.setSender(currentPage.getPageId()); fileparams.setResourceType(false); fileparams.setWebsite(false); fileparams.viewID = ResourcePickerProducer.VIEW_ID; UIInternalLink.make(form, "change-resource", messageLocator.getMessage("simplepage.change_resource"), fileparams); params = new GeneralViewParameters(); params.setSendingPage(currentPage.getPageId()); params.viewID = PagePickerProducer.VIEW_ID; UIInternalLink.make(form, "change-page", messageLocator.getMessage("simplepage.change_page"), params); params = new GeneralViewParameters(); params.setSendingPage(currentPage.getPageId()); params.setItemId(pageItem.getId()); params.setReturnView(VIEW_ID); params.setTitle(messageLocator.getMessage("simplepage.return_from_edit")); params.setSource("SRC"); params.viewID = ShowItemProducer.VIEW_ID; UIInternalLink.make(form, "edit-item-object", params); UIOutput.make(form, "edit-item-text"); params = new GeneralViewParameters(); params.setSendingPage(currentPage.getPageId()); params.setItemId(pageItem.getId()); params.setReturnView(VIEW_ID); params.setTitle(messageLocator.getMessage("simplepage.return_from_edit")); params.setSource("SRC"); params.viewID = ShowItemProducer.VIEW_ID; UIInternalLink.make(form, "edit-item-settings", params); UIOutput.make(form, "edit-item-settings-text"); UIBoundBoolean.make(form, "item-next", "#{simplePageBean.subpageNext}", false); UIBoundBoolean.make(form, "item-button", "#{simplePageBean.subpageButton}", false); UIInput.make(form, "item-id", "#{simplePageBean.itemId}"); UIOutput permDiv = UIOutput.make(form, "permDiv"); if(currentPage.getOwner() != null) permDiv.decorate(new UIStyleDecorator("noDisplay")); UIBoundBoolean.make(form, "item-required2", "#{simplePageBean.subrequirement}", false); UIBoundBoolean.make(form, "item-required", "#{simplePageBean.required}", false); UIBoundBoolean.make(form, "item-prerequisites", "#{simplePageBean.prerequisite}", false); UIBoundBoolean.make(form, "item-newwindow", "#{simplePageBean.newWindow}", false); UISelect radios = UISelect.make(form, "format-select", new String[] {"window", "inline", "page"}, "#{simplePageBean.format}", ""); UISelectChoice.make(form, "format-window", radios.getFullID(), 0); UISelectChoice.make(form, "format-inline", radios.getFullID(), 1); UISelectChoice.make(form, "format-page", radios.getFullID(), 2); UIInput.make(form, "edit-height-value", "#{simplePageBean.height}"); UISelect.make(form, "assignment-dropdown", SimplePageBean.GRADES, "#{simplePageBean.dropDown}", SimplePageBean.GRADES[0]); UIInput.make(form, "assignment-points", "#{simplePageBean.points}"); UICommand.make(form, "edit-item", messageLocator.getMessage("simplepage.edit"), "#{simplePageBean.editItem}"); // can't use site groups on user content, and don't want students to hack // on groups for site content if (currentPage.getOwner() == null) createGroupList(form, null, "", "#{simplePageBean.selectedGroups}"); UICommand.make(form, "delete-item", messageLocator.getMessage("simplepage.delete"), "#{simplePageBean.deleteItem}"); UICommand.make(form, "edit-item-cancel", messageLocator.getMessage("simplepage.cancel"), null); } // for both add multimedia and add resource, as well as updating resources // in the edit dialogs public void createGroupList(UIContainer tofill, Collection<String> groupsSet, String prefix, String beanspec) { List<GroupEntry> groups = simplePageBean.getCurrentGroups(); ArrayList<String> values = new ArrayList<String>(); ArrayList<String> initValues = new ArrayList<String>(); if (groups == null || groups.size() == 0) return; for (GroupEntry entry : groups) { if (entry.name.startsWith("Access: ")) { continue; } values.add(entry.id); if (groupsSet != null && groupsSet.contains(entry.id)) { initValues.add(entry.id); } } if (groupsSet == null || groupsSet.size() == 0) { initValues.add(""); } // this could happen if the only groups are Access groups if (values.size() == 0) return; UIOutput.make(tofill, prefix + "grouplist"); UISelect select = UISelect.makeMultiple(tofill, prefix + "group-list-span", values.toArray(new String[1]), beanspec, initValues.toArray(new String[1])); int index = 0; for (GroupEntry entry : groups) { if (entry.name.startsWith("Access: ")) { continue; } UIBranchContainer row = UIBranchContainer.make(tofill, prefix + "select-group-list:"); UISelectChoice.make(row, prefix + "select-group", select.getFullID(), index); UIOutput.make(row, prefix + "select-group-text", entry.name); index++; } } // for both add multimedia and add resource, as well as updating resources // in the edit dialogs private void createAddMultimediaDialog(UIContainer tofill, SimplePage currentPage) { UIOutput.make(tofill, "add-multimedia-dialog").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.resource"))); UILink.make(tofill, "mm-additional-instructions", messageLocator.getMessage("simplepage.additional-instructions-label"), getLocalizedURL( "multimedia.html")); UILink.make(tofill, "mm-additional-website-instructions", messageLocator.getMessage("simplepage.additional-website-instructions-label"), getLocalizedURL( "website.html")); UIForm form = UIForm.make(tofill, "add-multimedia-form"); UIOutput.make(form, "mm-file-label", messageLocator.getMessage("simplepage.upload_label")); UIOutput.make(form, "mm-url-label", messageLocator.getMessage("simplepage.addLink_label")); UIInput.make(form, "mm-url", "#{simplePageBean.mmUrl}"); FilePickerViewParameters fileparams = new FilePickerViewParameters(); fileparams.setSender(currentPage.getPageId()); fileparams.setResourceType(true); fileparams.viewID = ResourcePickerProducer.VIEW_ID; UILink link = UIInternalLink.make(form, "mm-choose", messageLocator.getMessage("simplepage.choose_existing_or"), fileparams); UICommand.make(form, "mm-add-item", messageLocator.getMessage("simplepage.save_message"), "#{simplePageBean.addMultimedia}"); UIInput.make(form, "mm-item-id", "#{simplePageBean.itemId}"); UIInput.make(form, "mm-is-mm", "#{simplePageBean.isMultimedia}"); UIInput.make(form, "mm-display-type", "#{simplePageBean.multimediaDisplayType}"); UIInput.make(form, "mm-is-website", "#{simplePageBean.isWebsite}"); UICommand.make(form, "mm-cancel", messageLocator.getMessage("simplepage.cancel"), null); } private void createImportCcDialog(UIContainer tofill) { UIOutput.make(tofill, "import-cc-dialog").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.import_cc"))); UIForm form = UIForm.make(tofill, "import-cc-form"); UICommand.make(form, "import-cc-submit", messageLocator.getMessage("simplepage.save_message"), "#{simplePageBean.importCc}"); UICommand.make(form, "mm-cancel", messageLocator.getMessage("simplepage.cancel"), null); UIBoundBoolean.make(form, "import-toplevel", "#{simplePageBean.importtop}", false); class ToolData { String toolId; String toolName; } int numQuizEngines = 0; List<ToolData> quizEngines = new ArrayList<ToolData>(); for (LessonEntity q = quizEntity; q != null; q = q.getNextEntity()) { String toolId = q.getToolId(); String toolName = simplePageBean.getCurrentToolTitle(q.getToolId()); // we only want the ones that are actually in our site if (toolName != null) { ToolData toolData = new ToolData(); toolData.toolId = toolId; toolData.toolName = toolName; numQuizEngines++; quizEngines.add(toolData); } } if (numQuizEngines == 0) { UIVerbatim.make(form, "quizmsg", messageLocator.getMessage("simplepage.noquizengines")); } else if (numQuizEngines == 1) { UIInput.make(form, "quiztool", "#{simplePageBean.quiztool}", quizEntity.getToolId()); } else { // put up message and then radio buttons for each possibility // need values array for RSF's select implementation. It sees radio // buttons as a kind of select ArrayList<String> values = new ArrayList<String>(); for (ToolData toolData : quizEngines) { values.add(toolData.toolId); } // the message UIOutput.make(form, "quizmsg", messageLocator.getMessage("simplepage.choosequizengine")); // now the list of radio buttons UISelect quizselect = UISelect.make(form, "quiztools", values.toArray(new String[1]), "#{simplePageBean.quiztool}", null); int i = 0; for (ToolData toolData : quizEngines) { UIBranchContainer toolItem = UIBranchContainer.make(form, "quiztoolitem:", String.valueOf(i)); UISelectChoice.make(toolItem, "quiztoolbox", quizselect.getFullID(), i); UIOutput.make(toolItem, "quiztoollabel", toolData.toolName); i++; } } int numTopicEngines = 0; List<ToolData> topicEngines = new ArrayList<ToolData>(); for (LessonEntity q = forumEntity; q != null; q = q.getNextEntity()) { String toolId = q.getToolId(); String toolName = simplePageBean.getCurrentToolTitle(q.getToolId()); // we only want the ones that are actually in our site if (toolName != null) { ToolData toolData = new ToolData(); toolData.toolId = toolId; toolData.toolName = toolName; numTopicEngines++; topicEngines.add(toolData); } } if (numTopicEngines == 0) { UIVerbatim.make(form, "topicmsg", messageLocator.getMessage("simplepage.notopicengines")); } else if (numTopicEngines == 1) { UIInput.make(form, "topictool", "#{simplePageBean.topictool}", forumEntity.getToolId()); } else { ArrayList<String> values = new ArrayList<String>(); for (ToolData toolData : topicEngines) { values.add(toolData.toolId); } UIOutput.make(form, "topicmsg", messageLocator.getMessage("simplepage.choosetopicengine")); UISelect topicselect = UISelect.make(form, "topictools", values.toArray(new String[1]), "#{simplePageBean.topictool}", null); int i = 0; for (ToolData toolData : topicEngines) { UIBranchContainer toolItem = UIBranchContainer.make(form, "topictoolitem:", String.valueOf(i)); UISelectChoice.make(toolItem, "topictoolbox", topicselect.getFullID(), i); UIOutput.make(toolItem, "topictoollabel", toolData.toolName); i++; } } int numAssignEngines = 0; List<ToolData> assignEngines = new ArrayList<ToolData>(); for (LessonEntity q = assignmentEntity; q != null; q = q.getNextEntity()) { String toolId = q.getToolId(); String toolName = simplePageBean.getCurrentToolTitle(q.getToolId()); // we only want the ones that are actually in our site if (toolName != null) { ToolData toolData = new ToolData(); toolData.toolId = toolId; toolData.toolName = toolName; numAssignEngines++; assignEngines.add(toolData); } } if (numAssignEngines == 0) { UIVerbatim.make(form, "assignmsg", messageLocator.getMessage("simplepage.noassignengines")); } else if (numAssignEngines == 1) { UIInput.make(form, "assigntool", "#{simplePageBean.assigntool}", assignmentEntity.getToolId()); } else { ArrayList<String> values = new ArrayList<String>(); for (ToolData toolData : assignEngines) { values.add(toolData.toolId); } UIOutput.make(form, "assignmsg", messageLocator.getMessage("simplepage.chooseassignengine")); UISelect assignselect = UISelect.make(form, "assigntools", values.toArray(new String[1]), "#{simplePageBean.assigntool}", null); int i = 0; for (ToolData toolData : assignEngines) { UIBranchContainer toolItem = UIBranchContainer.make(form, "assigntoolitem:", String.valueOf(i)); UISelectChoice.make(toolItem, "assigntoolbox", assignselect.getFullID(), i); UIOutput.make(toolItem, "assigntoollabel", toolData.toolName); i++; } } } private void createExportCcDialog(UIContainer tofill) { UIOutput.make(tofill, "export-cc-dialog").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.export-cc-title"))); UIForm form = UIForm.make(tofill, "export-cc-form"); UIOutput.make(form, "export-cc-v11"); // value is handled by JS, so RSF doesn't need to treat it as input UIOutput.make(form, "export-cc-bank"); // value is handled by JS, so RSF doesn't need to treat it as input UICommand.make(form, "export-cc-submit", messageLocator.getMessage("simplepage.exportcc-download"), "#{simplePageBean.importCc}"); UICommand.make(form, "export-cc-cancel", messageLocator.getMessage("simplepage.cancel"), null); // the actual submission is with a GET. The submit button clicks this link. ExportCCViewParameters view = new ExportCCViewParameters("exportCc"); view.setExportcc(true); view.setVersion("1.2"); view.setBank("1"); UIInternalLink.make(form, "export-cc-link", "export cc link", view); } private void createEditMultimediaDialog(UIContainer tofill, SimplePage currentPage) { UIOutput.make(tofill, "edit-multimedia-dialog").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.editMultimedia"))); UIOutput.make(tofill, "instructions"); UIForm form = UIForm.make(tofill, "edit-multimedia-form"); UIOutput.make(form, "height-label", messageLocator.getMessage("simplepage.height_label")); UIInput.make(form, "height", "#{simplePageBean.height}"); UIOutput.make(form, "width-label", messageLocator.getMessage("simplepage.width_label")); UIInput.make(form, "width", "#{simplePageBean.width}"); UIOutput.make(form, "description2-label", messageLocator.getMessage("simplepage.description_label")); UIInput.make(form, "description2", "#{simplePageBean.description}"); FilePickerViewParameters fileparams = new FilePickerViewParameters(); fileparams.setSender(currentPage.getPageId()); fileparams.setResourceType(true); fileparams.viewID = ResourcePickerProducer.VIEW_ID; UIInternalLink.make(form, "change-resource-mm", messageLocator.getMessage("simplepage.change_resource"), fileparams); UIOutput.make(form, "alt-label", messageLocator.getMessage("simplepage.alt_label")); UIInput.make(form, "alt", "#{simplePageBean.alt}"); UIInput.make(form, "mimetype", "#{simplePageBean.mimetype}"); UICommand.make(form, "edit-multimedia-item", messageLocator.getMessage("simplepage.save_message"), "#{simplePageBean.editMultimedia}"); UIInput.make(form, "multimedia-item-id", "#{simplePageBean.itemId}"); UICommand.make(form, "delete-multimedia-item", messageLocator.getMessage("simplepage.delete"), "#{simplePageBean.deleteItem}"); UICommand.make(form, "edit-multimedia-cancel", messageLocator.getMessage("simplepage.cancel"), null); } private void createYoutubeDialog(UIContainer tofill, SimplePage currentPage) { UIOutput.make(tofill, "youtube-dialog").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.edit_youtubelink"))); UIForm form = UIForm.make(tofill, "youtube-form"); UIInput.make(form, "youtubeURL", "#{simplePageBean.youtubeURL}"); UIInput.make(form, "youtubeEditId", "#{simplePageBean.youtubeId}"); UIInput.make(form, "youtubeHeight", "#{simplePageBean.height}"); UIInput.make(form, "youtubeWidth", "#{simplePageBean.width}"); UIOutput.make(form, "description4-label", messageLocator.getMessage("simplepage.description_label")); UIInput.make(form, "description4", "#{simplePageBean.description}"); UICommand.make(form, "delete-youtube-item", messageLocator.getMessage("simplepage.delete"), "#{simplePageBean.deleteYoutubeItem}"); UICommand.make(form, "update-youtube", messageLocator.getMessage("simplepage.edit"), "#{simplePageBean.updateYoutube}"); UICommand.make(form, "cancel-youtube", messageLocator.getMessage("simplepage.cancel"), null); if(currentPage.getOwner() == null) { UIOutput.make(form, "editgroups-youtube"); } } private void createMovieDialog(UIContainer tofill, SimplePage currentPage) { UIOutput.make(tofill, "movie-dialog").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.edititem_header"))); UIForm form = UIForm.make(tofill, "movie-form"); UIInput.make(form, "movie-height", "#{simplePageBean.height}"); UIInput.make(form, "movie-width", "#{simplePageBean.width}"); UIInput.make(form, "movieEditId", "#{simplePageBean.itemId}"); UIOutput.make(form, "description3-label", messageLocator.getMessage("simplepage.description_label")); UIInput.make(form, "description3", "#{simplePageBean.description}"); UIInput.make(form, "mimetype4", "#{simplePageBean.mimetype}"); FilePickerViewParameters fileparams = new FilePickerViewParameters(); fileparams.setSender(currentPage.getPageId()); fileparams.setResourceType(true); fileparams.viewID = ResourcePickerProducer.VIEW_ID; UIInternalLink.make(form, "change-resource-movie", messageLocator.getMessage("simplepage.change_resource"), fileparams); UIBoundBoolean.make(form, "question-prerequisite", "#{simplePageBean.prerequisite}",false); UICommand.make(form, "delete-movie-item", messageLocator.getMessage("simplepage.delete"), "#{simplePageBean.deleteItem}"); UICommand.make(form, "update-movie", messageLocator.getMessage("simplepage.edit"), "#{simplePageBean.updateMovie}"); UICommand.make(form, "movie-cancel", messageLocator.getMessage("simplepage.cancel"), null); } private void createEditTitleDialog(UIContainer tofill, SimplePage page, SimplePageItem pageItem) { UIOutput.make(tofill, "edit-title-dialog").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.editTitle"))); UIForm form = UIForm.make(tofill, "title-form"); UIOutput.make(form, "pageTitleLabel", messageLocator.getMessage("simplepage.pageTitle_label")); UIInput.make(form, "pageTitle", "#{simplePageBean.pageTitle}"); if (page.getOwner() == null) { UIOutput.make(tofill, "hideContainer"); UIBoundBoolean.make(form, "hide", "#{simplePageBean.hidePage}", (page.isHidden())); Date releaseDate = page.getReleaseDate(); UIBoundBoolean.make(form, "page-releasedate", "#{simplePageBean.hasReleaseDate}", (releaseDate != null)); if (releaseDate == null) { releaseDate = new Date(); } simplePageBean.setReleaseDate(releaseDate); UIInput releaseForm = UIInput.make(form, "releaseDate:", "simplePageBean.releaseDate"); dateevolver.setStyle(FormatAwareDateInputEvolver.DATE_TIME_INPUT); dateevolver.evolveDateInput(releaseForm, page.getReleaseDate()); if (pageItem.getPageId() == 0) { UIOutput.make(form, "prereqContainer"); UIBoundBoolean.make(form, "page-required", "#{simplePageBean.required}", (pageItem.isRequired())); UIBoundBoolean.make(form, "page-prerequisites", "#{simplePageBean.prerequisite}", (pageItem.isPrerequisite())); } } UIOutput gradeBook = UIOutput.make(form, "gradeBookDiv"); if(page.getOwner() != null) gradeBook.decorate(new UIStyleDecorator("noDisplay")); UIOutput.make(form, "page-gradebook"); Double points = page.getGradebookPoints(); String pointString = ""; if (points != null) { pointString = points.toString(); } if(page.getOwner() == null) { UIOutput.make(form, "csssection"); ArrayList<ContentResource> sheets = simplePageBean.getAvailableCss(); String[] options = new String[sheets.size()+2]; String[] labels = new String[sheets.size()+2]; // Sets up the CSS arrays options[0] = null; labels[0] = messageLocator.getMessage("simplepage.default-css"); options[1] = null; labels[1] = "----------"; for(int i = 0; i < sheets.size(); i++) { if(sheets.get(i) != null) { options[i+2] = sheets.get(i).getId(); labels[i+2] = sheets.get(i).getProperties().getProperty(ResourceProperties.PROP_DISPLAY_NAME); }else { // We show just one un-named separator if there are only site css, or system css, but not both. // If we get here, it means we have both, so we name them. options[i+2] = null; labels[i+2] = "---" + messageLocator.getMessage("simplepage.system") + "---"; labels[1] = "---" + messageLocator.getMessage("simplepage.site") + "---"; } } UIOutput.make(form, "cssDropdownLabel", messageLocator.getMessage("simplepage.css-dropdown-label")); UISelect.make(form, "cssDropdown", options, labels, "#{simplePageBean.dropDown}", page.getCssSheet()); UIOutput.make(form, "cssDefaultInstructions", messageLocator.getMessage("simplepage.css-default-instructions")); UIOutput.make(form, "cssUploadLabel", messageLocator.getMessage("simplepage.css-upload-label")); UIOutput.make(form, "cssUpload"); } UIInput.make(form, "page-points", "#{simplePageBean.points}", pointString); UICommand.make(form, "create-title", messageLocator.getMessage("simplepage.save"), "#{simplePageBean.editTitle}"); UICommand.make(form, "cancel-title", messageLocator.getMessage("simplepage.cancel"), "#{simplePageBean.cancel}"); } private void createNewPageDialog(UIContainer tofill, SimplePage page, SimplePageItem pageItem) { UIOutput.make(tofill, "new-page-dialog").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.new-page"))); UIForm form = UIForm.make(tofill, "new-page-form"); UIInput.make(form, "newPage", "#{simplePageBean.newPageTitle}"); UIInput.make(form, "new-page-number", "#{simplePageBean.numberOfPages}"); UIBoundBoolean.make(form, "new-page-copy", "#{simplePageBean.copyPage}", false); GeneralViewParameters view = new GeneralViewParameters(PagePickerProducer.VIEW_ID); view.setSendingPage(-1L); view.newTopLevel = true; UIInternalLink.make(tofill, "new-page-choose", messageLocator.getMessage("simplepage.lm_existing_page"), view); UICommand.make(form, "new-page-submit", messageLocator.getMessage("simplepage.save"), "#{simplePageBean.addPages}"); UICommand.make(form, "new-page-cancel", messageLocator.getMessage("simplepage.cancel"), "#{simplePageBean.cancel}"); } private void createRemovePageDialog(UIContainer tofill, SimplePage page, SimplePageItem pageItem) { UIOutput.make(tofill, "remove-page-dialog").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.remove-page"))); UIOutput.make(tofill, "remove-page-explanation", (page.getOwner() == null ? messageLocator.getMessage("simplepage.remove-page-explanation") : messageLocator.getMessage("simplepage.remove-student-page-explanation"))); UIForm form = UIForm.make(tofill, "remove-page-form"); form.addParameter(new UIELBinding("#{simplePageBean.removeId}", page.getPageId())); // if (page.getOwner() == null) { // // top level normal page. Use the remove page producer, which can handle removing tools out from under RSF // GeneralViewParameters params = new GeneralViewParameters(RemovePageProducer.VIEW_ID); // UIInternalLink.make(form, "remove-page-submit", "", params).decorate(new UIFreeAttributeDecorator("value", messageLocator.getMessage("simplepage.remove"))); // } else // // a student top level page. call remove page directly, as it will just return to show page // UICommand.make(form, "remove-page-submit", messageLocator.getMessage("simplepage.remove"), "#{simplePageBean.removePage}"); UIComponent button = UICommand.make(form, "remove-page-submit", messageLocator.getMessage("simplepage.remove"), "#{simplePageBean.removePage}"); if (page.getOwner() == null) // not student page button.decorate(new UIFreeAttributeDecorator("onclick", "window.location='/lessonbuilder-tool/removePage?site=" + simplePageBean.getCurrentSiteId() + "&page=" + page.getPageId() + "';return false;")); UICommand.make(form, "remove-page-cancel", messageLocator.getMessage("simplepage.cancel"), "#{simplePageBean.cancel}"); } private void createCommentsDialog(UIContainer tofill) { UIOutput.make(tofill, "comments-dialog").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.edit_commentslink"))); UIForm form = UIForm.make(tofill, "comments-form"); UIInput.make(form, "commentsEditId", "#{simplePageBean.itemId}"); UIBoundBoolean.make(form, "comments-anonymous", "#{simplePageBean.anonymous}"); UIBoundBoolean.make(form, "comments-graded", "#{simplePageBean.graded}"); UIInput.make(form, "comments-max", "#{simplePageBean.maxPoints}"); UIBoundBoolean.make(form, "comments-required", "#{simplePageBean.required}"); UIBoundBoolean.make(form, "comments-prerequisite", "#{simplePageBean.prerequisite}"); UICommand.make(form, "delete-comments-item", messageLocator.getMessage("simplepage.delete"), "#{simplePageBean.deleteItem}"); UICommand.make(form, "update-comments", messageLocator.getMessage("simplepage.edit"), "#{simplePageBean.updateComments}"); UICommand.make(form, "cancel-comments", messageLocator.getMessage("simplepage.cancel"), null); } private void createStudentContentDialog(UIContainer tofill, SimplePage currentPage) { UIOutput.make(tofill, "student-dialog").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.edit_studentlink"))); UIForm form = UIForm.make(tofill, "student-form"); UIInput.make(form, "studentEditId", "#{simplePageBean.itemId}"); UIBoundBoolean.make(form, "student-anonymous", "#{simplePageBean.anonymous}"); UIBoundBoolean.make(form, "student-comments", "#{simplePageBean.comments}"); UIBoundBoolean.make(form, "student-comments-anon", "#{simplePageBean.forcedAnon}"); UIBoundBoolean.make(form, "student-required", "#{simplePageBean.required}"); UIBoundBoolean.make(form, "student-prerequisite", "#{simplePageBean.prerequisite}"); UIOutput.make(form, "peer-evaluation-creation"); UIBoundBoolean.make(form, "peer-eval-check", "#{simplePageBean.peerEval}"); UIInput.make(form, "peer-eval-input-title", "#{simplePageBean.rubricTitle}"); UIInput.make(form, "peer-eval-input-row", "#{simplePageBean.rubricRow}"); UIOutput.make(form, "peer_eval_open_date_label", messageLocator.getMessage("simplepage.peer-eval.open_date")); UIInput openDateField = UIInput.make(form, "peer_eval_open_date:", "#{simplePageBean.peerEvalOpenDate}"); dateevolver.setStyle(FormatAwareDateInputEvolver.DATE_TIME_INPUT); dateevolver.evolveDateInput(openDateField); UIOutput.make(form, "peer_eval_due_date_label", messageLocator.getMessage("simplepage.peer-eval.due_date")); UIInput dueDateField = UIInput.make(form, "peer_eval_due_date:", "#{simplePageBean.peerEvalDueDate}"); dateevolver.setStyle(FormatAwareDateInputEvolver.DATE_TIME_INPUT); dateevolver.evolveDateInput(dueDateField); UIBoundBoolean.make(form, "peer-eval-allow-selfgrade", "#{simplePageBean.peerEvalAllowSelfGrade}"); UIBoundBoolean.make(form, "student-graded", "#{simplePageBean.graded}"); UIInput.make(form, "student-max", "#{simplePageBean.maxPoints}"); UIBoundBoolean.make(form, "student-comments-graded", "#{simplePageBean.sGraded}"); UIInput.make(form, "student-comments-max", "#{simplePageBean.sMaxPoints}"); UIBoundBoolean.make(form, "student-group-owned", "#{simplePageBean.groupOwned}"); createGroupList(form, null, "student-", "#{simplePageBean.studentSelectedGroups}"); UICommand.make(form, "delete-student-item", messageLocator.getMessage("simplepage.delete"), "#{simplePageBean.deleteItem}"); UICommand.make(form, "update-student", messageLocator.getMessage("simplepage.edit"), "#{simplePageBean.updateStudent}"); UICommand.make(form, "cancel-student", messageLocator.getMessage("simplepage.cancel"), null); // RU Rubrics UIOutput.make(tofill, "peer-eval-create-dialog").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.peer-eval-create-title"))); } private void createQuestionDialog(UIContainer tofill, SimplePage currentPage) { UIOutput.make(tofill, "question-dialog").decorate(new UIFreeAttributeDecorator("title", messageLocator.getMessage("simplepage.edit_questionlink"))); UIForm form = UIForm.make(tofill, "question-form"); UISelect questionType = UISelect.make(form, "question-select", new String[] {"multipleChoice", "shortanswer"}, "#{simplePageBean.questionType}", ""); UISelectChoice.make(form, "multipleChoiceSelect", questionType.getFullID(), 0); UISelectChoice.make(form, "shortanswerSelect", questionType.getFullID(), 1); UIOutput.make(form, "question-shortans-del").decorate(new UIFreeAttributeDecorator("alt", messageLocator.getMessage("simplepage.delete"))); UIOutput.make(form, "question-mc-del").decorate(new UIFreeAttributeDecorator("alt", messageLocator.getMessage("simplepage.delete"))); UIInput.make(form, "questionEditId", "#{simplePageBean.itemId}"); UIBoundBoolean.make(form, "question-required", "#{simplePageBean.required}"); UIBoundBoolean.make(form, "question-prerequisite", "#{simplePageBean.prerequisite}"); UIInput.make(form, "question-text-input", "#{simplePageBean.questionText}"); UIInput.make(form, "question-answer-full-shortanswer", "#{simplePageBean.questionAnswer}"); UIBoundBoolean.make(form, "question-graded", "#{simplePageBean.graded}"); UIInput.make(form, "question-gradebook-title", "#{simplePageBean.gradebookTitle}"); UIInput.make(form, "question-max", "#{simplePageBean.maxPoints}"); UIInput.make(form, "question-multiplechoice-answer-complete", "#{simplePageBean.addAnswerData}"); UIInput.make(form, "question-multiplechoice-answer-id", null); UIBoundBoolean.make(form, "question-multiplechoice-answer-correct"); UIInput.make(form, "question-multiplechoice-answer", null); UIBoundBoolean.make(form, "question-show-poll", "#{simplePageBean.questionShowPoll}"); UIInput.make(form, "question-correct-text", "#{simplePageBean.questionCorrectText}"); UIInput.make(form, "question-incorrect-text", "#{simplePageBean.questionIncorrectText}"); UICommand.make(form, "delete-question-item", messageLocator.getMessage("simplepage.delete"), "#{simplePageBean.deleteItem}"); UICommand.make(form, "update-question", messageLocator.getMessage("simplepage.edit"), "#{simplePageBean.updateQuestion}"); UICommand.make(form, "cancel-question", messageLocator.getMessage("simplepage.cancel"), null); } /* * return true if the item is required and not completed, i.e. if we need to * update the status after the user views the item */ private Status handleStatusImage(UIContainer container, SimplePageItem i) { if (i.getType() != SimplePageItem.TEXT && i.getType() != SimplePageItem.MULTIMEDIA) { if (!i.isRequired()) { addStatusImage(Status.NOT_REQUIRED, container, "status", i.getName()); return Status.NOT_REQUIRED; } else if (simplePageBean.isItemComplete(i)) { addStatusImage(Status.COMPLETED, container, "status", i.getName()); return Status.COMPLETED; } else { addStatusImage(Status.REQUIRED, container, "status", i.getName()); return Status.REQUIRED; } } return Status.NOT_REQUIRED; } /** * Returns a Status object with the status of a user's response to a question. * For showing status images next to the question. */ private Status getQuestionStatus(SimplePageItem question, SimplePageQuestionResponse response) { String questionType = question.getAttribute("questionType"); boolean noSpecifiedAnswers = false; boolean manuallyGraded = false; if ("multipleChoice".equals(questionType) && !simplePageToolDao.hasCorrectAnswer(question)) noSpecifiedAnswers = true; else if ("shortanswer".equals(questionType) && "".equals(question.getAttribute("questionAnswer"))) noSpecifiedAnswers = true; if (noSpecifiedAnswers && "true".equals(question.getAttribute("questionGraded"))) manuallyGraded = true; if (noSpecifiedAnswers && !manuallyGraded) { // poll. should we show completed if not required? Don't for // other item types, but here there's no separate tool where you // can look at the status. I'm currently showing completed, to // be consistent with non-polls, where I always show a result if (response != null) return Status.COMPLETED; if(question.isRequired()) return Status.REQUIRED; return Status.NOT_REQUIRED; } if (manuallyGraded && (response != null && !response.isOverridden())) { return Status.NEEDSGRADING; } else if (response != null && response.isCorrect()) { return Status.COMPLETED; } else if (response != null && !response.isCorrect()) { return Status.FAILED; }else if(question.isRequired()) { return Status.REQUIRED; }else { return Status.NOT_REQUIRED; } } String getStatusNote(Status status) { if (status == Status.COMPLETED) return messageLocator.getMessage("simplepage.status.completed"); else if (status == Status.REQUIRED) return messageLocator.getMessage("simplepage.status.required"); else if (status == Status.NEEDSGRADING) return messageLocator.getMessage("simplepage.status.needsgrading"); else if (status == Status.FAILED) return messageLocator.getMessage("simplepage.status.failed"); else return null; } // add the checkmark or asterisk. This code supports a couple of other // statuses that we // never ended up using private void addStatusImage(Status status, UIContainer container, String imageId, String name) { String imagePath = "/lessonbuilder-tool/images/"; String imageAlt = ""; // better not to include alt or title. Bundle them with the link. Avoids // complexity for screen reader if (status == Status.COMPLETED) { imagePath += "checkmark.png"; imageAlt = ""; // messageLocator.getMessage("simplepage.status.completed") // + " " + name; } else if (status == Status.DISABLED) { imagePath += "unavailable.png"; imageAlt = ""; // messageLocator.getMessage("simplepage.status.disabled") // + " " + name; } else if (status == Status.FAILED) { imagePath += "failed.png"; imageAlt = ""; // messageLocator.getMessage("simplepage.status.failed") // + " " + name; } else if (status == Status.REQUIRED) { imagePath += "available.png"; imageAlt = ""; // messageLocator.getMessage("simplepage.status.required") // + " " + name; } else if (status == Status.NEEDSGRADING) { imagePath += "blue-question.png"; imageAlt = ""; // messageLocator.getMessage("simplepage.status.required") // + " " + name; } else if (status == Status.NOT_REQUIRED) { imagePath += "not-required.png"; // it's a blank image, no need for screen readers to say anything imageAlt = ""; // messageLocator.getMessage("simplepage.status.notrequired"); } UIOutput.make(container, "status-td"); UIOutput.make(container, imageId).decorate(new UIFreeAttributeDecorator("src", imagePath)) .decorate(new UIFreeAttributeDecorator("alt", imageAlt)).decorate(new UITooltipDecorator(imageAlt)); } private String getLocalizedURL(String fileName) { if (fileName == null || fileName.trim().length() == 0) return fileName; else { fileName = fileName.trim(); } Locale locale = new ResourceLoader().getLocale(); String helploc = ServerConfigurationService.getString("lessonbuilder.helpfolder", null); // we need to test the localized URL and return the initial one if it // doesn't exists // defaultPath will be the one to use if the localized one doesn't exist String defaultPath = null; // this is the part up to where we add the locale String prefix = null; // this is the part after the locale String suffix = null; // this is an additional prefix needed to make a full URL, for testing String testPrefix = null; int suffixIndex = fileName.lastIndexOf("."); if (suffixIndex >= 0) { prefix = fileName.substring(0, suffixIndex); suffix = fileName.substring(suffixIndex); } else { prefix = fileName; suffix = ""; } // if user specified, we make up an absolute URL // otherwise use one relative to the servlet context if (helploc != null) { // user has specified a base URL. Will be absolute, but may not have // http and hostname defaultPath = helploc + fileName; prefix = helploc + prefix; if (helploc.startsWith("http:") || helploc.startsWith("https:")) { testPrefix = ""; // absolute, can test as is } else { testPrefix = myUrl(); // relative, need to make absolute } } else { // actual URL will be related to templates defaultPath = "/lessonbuilder-tool/templates/instructions/" + fileName; prefix = "/lessonbuilder-tool/templates/instructions/" + prefix; // but have to test relative to servlet base testPrefix = ""; // urlok will have to remove /lessonbuilder-tool } String[] localeDetails = locale.toString().split("_"); int localeSize = localeDetails.length; String filePath = null; String localizedPath = null; if (localeSize > 2) { localizedPath = prefix + "_" + locale.toString() + suffix; filePath = testPrefix + localizedPath; if (UrlOk(filePath)) return localizedPath; } if (localeSize > 1) { localizedPath = prefix + "_" + locale.getLanguage() + "_" + locale.getCountry() + suffix; filePath = testPrefix + localizedPath; if (UrlOk(filePath)) return localizedPath; } if (localeSize > 0) { localizedPath = prefix + "_" + locale.getLanguage() + suffix; filePath = testPrefix + localizedPath; if (UrlOk(filePath)) return localizedPath; } return defaultPath; } // this can be either a fully specified URL starting with http: or https: // or something relative to the servlet base, e.g. /lessonbuilder-tool/template/instructions/general.html private boolean UrlOk(String url) { Boolean cached = (Boolean) urlCache.get(url); if (cached != null) return (boolean) cached; if (url.startsWith("http:") || url.startsWith("https:")) { // actual URL, check it out try { HttpURLConnection.setFollowRedirects(false); HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection(); con.setRequestMethod("HEAD"); con.setConnectTimeout(30 * 1000); boolean ret = (con.getResponseCode() == HttpURLConnection.HTTP_OK); urlCache.put(url, (Boolean) ret); return ret; } catch (java.net.SocketTimeoutException e) { log.error("Internationalization url lookup timed out for " + url + ": Please check lessonbuilder.helpfolder. It appears that the host specified is not responding."); urlCache.put(url, (Boolean) false); return false; } catch (ProtocolException e) { urlCache.put(url, (Boolean) false); return false; } catch (IOException e) { urlCache.put(url, (Boolean) false); return false; } } else { // remove the leading /lessonbuilder-tool, since getresource is // relative to the top of the servlet int i = url.indexOf("/", 1); url = url.substring(i); try { // inside the war file, check the file system. That avoid issues // with odd deployments behind load balancers, where the user's URL may not // work from one of the front ends if (httpServletRequest.getSession().getServletContext().getResource(url) == null) { urlCache.put(url, (Boolean) false); return false; } else { urlCache.put(url, (Boolean) true); return true; } } catch (Exception e) { // probably malfformed url log.error("Internationalization url lookup failed for " + url + ": " + e); urlCache.put(url, (Boolean) true); return true; } } } private long findMostRecentComment() { List<SimplePageComment> comments = simplePageToolDao.findCommentsOnPageByAuthor(simplePageBean.getCurrentPage().getPageId(), UserDirectoryService.getCurrentUser().getId()); Collections.sort(comments, new Comparator<SimplePageComment>() { public int compare(SimplePageComment c1, SimplePageComment c2) { return c1.getTimePosted().compareTo(c2.getTimePosted()); } }); if (comments.size() > 0) return comments.get(comments.size() - 1).getId(); else return -1; } private boolean printedGradingForm = false; private void printGradingForm(UIContainer tofill) { // Ajax grading form so faculty can grade comments if(!printedGradingForm) { UIForm gradingForm = UIForm.make(tofill, "gradingForm"); gradingForm.viewparams = new SimpleViewParameters(UVBProducer.VIEW_ID); UIInput idInput = UIInput.make(gradingForm, "gradingForm-id", "gradingBean.id"); UIInput jsIdInput = UIInput.make(gradingForm, "gradingForm-jsId", "gradingBean.jsId"); UIInput pointsInput = UIInput.make(gradingForm, "gradingForm-points", "gradingBean.points"); UIInput typeInput = UIInput.make(gradingForm, "gradingForm-type", "gradingBean.type"); UIInitBlock.make(tofill, "gradingForm-init", "initGradingForm", new Object[] {idInput, pointsInput, jsIdInput, typeInput, "gradingBean.results"}); printedGradingForm = true; } } private static String getItemPath(SimplePageItem i) { String itemPath = ""; boolean isURL = false; String pathId = i.getType() == SimplePageItem.MULTIMEDIA ? "path-url":"path-url"; String[] itemPathTokens = i.getSakaiId().split("/"); for(int tokenIndex=3 ; tokenIndex < itemPathTokens.length ; tokenIndex++) { if(isURL) { itemPath+= "/<a target=\"_blank\" href=\"\" class=\"" + URLEncoder.encode(pathId) + "\">" + FormattedText.escapeHtml(itemPathTokens[tokenIndex],false) + "</a>"; isURL = false; } else itemPath+="/" + FormattedText.escapeHtml(itemPathTokens[tokenIndex],false); isURL = itemPathTokens[tokenIndex].equals("urls") ? true: false; } return itemPath; } //Output rubric data for a Student Content box. private String[] makeStudentRubric = {"peer-eval-title-student", "peer-eval-row-student:", "peerReviewIdStudent", "peerReviewTextStudent"}; private String[] makeMaintainRubric = {"peer-eval-title", "peer-eval-row:", "peerReviewId", "peerReviewText"}; private void makePeerRubric(UIContainer parent, SimplePageItem i, String[] peerReviewRsfIds) { //System.out.println("makePeerRubric(): i.getAttributesString() " + i.getAttributeString()); //System.out.println("makePeerRubric(): i.getAttribute(\"rubricTitle\") " + i.getAttribute("rubricTitle")); //System.out.println("makePeerRubric(): i.getJsonAttribute(\"rows\") " + i.getJsonAttribute("rows")); UIOutput.make(parent, peerReviewRsfIds[0], String.valueOf(i.getAttribute("rubricTitle"))); class RubricRow implements Comparable{ public int id; public String text; public RubricRow(int id, String text){ this.id=id; this.text=text;} public int compareTo(Object o){ RubricRow r = (RubricRow)o; if(id==r.id) return 0; if(id>r.id) return 1; return -1; } } ArrayList<RubricRow> rows = new ArrayList<RubricRow>(); List categories = (List) i.getJsonAttribute("rows"); if(categories != null){ for(Object o: categories){ Map cat = (Map)o; rows.add(new RubricRow(Integer.parseInt(String.valueOf(cat.get("id"))), String.valueOf(cat.get("rowText")))); } } //else{System.out.println("This rubric has no rows.");} Collections.sort(rows); for(RubricRow row : rows){ UIBranchContainer peerReviewRows = UIBranchContainer.make(parent, peerReviewRsfIds[1]); UIOutput.make(peerReviewRows, peerReviewRsfIds[2], String.valueOf(row.id)); UIOutput.make(peerReviewRows, peerReviewRsfIds[3], row.text); } } private void makeSamplePeerEval(UIContainer parent) { UIOutput.make(parent, "peer-eval-sample-title", "Sample Peer Evaluation"); UIBranchContainer peerReviewRows = UIBranchContainer.make(parent, "peer-eval-sample-data:"); UIOutput.make(peerReviewRows, "peer-eval-sample-id", "1"); UIOutput.make(peerReviewRows, "peer-eval-sample-text", messageLocator.getMessage("simplepage.peer-eval.sample.1")); peerReviewRows = UIBranchContainer.make(parent, "peer-eval-sample-data:"); UIOutput.make(peerReviewRows, "peer-eval-sample-id", "2"); UIOutput.make(peerReviewRows, "peer-eval-sample-text", messageLocator.getMessage("simplepage.peer-eval.sample.2")); peerReviewRows = UIBranchContainer.make(parent, "peer-eval-sample-data:"); UIOutput.make(peerReviewRows, "peer-eval-sample-id", "3"); UIOutput.make(peerReviewRows, "peer-eval-sample-text", messageLocator.getMessage("simplepage.peer-eval.sample.3")); peerReviewRows = UIBranchContainer.make(parent, "peer-eval-sample-data:"); UIOutput.make(peerReviewRows, "peer-eval-sample-id", "4"); UIOutput.make(peerReviewRows, "peer-eval-sample-text", messageLocator.getMessage("simplepage.peer-eval.sample.4")); } }