/// <summary> /// Render a file attachment. File attachments will have a relativePath of the form: /// .../~RLO/<interactionId>/<attachmentIndex> /// </summary> /// <param name="context"></param> /// <param name="attachmentInfo">The portion of the path to the file after the "/~RLO" portion.</param> /// <exception cref="FileNotFoundException">The requested file attachment can't be found.</exception> private static void RenderFileAttachment(RloRenderContext context, string attachmentInfo) { AIResources.Culture = LocalizationManager.GetCurrentCulture(); // File attachments will have a relativePath of the form: // .../~RLO/<interactionId>/<attachmentIndex> // // If it's a request to view a file attachment, then there will be extension data in the data model of the form: // ms.learningcomponents.fileAttachment.<interactionId>.<attachmentIndex> // find the /~RLO/ portion of the path, so that we can find the beginning of the <interactionId>/<attachmentIndex> // part of the path. // attachmentInfo should be of the form <interactionId>/<attachmentIndex>, so split it into the parts string[] splitAttachmentInfo = attachmentInfo.Split(new char[] { '/' }); LearningDataModel learningDataModel = context.LearningDataModel; //if(learningDataModel.Interactions.Contains(splitAttachmentInfo[0])) if (learningDataModel.InteractionListContains(splitAttachmentInfo[0])) { //Interaction i = learningDataModel.Interactions[splitAttachmentInfo[0]]; Interaction i = learningDataModel.InteractionListElement(splitAttachmentInfo[0]); string fileAttachmentKey = FileAttachmentKey(splitAttachmentInfo[1]); if (i.ExtensionData.ContainsKey(fileAttachmentKey)) { byte[] fileAttachment = (byte[])i.ExtensionData[fileAttachmentKey]; if (context.Response != null) { context.Response.Clear(); } context.SetOutputStreamExtension((string)i.ExtensionData[FileAttachmentExtensionKey(splitAttachmentInfo[1])]); context.WriteFileToResponse(fileAttachment); return; } } // couldn't get the file throw new FileNotFoundException(AIResources.FileAttachmentNotFound); }
/// <summary> /// Render a file attachment. File attachments will have a relativePath of the form: /// .../~RLO/<interactionId>/<attachmentIndex> /// </summary> /// <param name="context"></param> /// <param name="attachmentInfo">The portion of the path to the file after the "/~RLO" portion.</param> /// <exception cref="FileNotFoundException">The requested file attachment can't be found.</exception> private static void RenderFileAttachment(RloRenderContext context, string attachmentInfo) { AIResources.Culture = LocalizationManager.GetCurrentCulture(); // File attachments will have a relativePath of the form: // .../~RLO/<interactionId>/<attachmentIndex> // // If it's a request to view a file attachment, then there will be extension data in the data model of the form: // ms.learningcomponents.fileAttachment.<interactionId>.<attachmentIndex> // find the /~RLO/ portion of the path, so that we can find the beginning of the <interactionId>/<attachmentIndex> // part of the path. // attachmentInfo should be of the form <interactionId>/<attachmentIndex>, so split it into the parts string[] splitAttachmentInfo = attachmentInfo.Split(new char[] { '/' }); LearningDataModel learningDataModel = context.LearningDataModel; //if(learningDataModel.Interactions.Contains(splitAttachmentInfo[0])) if(learningDataModel.InteractionListContains(splitAttachmentInfo[0])) { //Interaction i = learningDataModel.Interactions[splitAttachmentInfo[0]]; Interaction i = learningDataModel.InteractionListElement(splitAttachmentInfo[0]); string fileAttachmentKey = FileAttachmentKey(splitAttachmentInfo[1]); if (i.ExtensionData.ContainsKey(fileAttachmentKey)) { byte[] fileAttachment = (byte[])i.ExtensionData[fileAttachmentKey]; if (context.Response != null) { context.Response.Clear(); } context.SetOutputStreamExtension((string)i.ExtensionData[FileAttachmentExtensionKey(splitAttachmentInfo[1])]); context.WriteFileToResponse(fileAttachment); return; } } // couldn't get the file throw new FileNotFoundException(AIResources.FileAttachmentNotFound); }
/// <summary> /// Render the requested view into the output stream. /// </summary> /// <param name="context">The context within which to render the page.</param> /// <remarks> /// When this method returns the <paramref name="context"/> OutputStream will contain /// the rendered file. /// <p> /// The following methods and properties must be return valid values from /// the <paramref name="context"/>: /// <ul> /// <li>EmbeddedUiResourcePath, must be non-null</li> /// <li>FormElementId</li> /// <li>GetInputStream</li> /// <li>OutputStream</li> /// <li>View</li> /// </ul> /// </p> /// <p> /// Additionally, if the following properties are set, they will be used: /// <ul> /// <li>FormElementAction</li> /// <li>HiddenControls</li> /// <li>ScriptToRender</li> /// </ul> /// </p> /// All other properties on <paramref name="context"/> are ignored. /// </remarks> /// <exception cref="FileNotFoundException">The requested file attachment can't be found.</exception> public override void Render(RloRenderContext context) { AIResources.Culture = LocalizationManager.GetCurrentCulture(); // string is the key (which is AssessmentItem.Id_AssessmentItem.Type) // int is the ordinal (0 based) which is the number of times the key has been processed Dictionary <string, int> assessmentItems = new Dictionary <string, int>(); // The most common case is that the file is in the package Stream inputStream = null; AssessmentItemManager.DataModelContext = context; LearningDataModel learningDataModel = context.LearningDataModel; try { int srcIndex; // represents the index of the "src" attribute on an <img> node. // If this is the first time the page is being rendered, parse the page and determine // the interactions on the page. if (context.View == SessionView.Execute) { if (!GetPageHasBeenVisited(learningDataModel)) { using (inputStream = context.GetInputStream()) { // If the file being requested is the default file for the current activity, if (context.IsResourceEntryPoint) { // find all the assessment items (<IMG> tags that contain the text "mslamrk" as part of the src attribute.) using (HtmlTextReader reader = new HtmlTextReader(inputStream)) { while (reader.Read()) { if (IsAITag(reader, out srcIndex)) { try { AssessmentItem ai = AssessmentItem.Parse(reader.GetAttributeValue(srcIndex)); AssessmentItemRenderer renderer = AssessmentItemManager.GetRenderer(ai); renderer.TryAddToDataModel(); } catch (FormatException) { // skip this one. This is mirrored below in the 2nd pass. } } } } } } SetPageHasBeenVisited(learningDataModel); } } // must get the input stream again since it may not be possible to seek back to the beginning using (inputStream = context.GetInputStream()) { if (context.Response != null) { // Clear the output response context.Response.Clear(); } // If the file being requested is the default file for the current activity, if (context.IsResourceEntryPoint) { if (context.View == SessionView.Execute) { // Set ExitMode to suspend so that when a student exits the activity it is left in a suspended state. // This way if the activity is reactivated, the student's previous answers are intact. learningDataModel.NavigationRequest.ExitMode = ExitMode.Suspended; } DetachableStream detachable = new DetachableStream(context.OutputStream); // Parse through the input stream again, this time rendering into the output as we go. using (StreamWriter writer = new StreamWriter(detachable)) { using (HtmlTextReader reader = new HtmlTextReader(inputStream)) { while (reader.Read()) { if (IsAITag(reader, out srcIndex)) { try { AssessmentItem ai = AssessmentItem.Parse(reader.GetAttributeValue(srcIndex)); AssessmentItemRenderer renderer = AssessmentItemManager.GetRenderer(ai); if (assessmentItems.ContainsKey(ai.RenderKey)) { assessmentItems[ai.RenderKey] += 1; } else { assessmentItems.Add(ai.RenderKey, 0); } writer.Write(renderer.Render(assessmentItems[ai.RenderKey]).ToString()); } catch (FormatException) { // skip this one. This is mirrored above in the 1st pass. } } else { HandleNode(reader, writer); } } } // don't allow closing the StreamWriter to close the context.OutputStream. writer.Flush(); detachable.Detach(); } // set the response type context.SetOutputStreamExtension(Path.GetExtension(context.RelativePath)); } else { // for a non-entry-point file, copy the file directly to the output stream context.WriteFileToResponse(context.RelativePath); } } return; } catch (FileNotFoundException) { // This means the requested file is not in the package. That's not necessarily a problem, since it // may be a request for an attachment. } // We got here because the file is not in the package. In that case, render it if it is a file attachment int beginAttachmentInfo = context.RelativePath.IndexOf("/~RLO/", StringComparison.Ordinal); if (beginAttachmentInfo != -1) { // attachmentInfo should be of the form <interactionId>/<attachmentIndex>, so split it into the parts string attachmentInfo = context.RelativePath.Substring(beginAttachmentInfo + 6); RenderFileAttachment(context, attachmentInfo); } else { // This means the requested file is not in the package, nor is it a request for an attachment. throw new FileNotFoundException(AIResources.FileNotFound); } }
/// <summary> /// Render the requested view into the output stream. /// </summary> /// <param name="context">The context within which to render the page.</param> /// <remarks> /// When this method returns the <paramref name="context"/> OutputStream will contain /// the rendered file. /// <p> /// The following methods and properties must be return valid values from /// the <paramref name="context"/>: /// <ul> /// <li>EmbeddedUiResourcePath, must be non-null</li> /// <li>FormElementId</li> /// <li>GetInputStream</li> /// <li>OutputStream</li> /// <li>View</li> /// </ul> /// </p> /// <p> /// Additionally, if the following properties are set, they will be used: /// <ul> /// <li>FormElementAction</li> /// <li>HiddenControls</li> /// <li>ScriptToRender</li> /// </ul> /// </p> /// All other properties on <paramref name="context"/> are ignored. /// </remarks> /// <exception cref="FileNotFoundException">The requested file attachment can't be found.</exception> public override void Render(RloRenderContext context) { AIResources.Culture = LocalizationManager.GetCurrentCulture(); // string is the key (which is AssessmentItem.Id_AssessmentItem.Type) // int is the ordinal (0 based) which is the number of times the key has been processed Dictionary<string, int> assessmentItems = new Dictionary<string, int>(); // The most common case is that the file is in the package Stream inputStream = null; AssessmentItemManager.DataModelContext = context; LearningDataModel learningDataModel = context.LearningDataModel; try { int srcIndex; // represents the index of the "src" attribute on an <img> node. // If this is the first time the page is being rendered, parse the page and determine // the interactions on the page. if (context.View == SessionView.Execute) { if (!GetPageHasBeenVisited(learningDataModel)) { using (inputStream = context.GetInputStream()) { // If the file being requested is the default file for the current activity, if (context.IsResourceEntryPoint) { // find all the assessment items (<IMG> tags that contain the text "mslamrk" as part of the src attribute.) using (HtmlTextReader reader = new HtmlTextReader(inputStream)) { while (reader.Read()) { if (IsAITag(reader, out srcIndex)) { try { AssessmentItem ai = AssessmentItem.Parse(reader.GetAttributeValue(srcIndex)); AssessmentItemRenderer renderer = AssessmentItemManager.GetRenderer(ai); renderer.TryAddToDataModel(); } catch (FormatException) { // skip this one. This is mirrored below in the 2nd pass. } } } } } } SetPageHasBeenVisited(learningDataModel); } } // must get the input stream again since it may not be possible to seek back to the beginning using (inputStream = context.GetInputStream()) { if (context.Response != null) { // Clear the output response context.Response.Clear(); } // If the file being requested is the default file for the current activity, if (context.IsResourceEntryPoint) { if (context.View == SessionView.Execute) { // Set ExitMode to suspend so that when a student exits the activity it is left in a suspended state. // This way if the activity is reactivated, the student's previous answers are intact. learningDataModel.NavigationRequest.ExitMode = ExitMode.Suspended; } DetachableStream detachable = new DetachableStream(context.OutputStream); // Parse through the input stream again, this time rendering into the output as we go. using (StreamWriter writer = new StreamWriter(detachable)) { using (HtmlTextReader reader = new HtmlTextReader(inputStream)) { while (reader.Read()) { if (IsAITag(reader, out srcIndex)) { try { AssessmentItem ai = AssessmentItem.Parse(reader.GetAttributeValue(srcIndex)); AssessmentItemRenderer renderer = AssessmentItemManager.GetRenderer(ai); if (assessmentItems.ContainsKey(ai.RenderKey)) { assessmentItems[ai.RenderKey] += 1; } else { assessmentItems.Add(ai.RenderKey, 0); } writer.Write(renderer.Render(assessmentItems[ai.RenderKey]).ToString()); } catch (FormatException) { // skip this one. This is mirrored above in the 1st pass. } } else { HandleNode(reader, writer); } } } // don't allow closing the StreamWriter to close the context.OutputStream. writer.Flush(); detachable.Detach(); } // set the response type context.SetOutputStreamExtension(Path.GetExtension(context.RelativePath)); } else { // for a non-entry-point file, copy the file directly to the output stream context.WriteFileToResponse(context.RelativePath); } } return; } catch (FileNotFoundException) { // This means the requested file is not in the package. That's not necessarily a problem, since it // may be a request for an attachment. } // We got here because the file is not in the package. In that case, render it if it is a file attachment int beginAttachmentInfo = context.RelativePath.IndexOf("/~RLO/", StringComparison.Ordinal); if (beginAttachmentInfo != -1) { // attachmentInfo should be of the form <interactionId>/<attachmentIndex>, so split it into the parts string attachmentInfo = context.RelativePath.Substring(beginAttachmentInfo + 6); RenderFileAttachment(context, attachmentInfo); } else { // This means the requested file is not in the package, nor is it a request for an attachment. throw new FileNotFoundException(AIResources.FileNotFound); } }