Beispiel #1
0
        SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]   // 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
            {
                RegisterError = registerError;
                GetErrorInfo = getErrorInfo;
                AppendContentFrameDetails = appendContentFrameDetails;
                GetFramesetMsg = getFramesetMsg;

                m_getSessionTitle = getSessionTitle;
                m_isPostedPage = isPostBack;

                AttemptItemIdentifier attemptId;
                SessionView view;
                LoadContentFrame = true;
                ActivityHasChanged = false;
                
                if (!TryGetViewInfo(false, out view))
                {
                    WriteError(ResHelper.GetMessage(IUDICO.TestingSystem.Localization.getMessage("FRM_ViewNotSupportedMsg")));
                    return;
                }

                switch (view)
                {
                    case SessionView.Execute:
                        {
                            if (!TryGetAttemptInfo(false, out attemptId))
                                return;

                            Session = new StoredLearningSession(view, attemptId, packageStore);
                            if (!ProcessViewRequest(view, Session))
                                return;

                            // If the session has ended, allow the application to deal with it.
                            ProcessSessionEnd(Session, ref m_sessionEndedMsgTitle, ref m_sessionEndedMsg);
                        }
                        break;
                    case SessionView.Review:
                        {
                            if (!TryGetAttemptInfo(false, out attemptId))
                                return;

                            Session = new StoredLearningSession(view, attemptId, packageStore);
                            // Do not set logging options in review view.

                            if (!ProcessViewRequest(view, 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;

                            Session = new StoredLearningSession(view, attemptId, packageStore);
                            // Do not set logging options in random access view.

                            if (!ProcessViewRequest(view, Session))
                                return;

                            // Move to the first activity with a resource.
                            MoveToNextActivity(Session);
                        }
                        break;
                    default:
                        WriteError(ResHelper.GetMessage(IUDICO.TestingSystem.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 && !SessionIsEnded)
                {
                    // Process any datamodel changes before doing any navigation. This does not save any data.
                    ProcessDataModelValues(Request.Form[HiddenFieldNames.DataModel], Request.Form[HiddenFieldNames.ObjectiveIdMap]);


                    // Assume we do not have to reload the content frame
                    ActivityHasChanged = false;
                    LoadContentFrame = false;

                    // if the view requires more data, get it
                    if ((Session.View == SessionView.Review) || (Session.View == SessionView.RandomAccess))
                    {
                        // Get the current activity from the posted data and set the session to that activity
                        string strActivityId = Request.Form[HiddenFieldNames.ActivityId];
                        long activityId;
                        if (String.IsNullOrEmpty(strActivityId)
                                || !long.TryParse(strActivityId, out activityId))
                        {
                            WriteError(ResHelper.GetMessage(IUDICO.TestingSystem.Localization.getMessage("HID_InvalidActivityId"), strActivityId));
                        }
                        else
                        {
                            MoveToActivity(Session, activityId);
                        }
                    }

                    // Find out what the commands are and do them.
                    m_saveOnly = true;
                    ICollection<CommandInfo> commands = GetCommands();
                    foreach (CommandInfo cmdInfo in commands)
                    {
                        switch (cmdInfo.Command)
                        {
                            case Commands.DoNext:
                                {
                                    if (!Session.HasCurrentActivity || !ProcessNavigationRequests(Session))
                                    {
                                        if (Session.IsMoveToNextValid())
                                        {
                                            MoveToNextActivity(Session);
                                            ActivityHasChanged = true;
                                            LoadContentFrame = true;
                                        }
                                    }
                                    else
                                    {
                                        ActivityHasChanged = true;
                                        LoadContentFrame = true;
                                    }

                                    if (!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.
                                        ActivateCurrentActivity();
                                        WriteError(ResHelper.Format(GetFramesetMsg(FramesetStringId.MoveToNextFailedHtml), ThemeFolderPath), true);
                                        
                                    }
                                    m_saveOnly = false;
                                }
                                break;
                            case Commands.DoPrevious:
                                {

                                    if (!Session.HasCurrentActivity || !ProcessNavigationRequests(Session))
                                    {
                                        if (Session.IsMoveToPreviousValid())
                                        {
                                            MoveToPreviousActivity(Session);
                                            ActivityHasChanged = true;
                                            LoadContentFrame = true;
                                        }
                                    }
                                    else
                                    {
                                        ActivityHasChanged = true;
                                        LoadContentFrame = true;
                                    }

                                    if (!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.
                                        ActivateCurrentActivity();
                                        WriteError(ResHelper.Format(GetFramesetMsg(FramesetStringId.MoveToPreviousFailedHtml), ThemeFolderPath), true);
                                    }
                                    m_saveOnly = 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 (IsCurrentActiveActivity(activityId))
                                        {
                                            ActivityHasChanged = true;
                                            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 (!Session.HasCurrentActivity || !ProcessNavigationRequests(Session))
                                            {
                                                if (Session.IsMoveToActivityValid(activityId))
                                                {
                                                    MoveToActivity(Session, activityId);
                                                    ActivityHasChanged = true;
                                                    LoadContentFrame = true;
                                                }
                                            }
                                            else
                                            {
                                                ActivityHasChanged = true;
                                                LoadContentFrame = true;
                                            }
                                        }
                                    }

                                    if (!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.

                                        ActivateCurrentActivity();
                                        WriteError(ResHelper.Format(GetFramesetMsg(FramesetStringId.MoveToActivityFailedHtml), ThemeFolderPath), true);
                                    }
                                    m_saveOnly = 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.
                                        ActivityHasChanged = true;
                                        LoadContentFrame = 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
                                    {
                                        // 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 (!Session.HasCurrentActivity || !ProcessNavigationRequests(Session))
                                            {
                                                if (Session.IsMoveToActivityValid(activityId))
                                                {
                                                    MoveToActivity(Session, activityId);
                                                    ActivityHasChanged = true;
                                                    LoadContentFrame = true;
                                                }
                                            }
                                            else
                                            {
                                                ActivityHasChanged = true;
                                                LoadContentFrame = true;
                                            }
                                        }
                                    }

                                    if (!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.

                                        ActivateCurrentActivity();
                                        WriteError(ResHelper.Format(GetFramesetMsg(FramesetStringId.MoveToActivityFailedHtml), ThemeFolderPath), true);
                                    }
                                    m_saveOnly = false;
                                }
                                break;
                            case Commands.DoIsChoiceValid:
                                {
                                    string activityKey = cmdInfo.CommandData;
                                    bool isValid = false;
                                    if (!String.IsNullOrEmpty(activityKey))
                                    {
                                        isValid = Session.IsMoveToActivityValid(activityKey);
                                    }
                                    m_isNavValidResponse = new IsNavValidResponseData(Commands.DoIsChoiceValid, activityKey, isValid);
                                    m_saveOnly = false;
                                }
                                break;
                            case Commands.DoIsNavigationValid:
                                {
                                    string navCommand = cmdInfo.CommandData;
                                    if (!String.IsNullOrEmpty(navCommand))
                                    {
                                        bool isValid = false;
                                        if (navCommand == Commands.DoNext)
                                        {
                                            isValid = Session.IsMoveToNextValid();
                                        }
                                        else if (navCommand == Commands.DoPrevious)
                                        {
                                            isValid = Session.IsMoveToPreviousValid();
                                        }
                                        m_isNavValidResponse = new IsNavValidResponseData(Commands.DoIsNavigationValid, navCommand, isValid);
                                    }
                                    m_saveOnly = 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 (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 = Session.CurrentActivityId;
                                            int activityAttemptCount = Session.CurrentActivityDataModel.ActivityAttemptCount;

                                            LearningSession session = 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)
                                            {
                                                ActivityHasChanged = true;
                                                LoadContentFrame = true;
                                            }
                                            else if ((session.View == SessionView.Execute) && (!Session.CurrentActivityIsActive)) 
                                            {
                                                // In Execute view, it started as active or would have thrown an exception.
                                                ActivityHasChanged = true;

                                                // do not load content frame, as that causes messages to flash while switching activities
                                                LoadContentFrame = false;   
                                            }
                                            else if (activityBeforeNavigation != session.CurrentActivityId)
                                            {
                                                // The current activity has changed.
                                                ActivityHasChanged = true;
                                                LoadContentFrame = true;
                                            }
                                            else if ((activityBeforeNavigation == session.CurrentActivityId) 
                                                       && (activityAttemptCount != session.CurrentActivityDataModel.ActivityAttemptCount))
                                            {
                                                // The activity has not changed, but the attempt count has.
                                                ActivityHasChanged = true;
                                                LoadContentFrame = true;
                                            }
                                            else
                                            {
                                                // In all other cases, it has not changed
                                                ActivityHasChanged = false;
                                                LoadContentFrame = false;
                                            }
                                        }
                                        else if ((Session.View == SessionView.Review) || (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.
                                            ActivityHasChanged = true;
                                            LoadContentFrame = true;
                                        }
                                    }
                                    catch (SequencingException ex)
                                    {
                                        WriteError(ResHelper.GetMessage(IUDICO.TestingSystem.Localization.getMessage("HID_TerminateFailed"), HttpUtility.HtmlEncode(ex.Message)));
                                    }
                                    m_saveOnly = false;
                                }
                                break;
                            case Commands.DoSubmit:
                                {
                                    // Submit the attempt -- meaning, do an ExitAll
                                    if (Session.View == SessionView.Execute)
                                    {
                                        if (Session.HasCurrentActivity)
                                        {
                                            ProcessNavigationRequests(Session);
                                        }
                                        Session.Exit();
                                    }
                                    else if (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
                                        SessionIsEnded = true;
                                        ProcessSessionEnd(Session, ref m_sessionEndedMsgTitle, ref m_sessionEndedMsg);
                                    }

                                    ActivityHasChanged = true;
                                    LoadContentFrame = true;
                                    m_saveOnly = false;
                                }
                                break;
                        }
                    }
                }
                else
                {
                    // Is this the first page load, as part of frameset? (Some hidden values are only rendered in this case.)
                    string param = Request.QueryString[FramesetQueryParameter.Init];
                    if (!String.IsNullOrEmpty(param))
                    {
                        m_isFramesetInitialization = 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 (!m_saveOnly && !Session.HasCurrentActivity)
                {
                    RegisterError(GetFramesetMsg(FramesetStringId.SelectActivityTitleHtml), 
                                   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 (!SessionIsReadOnly)
                    {
                        // Save all changes
                        Session.CommitChanges();
                    }

                    if (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(Session, ref m_sessionEndedMsgTitle, ref m_sessionEndedMsg);
                    }

                    // finish the transaction
                    scope.Complete();
                }
            
                InitHiddenControlInfo();

                m_pageLoadSuccessful = true;
            }
            catch (ThreadAbortException)
            {
                // Do nothing -- thread is leaving.
                throw;
            }
            catch (Exception)
            {
                m_pageLoadSuccessful = false;
                throw;
            }
        }