SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")] // arguments are validated public void ProcessPageLoad(HttpResponse response, PackageStore packageStore, TryGetViewInfo TryGetViewInfo, TryGetAttemptInfo TryGetAttemptInfo, ProcessViewRequest ProcessViewRequest, RegisterError RegisterError, string submitPageLinkText) { FramesetUtil.ValidateNonNullParameter("packageStore", packageStore); FramesetUtil.ValidateNonNullParameter("TryGetViewInfo", TryGetViewInfo); FramesetUtil.ValidateNonNullParameter("TryGetAttemptInfo", TryGetAttemptInfo); FramesetUtil.ValidateNonNullParameter("RegisterError", RegisterError); m_response = response; m_submitPageLinkText = submitPageLinkText; AttemptItemIdentifier attemptId; SessionView view; if (!TryGetViewInfo(true, out view)) return; // This is an attempt-based session if (!TryGetAttemptInfo(true, out attemptId)) { return; } m_session = new StoredLearningSession(view, new AttemptItemIdentifier(attemptId), packageStore); // If user cannot access the session, then remove the reference to the session. if (!ProcessViewRequest(view, m_session)) m_session = null; }
[SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")] // arguments are validated public void ProcessPageLoad( HttpResponse response, PackageStore packageStore, TryGetViewInfo tryGetViewInfo, TryGetAttemptInfo tryGetAttemptInfo, ProcessViewRequest processViewRequest, RegisterError registerError, string submitPageLinkText) { FramesetUtil.ValidateNonNullParameter("packageStore", packageStore); FramesetUtil.ValidateNonNullParameter("tryGetViewInfo", tryGetViewInfo); FramesetUtil.ValidateNonNullParameter("tryGetAttemptInfo", tryGetAttemptInfo); FramesetUtil.ValidateNonNullParameter("registerError", registerError); this.mResponse = response; this.mSubmitPageLinkText = submitPageLinkText; AttemptItemIdentifier attemptId; SessionView view; if (!tryGetViewInfo(true, out view)) { return; } // This is an attempt-based session if (!tryGetAttemptInfo(true, out attemptId)) { return; } this.mSession = new StoredLearningSession(view, new AttemptItemIdentifier(attemptId), packageStore); // If user cannot access the session, then remove the reference to the session. if (!processViewRequest(view, this.mSession)) { this.mSession = null; } }
[SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")] // parameters are validated public void ProcessPageLoad(PackageStore packageStore, TryGetViewInfo TryGetViewInfo, TryGetAttemptInfo TryGetAttemptInfo, ProcessViewRequest ProcessViewRequest) { // These should never be a problem, however fxcop complains about them. FramesetUtil.ValidateNonNullParameter("TryGetViewInfo", TryGetViewInfo); FramesetUtil.ValidateNonNullParameter("TryGetAttemptInfo", TryGetAttemptInfo); FramesetUtil.ValidateNonNullParameter("ProcessViewRequest", ProcessViewRequest); FramesetUtil.ValidateNonNullParameter("packageStore", packageStore); // Session information that may be required SessionView view; AttemptItemIdentifier attemptId; // not required for all views // Get View information. It determines what else to look for. if (!TryGetViewInfo(true, out view)) { return; } // Based on View, request other information switch (view) { case SessionView.Execute: { // AttemptId is required if (!TryGetAttemptInfo(true, out attemptId)) { return; } // Create the session m_session = new StoredLearningSession(view, attemptId, packageStore); StoredLearningSession slsSession = m_session as StoredLearningSession; if (!ProcessViewRequest(SessionView.Execute, slsSession)) { return; } // If the attempt id appeared valid (that is, it was numeric), but does not represent a valid // attempt, the call to access AttemptStatus on the session will trigger an InvalidOperationException // containing a message for the user that the attempt id was not valid. switch (slsSession.AttemptStatus) { case AttemptStatus.Abandoned: { // Can't do execute view on abandoned sessions. The application should have handled this // in the ProcessViewRequest. return; } case AttemptStatus.Active: { // Check if it's started. If not, try starting it and forcing selection of a current activity. if (!slsSession.HasCurrentActivity) { try { slsSession.Start(false); slsSession.CommitChanges(); } catch (SequencingException) { // Intentionally ignored. This means it was either already started or could not // select an activity. In either case, just let the content frame ask the user to // deal with selecting an activity. } } else { // If the current activity is not active, then it's possible the frameset was removed from the // user and the content suspended the current activity. In that case, we do this little trick // and try suspending all the activities and then resuming them. The resume will simply resume // all the activities between the current activity and the root. Other suspended activities // will not be affected. if (!slsSession.CurrentActivityIsActive) { slsSession.Suspend(); slsSession.Resume(); slsSession.CommitChanges(); } } } break; case AttemptStatus.Completed: { // Can't do execute view on completed sessions. The application should have handled this in the // ProcessViewRequest. return; } case AttemptStatus.Suspended: { // Resume it slsSession.Resume(); slsSession.CommitChanges(); } break; default: break; } } break; case SessionView.RandomAccess: { // AttemptId is required if (!TryGetAttemptInfo(true, out attemptId)) { return; } StoredLearningSession slsSession = new StoredLearningSession(SessionView.RandomAccess, attemptId, packageStore); m_session = slsSession; if (!ProcessViewRequest(SessionView.RandomAccess, slsSession)) { return; } // Move to the first activity with a resource. PostableFrameHelper.MoveToNextActivity(m_session); } break; case SessionView.Review: { // AttemptId is required if (!TryGetAttemptInfo(true, out attemptId)) { return; } // Create the session StoredLearningSession slsSession = new StoredLearningSession(view, attemptId, packageStore); m_session = slsSession; if (!ProcessViewRequest(SessionView.Review, m_session)) { return; } // Access a property. If the user doesn't have permission, this will throw exception if (m_session.HasCurrentActivity) { // This is good. The 'if' statement is here to make compiler happy. } } break; } }
// it's not worth changing this now public void ProcessPageLoad( PackageStore packageStore, bool firstRendering, // frameset is being initialized bool isPostBack, // page was posted TryGetViewInfo tryGetViewInfo, TryGetAttemptInfo tryGetAttemptInfo, TryGetActivityInfo tryGetActivityInfo, GetResourcePath getResourcePath, AppendContentFrameDetails appendContentFrameDetails, UpdateRenderContext updateRenderContext, ProcessPostedData processPostedData, ProcessViewRequest processViewRequest, ProcessPostedDataComplete processPostedDataComplete, RegisterError registerError, GetErrorInfo getErrorInfo, GetFramesetMsg getFramesetMsg) { this.RegisterError = registerError; this.UpdateRenderContext = updateRenderContext; this.GetErrorInfo = getErrorInfo; this.AppendContentFrameDetails = appendContentFrameDetails; this.GetFramesetMsg = getFramesetMsg; this.mIsPostedPage = isPostBack; // If this is the first time the page is being rendered, just show 'please wait' and return. if (firstRendering) { // Initial page rendering, show "please wait" WritePleaseWaitToResponse(this.Response); this.Response.End(); return; } SessionView view; // Get View information. It determines what else to look for. if (!tryGetViewInfo(true, out view)) { return; } // There is something to display, so process the request... AttemptItemIdentifier attemptId; if (!tryGetAttemptInfo(true, out attemptId)) { return; } StoredLearningSession slsSession = new StoredLearningSession(view, attemptId, packageStore); if (!processViewRequest(view, slsSession)) { return; } this.Session = slsSession; if (slsSession.View == SessionView.Execute) { // Check if the session can be executed... if (slsSession.AttemptStatus != AttemptStatus.Active) { this.RegisterError( this.GetFramesetMsg(FramesetStringId.CannotDisplayContentTitle), this.GetFramesetMsg(FramesetStringId.SessionIsNotActiveMsg), false); return; } if (!slsSession.CurrentActivityIsActive) { this.RegisterError( this.GetFramesetMsg(FramesetStringId.SelectActivityTitleHtml), this.GetFramesetMsg(FramesetStringId.SelectActivityMessageHtml), true); return; } } else if (slsSession.View == SessionView.Review) { // Get information about which activity to review, then make that the current activity. long activityId = -1; // make compiler happy if (!tryGetActivityInfo(true, out activityId)) { return; } // Move to the requested activity. Under normal conditions, this should always succeed, since the frameset should be // giving this page a valid activity id. MoveToActivity(slsSession, activityId); } else if (slsSession.View == SessionView.RandomAccess) { // Get information about which activity to edit, then make that the current activity. long activityId = -1; // make compiler happy if (!tryGetActivityInfo(true, out activityId)) { return; } // Move to the requested activity. Under normal conditions, this should always succeed, since the frameset should be // giving this page a valid activity id. MoveToActivity(slsSession, activityId); } if (isPostBack /* && !SessionEnded */) { // Process information from posted content if (!this.SessionIsReadOnly) { HttpFileCollection files = this.Request.Files; int numFiles = files.Count; Dictionary<string, HttpPostedFile> newFileCollection = new Dictionary<string, HttpPostedFile>(numFiles); // Allow the application to process the form data if (!processPostedData(slsSession, this.Request, newFileCollection)) { return; } // Allow MLC to process the form data this.Session.ProcessFormData(this.Request.Form, newFileCollection); // Allow application to do final processing after all posted data is processed. processPostedDataComplete(this.Session); // If there was an error in processing form data, end the processing. This allows, for instance, to // save the data, display an error and not have commands (such as 'move to next activity') executed. if (this.HasError) { this.Session.CommitChanges(); return; } } // Issue with Review view: where to get activity id? From URL or posted data? // Find out what the commands are and do them. ICollection<CommandInfo> commands = this.GetCommands(); foreach (CommandInfo cmdInfo in commands) { switch (cmdInfo.Command) { case Commands.DoNext: { // When leaving a current activity, we must allow navigation requests the SCO has made to be // executed. If that results in changing the current activity, then do not also ask for another // move. if (!ProcessNavigationRequests(this.Session)) { if (this.Session.IsMoveToNextValid()) { MoveToNextActivity(this.Session); this.ActivityHasChanged = true; } } else { this.ActivityHasChanged = true; } if (!this.ActivityHasChanged) { // Moving to the next activity is not valid. this.WriteError( ResHelper.Format( this.GetFramesetMsg(FramesetStringId.MoveToNextFailedHtml), this.ThemeFolderPath), true); } } break; case Commands.DoPrevious: { if (!ProcessNavigationRequests(this.Session)) { if (this.Session.IsMoveToPreviousValid()) { MoveToPreviousActivity(this.Session); this.ActivityHasChanged = true; } } else { this.ActivityHasChanged = true; } if (!this.ActivityHasChanged) { // Moving to the previous activity is not valid. this.WriteError( ResHelper.Format( this.GetFramesetMsg(FramesetStringId.MoveToPreviousFailedHtml), this.ThemeFolderPath), true); } } break; case Commands.DoChoice: case Commands.DoTocChoice: { // These commands are used to navigate to activities, and to navigate to the final 'submit' page. // In SCORM content, these commands do different things. In LRM content (which is what we are // in, since this is the posted page), they have identical effects. // First check whether this is a request for the submit page or an activity. string cmdData = cmdInfo.CommandData; if (string.CompareOrdinal(cmdData, SubmitId) == 0) { // Requesting submit page. Do not change the current activity, but mark it as changed so that // it appears to the user that it has changed. this.ActivityHasChanged = true; string title = this.GetFramesetMsg(FramesetStringId.SubmitPageTitleHtml); string message; string saveBtn = this.GetFramesetMsg(FramesetStringId.SubmitPageSaveButtonHtml); if (this.Session.HasCurrentActivity) { message = this.GetFramesetMsg(FramesetStringId.SubmitPageMessageHtml); } else { message = this.GetFramesetMsg(FramesetStringId.SubmitPageMessageNoCurrentActivityHtml); } this.WriteSubmitPage(title, message, saveBtn); } else { long activityId; if (long.TryParse(cmdData, out activityId)) { // If the requested activity is the current activity, then do not do the navigation. // We skip it because moving to the activity basically exits the current attempt and creates // a new one. That new one also increments the attempt count. If we don't do the move, we // pretend it was done. This will force the content frame to be reloaded with the current // activity. if (this.IsCurrentActiveActivity(activityId)) { this.ActivityHasChanged = true; } else { if (!ProcessNavigationRequests(this.Session)) { if (this.Session.IsMoveToActivityValid(activityId)) { MoveToActivity(this.Session, activityId); this.ActivityHasChanged = true; } } else { this.ActivityHasChanged = true; } } } } if (!this.ActivityHasChanged) { // Moving to the selected activity is not valid. this.WriteError( ResHelper.Format( this.GetFramesetMsg(FramesetStringId.MoveToActivityFailedHtml), this.ThemeFolderPath), true); } } break; case Commands.DoSave: { // Do nothing. The information will be saved since the page was posted. } break; case Commands.DoSubmit: { if (this.Session.View == SessionView.Execute) { ProcessNavigationRequests(this.Session); this.Session.Exit(); } this.ActivityHasChanged = true; } break; } } } // If an error has been registered (and it's not the submit // page rendering), don't attempt to render the content. if (this.HasError && !this.SubmitPageDisplayed) { if (!this.SessionIsReadOnly) { this.Session.CommitChanges(); } return; } // There was no error, so go ahead and render the content from the package. // If the current activity has changed in the processing of this page, then render the current activity's resource. // Otherwise, ask the application which resource to read. if (this.ActivityHasChanged) { // If the activity has changed, we render the content page without rendering the content. The page will then // reload the content frame with the new activity. if (!this.SessionIsReadOnly) { this.Session.CommitChanges(); } } else { // Render the requested file in the current activity. this.RenderPackageContent(getResourcePath()); // If there was no error, end the response. Otherwise, we wait and render the error on the page. if (!this.HasError) { this.Response.End(); } } }
// it's not worth changing this now public void ProcessPageLoad( PackageStore packageStore, GetSessionTitle getSessionTitle, TryGetViewInfo tryGetViewInfo, TryGetAttemptInfo tryGetAttemptInfo, AppendContentFrameDetails appendContentFrameDetails, RegisterError registerError, GetErrorInfo getErrorInfo, ProcessSessionEnd processSessionEnd, ProcessViewRequest processViewRequest, GetFramesetMsg getFramesetMsg, // messages that appear that are unique to different framesets bool isPostBack) { try { this.RegisterError = registerError; this.GetErrorInfo = getErrorInfo; this.AppendContentFrameDetails = appendContentFrameDetails; this.GetFramesetMsg = getFramesetMsg; this.mGetSessionTitle = getSessionTitle; this.mIsPostedPage = isPostBack; AttemptItemIdentifier attemptId; SessionView view; this.LoadContentFrame = true; this.ActivityHasChanged = false; if (!tryGetViewInfo(false, out view)) { this.WriteError( ResHelper.GetMessage(Localization.GetMessage("FRM_ViewNotSupportedMsg"))); return; } switch (view) { case SessionView.Execute: { if (!tryGetAttemptInfo(false, out attemptId)) { return; } this.Session = new StoredLearningSession(view, attemptId, packageStore); if (!processViewRequest(view, this.Session)) { return; } // If the session has ended, allow the application to deal with it. processSessionEnd(this.Session, ref this.mSessionEndedMsgTitle, ref this.mSessionEndedMsg); } break; case SessionView.Review: { if (!tryGetAttemptInfo(false, out attemptId)) { return; } this.Session = new StoredLearningSession(view, attemptId, packageStore); // Do not set logging options in review view. if (!processViewRequest(view, this.Session)) { return; } } break; case SessionView.RandomAccess: { // Note: RandomAccess is not supported in BWP, however that would have been caught before // displaying this frame. if (!tryGetAttemptInfo(false, out attemptId)) { return; } this.Session = new StoredLearningSession(view, attemptId, packageStore); // Do not set logging options in random access view. if (!processViewRequest(view, this.Session)) { return; } // Move to the first activity with a resource. MoveToNextActivity(this.Session); } break; default: this.WriteError( ResHelper.GetMessage( Localization.GetMessage("FRM_ViewNotSupportedMsg"))); return; } // If the page is posted, process posted data. Remember that all posted data should be considered hostile! // Note that if the session has already ended, then none of the posted data is saved or processed. if (isPostBack && !this.SessionIsEnded) { // Process any datamodel changes before doing any navigation. This does not save any data. this.ProcessDataModelValues( this.Request.Form[HiddenFieldNames.DataModel], this.Request.Form[HiddenFieldNames.ObjectiveIdMap]); // Assume we do not have to reload the content frame this.ActivityHasChanged = false; this.LoadContentFrame = false; // if the view requires more data, get it if ((this.Session.View == SessionView.Review) || (this.Session.View == SessionView.RandomAccess)) { // Get the current activity from the posted data and set the session to that activity string strActivityId = this.Request.Form[HiddenFieldNames.ActivityId]; long activityId; if (string.IsNullOrEmpty(strActivityId) || !long.TryParse(strActivityId, out activityId)) { this.WriteError( ResHelper.GetMessage( Localization.GetMessage("HID_InvalidActivityId"), strActivityId)); } else { MoveToActivity(this.Session, activityId); } } // Find out what the commands are and do them. this.mSaveOnly = true; ICollection<CommandInfo> commands = this.GetCommands(); foreach (CommandInfo cmdInfo in commands) { switch (cmdInfo.Command) { case Commands.DoNext: { if (!this.Session.HasCurrentActivity || !ProcessNavigationRequests(this.Session)) { if (this.Session.IsMoveToNextValid()) { MoveToNextActivity(this.Session); this.ActivityHasChanged = true; this.LoadContentFrame = true; } else { this.ActivityHasChanged = true; this.LoadContentFrame = true; continue; } } else { this.ActivityHasChanged = true; this.LoadContentFrame = true; } if (!this.ActivityHasChanged) { // Moving to the next activity is not valid. It's possible that when the current // activity was unloaded, it exited or suspended itself. If that's the case, we // try to reactivate it. Note this causes the attempt count on the activity to // increment, so on "poorly" written content, the user may not see their data // anymore. this.ActivateCurrentActivity(); this.WriteError( ResHelper.Format( this.GetFramesetMsg(FramesetStringId.MoveToNextFailedHtml), this.ThemeFolderPath), true); } this.mSaveOnly = false; } break; case Commands.DoPrevious: { if (!this.Session.HasCurrentActivity || !ProcessNavigationRequests(this.Session)) { if (this.Session.IsMoveToPreviousValid()) { MoveToPreviousActivity(this.Session); this.ActivityHasChanged = true; this.LoadContentFrame = true; } else { this.ActivityHasChanged = true; this.LoadContentFrame = true; continue; } } else { this.ActivityHasChanged = true; this.LoadContentFrame = true; } if (!this.ActivityHasChanged) { // Moving to the previous activity is not valid. It's possible that when the current // activity was unloaded, it exited or suspended itself. If that's the case, we // try to reactivate it. Note this causes the attempt count on the activity to // increment, so on "poorly" written content, the user may not see their data // anymore. this.ActivateCurrentActivity(); this.WriteError( ResHelper.Format( this.GetFramesetMsg(FramesetStringId.MoveToPreviousFailedHtml), this.ThemeFolderPath), true); } this.mSaveOnly = false; } break; case Commands.DoChoice: { // This command is used to navigate to activities, primarily from either within a link in an // error page, or the submit page returning to the current activity. This command does not // create a new attempt if the requested activity is already the current activity. // Requesting to move to a specific activity. The cmdData will include a numeric activity id. string cmdData = cmdInfo.CommandData; long activityId; if (long.TryParse(cmdData, out activityId)) { // If the requested activity is the current activity, then do not do the navigation. // We skip it because moving to the activity basically exits the current attempt and creates // a new one and in this case, that is not the desired behavior. // That new one also increments the attempt count. If we don't do the move, we // pretend it was done. This will force the content frame to be reloaded with the current // activity. if (this.IsCurrentActiveActivity(activityId)) { this.ActivityHasChanged = true; this.LoadContentFrame = true; } else { // If there is no current activity, or if any navigation requests did not // result in a move, then continue with the choice. if (!this.Session.HasCurrentActivity || !ProcessNavigationRequests(this.Session)) { if (this.Session.IsMoveToActivityValid(activityId)) { MoveToActivity(this.Session, activityId); this.ActivityHasChanged = true; this.LoadContentFrame = true; } } else { this.ActivityHasChanged = true; this.LoadContentFrame = true; } } } if (!this.ActivityHasChanged) { // Moving to the selected activity is not valid. It's possible that when the current // activity was unloaded, it exited or suspended itself. If that's the case, we // try to reactivate it. Note this causes the attempt count on the activity to // increment, so on "poorly" written content, the user may not see their data // anymore. this.ActivateCurrentActivity(); this.WriteError( ResHelper.Format( this.GetFramesetMsg(FramesetStringId.MoveToActivityFailedHtml), this.ThemeFolderPath), true); } this.mSaveOnly = false; } break; case Commands.DoTocChoice: { // This command is used to navigate to activities in response to a user selecting a node // in the TOC. In this case, even if the current activity is the activity that is requested, // then a MoveToActivity() is requested. This will cause the attempt count on the activity // to be incremented and the RTE to be reinitialized. This may be a surprise to the user, but // is a requirement of the SCORM 2004 conformance tests. // If the selected page is the submit page (either in Execute or RandomAccess views), then // display the message and don't ask the session to move to a new activity. string cmdData = cmdInfo.CommandData; if (string.CompareOrdinal(cmdData, SubmitId) == 0) { // Requesting submit page. Do not change the current activity, but mark it as changed so that // it appears to the user that it has changed. this.ActivityHasChanged = true; this.LoadContentFrame = true; string title = this.GetFramesetMsg(FramesetStringId.SubmitPageTitleHtml); string message; string saveBtn = this.GetFramesetMsg(FramesetStringId.SubmitPageSaveButtonHtml); if (this.Session.HasCurrentActivity) { message = this.GetFramesetMsg(FramesetStringId.SubmitPageMessageHtml); } else { message = this.GetFramesetMsg( FramesetStringId.SubmitPageMessageNoCurrentActivityHtml); } this.WriteSubmitPage(title, message, saveBtn); } else { // Requesting to move to a specific activity. The cmdData will include a numeric activity id. long activityId; if (long.TryParse(cmdData, out activityId)) { // If there is no current activity, or if any navigation requests did not // result in a move, then continue with the choice. if (!this.Session.HasCurrentActivity || !ProcessNavigationRequests(this.Session)) { if (this.Session.IsMoveToActivityValid(activityId)) { MoveToActivity(this.Session, activityId); this.ActivityHasChanged = true; this.LoadContentFrame = true; } } else { this.ActivityHasChanged = true; this.LoadContentFrame = true; } } } if (!this.ActivityHasChanged) { // Moving to the selected activity is not valid. It's possible that when the current // activity was unloaded, it exited or suspended itself. If that's the case, we // try to reactivate it. Note this causes the attempt count on the activity to // increment, so on "poorly" written content, the user may not see their data // anymore. this.ActivateCurrentActivity(); this.WriteError( ResHelper.Format( this.GetFramesetMsg(FramesetStringId.MoveToActivityFailedHtml), this.ThemeFolderPath), true); } this.mSaveOnly = false; } break; case Commands.DoIsChoiceValid: { string activityKey = cmdInfo.CommandData; bool isValid = false; if (!string.IsNullOrEmpty(activityKey)) { isValid = this.Session.IsMoveToActivityValid(activityKey); } this.mIsNavValidResponse = new IsNavValidResponseData( Commands.DoIsChoiceValid, activityKey, isValid); this.mSaveOnly = false; } break; case Commands.DoIsNavigationValid: { string navCommand = cmdInfo.CommandData; if (!string.IsNullOrEmpty(navCommand)) { bool isValid = false; if (navCommand == Commands.DoNext) { isValid = this.Session.IsMoveToNextValid(); } else if (navCommand == Commands.DoPrevious) { isValid = this.Session.IsMoveToPreviousValid(); } this.mIsNavValidResponse = new IsNavValidResponseData( Commands.DoIsNavigationValid, navCommand, isValid); } this.mSaveOnly = false; } break; case Commands.DoSave: { // Do nothing. The information will be saved since the page was posted. } break; case Commands.DoTerminate: { // end the current activity try { if (this.Session.View == SessionView.Execute) { // Keep track of state before calling ProcessNavigationRequests so that we can // detect if the call changes it in a way that requires reloading the content frame. long activityBeforeNavigation = this.Session.CurrentActivityId; int activityAttemptCount = this.Session.CurrentActivityDataModel.ActivityAttemptCount; LearningSession session = this.Session; if (session.HasCurrentActivity) { session.ProcessNavigationRequests(); } // The activity has changed if... // ... the session now does not have a current activity (since it did before we did the ProcessNavRequests call, OR // ... the session's current activity is not activity anymore (since it was before the call), OR // ... the session's activity id has changed // ... the session's activity id has not changed, but the attempt count has if (!session.HasCurrentActivity) { this.ActivityHasChanged = true; this.LoadContentFrame = true; } else if ((session.View == SessionView.Execute) && (!this.Session.CurrentActivityIsActive)) { // In Execute view, it started as active or would have thrown an exception. this.ActivityHasChanged = true; // do not load content frame, as that causes messages to flash while switching activities this.LoadContentFrame = false; } else if (activityBeforeNavigation != session.CurrentActivityId) { // The current activity has changed. this.ActivityHasChanged = true; this.LoadContentFrame = true; } else if ((activityBeforeNavigation == session.CurrentActivityId) && (activityAttemptCount != session.CurrentActivityDataModel.ActivityAttemptCount)) { // The activity has not changed, but the attempt count has. this.ActivityHasChanged = true; this.LoadContentFrame = true; } else { // In all other cases, it has not changed this.ActivityHasChanged = false; this.LoadContentFrame = false; } } else if ((this.Session.View == SessionView.Review) || (this.Session.View == SessionView.RandomAccess)) { // The activity has changed simply by calling this. This allows the client RTE to reinitialize and behave // as if navigation has happened. this.ActivityHasChanged = true; this.LoadContentFrame = true; } } catch (SequencingException ex) { this.WriteError( ResHelper.GetMessage( Localization.GetMessage("HID_TerminateFailed"), HttpUtility.HtmlEncode(ex.Message))); } this.mSaveOnly = false; } break; case Commands.DoSubmit: { // Submit the attempt -- meaning, do an ExitAll if (this.Session.View == SessionView.Execute) { if (this.Session.HasCurrentActivity) { ProcessNavigationRequests(this.Session); } this.Session.Exit(); } else if (this.Session.View == SessionView.RandomAccess) { // This may also be a request to end grading (for SLK), in which case // we just set a flag, get the right strings to display and call it done this.SessionIsEnded = true; processSessionEnd( this.Session, ref this.mSessionEndedMsgTitle, ref this.mSessionEndedMsg); } this.ActivityHasChanged = true; this.LoadContentFrame = true; this.mSaveOnly = false; } break; } } } else { // Is this the first page load, as part of frameset? (Some hidden values are only rendered in this case.) string param = this.Request.QueryString[FramesetQueryParameter.Init]; if (!string.IsNullOrEmpty(param)) { this.mIsFramesetInitialization = true; } } // If this was not simply a save operation and there is no current activity, then display a message // asking the user to select one. if (!this.mSaveOnly && !this.Session.HasCurrentActivity) { this.RegisterError( this.GetFramesetMsg(FramesetStringId.SelectActivityTitleHtml), this.GetFramesetMsg(FramesetStringId.SelectActivityMessageHtml), true); } // In Execute view, ProcessSessionEnd may write to the database and change state of data related to the attempt. // Therefore, session changes must be written in the same transation as the session end changes. TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.Serializable; using (LearningStoreTransactionScope scope = new LearningStoreTransactionScope(transactionOptions)) { if (!this.SessionIsReadOnly) { // Save all changes this.Session.CommitChanges(); } if (this.Session.View == SessionView.Execute) { // The rollup and/or sequencing process may have changed the state of the attempt. If so, there are some cases // that cannot continue so show an error message. Allow the application to process this, since the messages are // different. processSessionEnd(this.Session, ref this.mSessionEndedMsgTitle, ref this.mSessionEndedMsg); } // finish the transaction scope.Complete(); } this.InitHiddenControlInfo(); this.mPageLoadSuccessful = true; } catch (ThreadAbortException) { // Do nothing -- thread is leaving. throw; } catch (Exception) { this.mPageLoadSuccessful = false; throw; } }
[SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")] // parameters are validated public void ProcessPageLoad(PackageStore packageStore, TryGetViewInfo TryGetViewInfo, TryGetAttemptInfo TryGetAttemptInfo, ProcessViewRequest ProcessViewRequest) { // These should never be a problem, however fxcop complains about them. FramesetUtil.ValidateNonNullParameter("TryGetViewInfo", TryGetViewInfo); FramesetUtil.ValidateNonNullParameter("TryGetAttemptInfo", TryGetAttemptInfo); FramesetUtil.ValidateNonNullParameter("ProcessViewRequest", ProcessViewRequest); FramesetUtil.ValidateNonNullParameter("packageStore", packageStore); // Session information that may be required SessionView view; AttemptItemIdentifier attemptId; // not required for all views // Get View information. It determines what else to look for. if (!TryGetViewInfo(true, out view)) return; // Based on View, request other information switch (view) { case SessionView.Execute: { // AttemptId is required if (!TryGetAttemptInfo(true, out attemptId)) return; // Create the session m_session = new StoredLearningSession(view, attemptId, packageStore); StoredLearningSession slsSession = m_session as StoredLearningSession; if (!ProcessViewRequest(SessionView.Execute, slsSession)) { if (slsSession.AttemptStatus == AttemptStatus.Completed) { } return; } // If the attempt id appeared valid (that is, it was numeric), but does not represent a valid // attempt, the call to access AttemptStatus on the session will trigger an InvalidOperationException // containing a message for the user that the attempt id was not valid. switch (slsSession.AttemptStatus) { case AttemptStatus.Abandoned: { // Can't do execute view on abandoned sessions. The application should have handled this // in the ProcessViewRequest. return; } case AttemptStatus.Active: { // Check if it's started. If not, try starting it and forcing selection of a current activity. if (!slsSession.HasCurrentActivity) { try { slsSession.Start(false); slsSession.CommitChanges(); } catch (SequencingException) { // Intentionally ignored. This means it was either already started or could not // select an activity. In either case, just let the content frame ask the user to // deal with selecting an activity. } } else { // If the current activity is not active, then it's possible the frameset was removed from the // user and the content suspended the current activity. In that case, we do this little trick // and try suspending all the activities and then resuming them. The resume will simply resume // all the activities between the current activity and the root. Other suspended activities // will not be affected. if (!slsSession.CurrentActivityIsActive) { slsSession.Suspend(); slsSession.Resume(); slsSession.CommitChanges(); } } } break; case AttemptStatus.Completed: { // Can't do execute view on completed sessions. The application should have handled this in the // ProcessViewRequest. return; } case AttemptStatus.Suspended: { // Resume it slsSession.Resume(); slsSession.CommitChanges(); } break; default: break; } } break; case SessionView.RandomAccess: { // AttemptId is required if (!TryGetAttemptInfo(true, out attemptId)) return; StoredLearningSession slsSession = new StoredLearningSession(SessionView.RandomAccess, attemptId, packageStore); m_session = slsSession; if (!ProcessViewRequest(SessionView.RandomAccess, slsSession )) return; // Move to the first activity with a resource. PostableFrameHelper.MoveToNextActivity(m_session); } break; case SessionView.Review: { // AttemptId is required if (!TryGetAttemptInfo(true, out attemptId)) return; // Create the session StoredLearningSession slsSession = new StoredLearningSession(view, attemptId, packageStore); m_session = slsSession; if (!ProcessViewRequest(SessionView.Review, m_session)) return; // Access a property. If the user doesn't have permission, this will throw exception if (m_session.HasCurrentActivity) { // This is good. The 'if' statement is here to make compiler happy. } } break; } }
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] // it's not worth changing this now public void ProcessPageLoad(PackageStore packageStore, bool firstRendering, // frameset is being initialized bool isPostBack, // page was posted TryGetViewInfo TryGetViewInfo, TryGetAttemptInfo TryGetAttemptInfo, TryGetActivityInfo TryGetActivityInfo, GetResourcePath GetResourcePath, AppendContentFrameDetails appendContentFrameDetails, UpdateRenderContext updateRenderContext, ProcessPostedData ProcessPostedData, ProcessViewRequest ProcessViewRequest, ProcessPostedDataComplete ProcessPostedDataComplete, RegisterError registerError, GetErrorInfo getErrorInfo, GetFramesetMsg getFramesetMsg) { RegisterError = registerError; UpdateRenderContext = updateRenderContext; GetErrorInfo = getErrorInfo; AppendContentFrameDetails = appendContentFrameDetails; GetFramesetMsg = getFramesetMsg; m_isPostedPage = isPostBack; // If this is the first time the page is being rendered, just show 'please wait' and return. if (firstRendering) { // Initial page rendering, show "please wait" WritePleaseWaitToResponse(Response); Response.End(); return; } SessionView view; // Get View information. It determines what else to look for. if (!TryGetViewInfo(true, out view)) { return; } // There is something to display, so process the request... AttemptItemIdentifier attemptId; if (!TryGetAttemptInfo(true, out attemptId)) { return; } StoredLearningSession slsSession = new StoredLearningSession(view, attemptId, packageStore); if (!ProcessViewRequest(view, slsSession)) { return; } Session = slsSession; if (slsSession.View == SessionView.Execute) { // Check if the session can be executed... if (slsSession.AttemptStatus != AttemptStatus.Active) { RegisterError(GetFramesetMsg(FramesetStringId.CannotDisplayContentTitle), GetFramesetMsg(FramesetStringId.SessionIsNotActiveMsg), false); return; } if (!slsSession.CurrentActivityIsActive) { RegisterError(GetFramesetMsg(FramesetStringId.SelectActivityTitleHtml), GetFramesetMsg(FramesetStringId.SelectActivityMessageHtml), true); return; } } else if (slsSession.View == SessionView.Review) { // Get information about which activity to review, then make that the current activity. long activityId = -1; // make compiler happy if (!TryGetActivityInfo(true, out activityId)) { return; } // Move to the requested activity. Under normal conditions, this should always succeed, since the frameset should be // giving this page a valid activity id. MoveToActivity(slsSession, activityId); } else if (slsSession.View == SessionView.RandomAccess) { // Get information about which activity to edit, then make that the current activity. long activityId = -1; // make compiler happy if (!TryGetActivityInfo(true, out activityId)) { return; } // Move to the requested activity. Under normal conditions, this should always succeed, since the frameset should be // giving this page a valid activity id. MoveToActivity(slsSession, activityId); } if (isPostBack /* && !SessionEnded */) { //Process information from posted content if (!this.SessionIsReadOnly) { HttpFileCollection files = Request.Files; int numFiles = files.Count; Dictionary <string, HttpPostedFile> newFileCollection = new Dictionary <string, HttpPostedFile>(numFiles); // Allow the application to process the form data if (!ProcessPostedData(slsSession, Request, newFileCollection)) { return; } // Allow MLC to process the form data Session.ProcessFormData(Request.Form, newFileCollection); // Allow application to do final processing after all posted data is processed. ProcessPostedDataComplete(Session); // If there was an error in processing form data, end the processing. This allows, for instance, to // save the data, display an error and not have commands (such as 'move to next activity') executed. if (HasError) { Session.CommitChanges(); return; } } // Issue with Review view: where to get activity id? From URL or posted data? // Find out what the commands are and do them. ICollection <CommandInfo> commands = GetCommands(); foreach (CommandInfo cmdInfo in commands) { switch (cmdInfo.Command) { case Commands.DoNext: { // When leaving a current activity, we must allow navigation requests the SCO has made to be // executed. If that results in changing the current activity, then do not also ask for another // move. if (!ProcessNavigationRequests(Session)) { if (Session.IsMoveToNextValid()) { MoveToNextActivity(Session); ActivityHasChanged = true; } } else { ActivityHasChanged = true; } if (!ActivityHasChanged) { // Moving to the next activity is not valid. WriteError(ResHelper.Format(GetFramesetMsg(FramesetStringId.MoveToNextFailedHtml), ThemeFolderPath), true); } } break; case Commands.DoPrevious: { if (!ProcessNavigationRequests(Session)) { if (Session.IsMoveToPreviousValid()) { MoveToPreviousActivity(Session); ActivityHasChanged = true; } } else { ActivityHasChanged = true; } if (!ActivityHasChanged) { // Moving to the previous activity is not valid. WriteError(ResHelper.Format(GetFramesetMsg(FramesetStringId.MoveToPreviousFailedHtml), ThemeFolderPath), true); } } break; case Commands.DoChoice: case Commands.DoTocChoice: { // These commands are used to navigate to activities, and to navigate to the final 'submit' page. // In SCORM content, these commands do different things. In LRM content (which is what we are // in, since this is the posted page), they have identical effects. // First check whether this is a request for the submit page or an activity. string cmdData = cmdInfo.CommandData; if (String.CompareOrdinal(cmdData, SubmitId) == 0) { // Requesting submit page. Do not change the current activity, but mark it as changed so that // it appears to the user that it has changed. ActivityHasChanged = true; string title = GetFramesetMsg(FramesetStringId.SubmitPageTitleHtml); string message; string saveBtn = GetFramesetMsg(FramesetStringId.SubmitPageSaveButtonHtml); if (Session.HasCurrentActivity) { message = GetFramesetMsg(FramesetStringId.SubmitPageMessageHtml); } else { message = GetFramesetMsg(FramesetStringId.SubmitPageMessageNoCurrentActivityHtml); } WriteSubmitPage(title, message, saveBtn); } else { long activityId; if (long.TryParse(cmdData, out activityId)) { // If the requested activity is the current activity, then do not do the navigation. // We skip it because moving to the activity basically exits the current attempt and creates // a new one. That new one also increments the attempt count. If we don't do the move, we // pretend it was done. This will force the content frame to be reloaded with the current // activity. if (IsCurrentActiveActivity(activityId)) { ActivityHasChanged = true; } else { if (!ProcessNavigationRequests(Session)) { if (Session.IsMoveToActivityValid(activityId)) { MoveToActivity(Session, activityId); ActivityHasChanged = true; } } else { ActivityHasChanged = true; } } } } if (!ActivityHasChanged) { // Moving to the selected activity is not valid. WriteError(ResHelper.Format(GetFramesetMsg(FramesetStringId.MoveToActivityFailedHtml), ThemeFolderPath), true); } } break; case Commands.DoSave: { // Do nothing. The information will be saved since the page was posted. } break; case Commands.DoSubmit: { if (Session.View == SessionView.Execute) { ProcessNavigationRequests(Session); Session.Exit(); } ActivityHasChanged = true; } break; } } } // If an error has been registered (and it's not the submit // page rendering), don't attempt to render the content. if (HasError && !SubmitPageDisplayed) { if (!SessionIsReadOnly) { Session.CommitChanges(); } return; } // There was no error, so go ahead and render the content from the package. // If the current activity has changed in the processing of this page, then render the current activity's resource. // Otherwise, ask the application which resource to read. if (ActivityHasChanged) { // If the activity has changed, we render the content page without rendering the content. The page will then // reload the content frame with the new activity. if (!SessionIsReadOnly) { Session.CommitChanges(); } } else { // Render the requested file in the current activity. RenderPackageContent(GetResourcePath()); // If there was no error, end the response. Otherwise, we wait and render the error on the page. if (!HasError) { Response.End(); } } }