Exemple #1
0
        /// <summary>
        /// Job that will close workflows.
        /// 
        /// Called by the <see cref="IScheduler" /> when a
        /// <see cref="ITrigger" /> fires that is associated with
        /// the <see cref="IJob" />.
        /// </summary>
        public virtual void Execute( IJobExecutionContext context )
        {
            JobDataMap dataMap = context.JobDetail.JobDataMap;

            // run a SQL query to do something
            var workflowTypeGuids = dataMap.GetString( "WorkflowTypes" ).Split(',').Select(Guid.Parse).ToList();
            int? expirationAge = dataMap.GetString( "ExpirationAge" ).AsIntegerOrNull();
            string closeStatus = dataMap.GetString( "CloseStatus" );

            RockContext rockContext = new RockContext();
            var workflowService = new WorkflowService( rockContext );

            var qry = workflowService.Queryable()
                        .Where( w => workflowTypeGuids.Contains( w.WorkflowType.Guid ) );

            if ( expirationAge.HasValue )
            {
                var expirationDate = RockDateTime.Now.AddMinutes( expirationAge.Value );

                qry = qry.Where(w => w.CreatedDateTime <= expirationDate );
            }

            var workflows = qry.ToList();

            foreach(var workflow in workflows )
            {
                workflow.MarkComplete();
                workflow.Status = closeStatus;

                rockContext.SaveChanges();
            }
        }
        /// <summary>
        /// Called by the <see cref="IScheduler" /> when a <see cref="ITrigger" />
        /// fires that is associated with the <see cref="IJob" />.
        /// </summary>
        /// <param name="context">The execution context.</param>
        /// <remarks>
        /// The implementation may wish to set a  result object on the
        /// JobExecutionContext before this method exits.  The result itself
        /// is meaningless to Quartz, but may be informative to
        /// <see cref="IJobListener" />s or
        /// <see cref="ITriggerListener" />s that are watching the job's
        /// execution.
        /// </remarks>
        public virtual void Execute( IJobExecutionContext context )
        {
            foreach ( var workflowId in new WorkflowService( new RockContext() ).GetActive().Select(a => a.Id).ToList() )
            {
                // create a new rockContext and service for every workflow to prevent a build-up of Context.ChangeTracker.Entries()
                var rockContext = new RockContext();
                var workflowService = new WorkflowService( rockContext );
                var workflow = workflowService.Queryable( "WorkflowType" ).FirstOrDefault( a => a.Id == workflowId );
                if ( workflow != null )
                {
                    if ( !workflow.LastProcessedDateTime.HasValue ||
                        RockDateTime.Now.Subtract( workflow.LastProcessedDateTime.Value ).TotalSeconds >= ( workflow.WorkflowType.ProcessingIntervalSeconds ?? 0 ) )
                    {
                        var errorMessages = new List<string>();

                        workflowService.Process( workflow, out errorMessages );
                    }
                }
            }
        }
        /// <summary>
        /// Handles the Click event of the btnSave control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
        protected void btnSave_Click( object sender, EventArgs e )
        {
            var rockContext = new RockContext();
            var service = new WorkflowService( rockContext );

            ParseControls( rockContext, true );

            Workflow dbWorkflow = null;

            if ( Workflow != null )
            {
                dbWorkflow = service.Get( Workflow.Id );

                if ( dbWorkflow != null )
                {
                    if ( !dbWorkflow.CompletedDateTime.HasValue && Workflow.CompletedDateTime.HasValue )
                    {
                        dbWorkflow.AddLogEntry( "Workflow Manually Completed." );
                        dbWorkflow.CompletedDateTime = Workflow.CompletedDateTime;
                    }
                    else if ( dbWorkflow.CompletedDateTime.HasValue && !Workflow.CompletedDateTime.HasValue )
                    {
                        dbWorkflow.AddLogEntry( "Workflow Manually Re-Activated." );
                        dbWorkflow.CompletedDateTime = null;
                    }

                    if ( dbWorkflow.Name.Trim() != Workflow.Name.Trim() )
                    {
                        dbWorkflow.AddLogEntry( string.Format( "Workflow name manually changed from '{0}' to '{0}'.", dbWorkflow.Name, tbName.Text ) );
                        dbWorkflow.Name = Workflow.Name;
                    }

                    if ( dbWorkflow.Status.Trim() != Workflow.Status.Trim() )
                    {
                        dbWorkflow.AddLogEntry( string.Format( "Workflow status manually changed from '{0}' to '{0}'.", dbWorkflow.Status, tbStatus.Text ) );
                        dbWorkflow.Status = Workflow.Status;
                    }

                    if ( !dbWorkflow.InitiatorPersonAliasId.Equals(Workflow.InitiatorPersonAliasId))
                    {
                        dbWorkflow.AddLogEntry( string.Format( "Workflow status manually changed from '{0}' to '{0}'.",
                            dbWorkflow.InitiatorPersonAlias != null ? dbWorkflow.InitiatorPersonAlias.Person.FullName : "",
                            Workflow.InitiatorPersonAlias != null ? Workflow.InitiatorPersonAlias.Person.FullName : "" ) );
                        dbWorkflow.InitiatorPersonAlias = Workflow.InitiatorPersonAlias;
                        dbWorkflow.InitiatorPersonAliasId = Workflow.InitiatorPersonAliasId;
                    }

                    if ( !Page.IsValid || !dbWorkflow.IsValid )
                    {
                        return;
                    }

                    foreach ( var activity in Workflow.Activities )
                    {
                        if ( !activity.IsValid )
                        {
                            return;
                        }
                        foreach ( var action in activity.Actions )
                        {
                            if ( !action.IsValid )
                            {
                                return;
                            }
                        }
                    }

                    rockContext.WrapTransaction( () =>
                    {
                        rockContext.SaveChanges();

                        dbWorkflow.LoadAttributes( rockContext );
                        foreach ( var attributeValue in Workflow.AttributeValues )
                        {
                            dbWorkflow.SetAttributeValue( attributeValue.Key, Workflow.GetAttributeValue( attributeValue.Key ) );
                        }
                        dbWorkflow.SaveAttributeValues( rockContext );

                        WorkflowActivityService workflowActivityService = new WorkflowActivityService( rockContext );
                        WorkflowActionService workflowActionService = new WorkflowActionService( rockContext );

                        var activitiesInUi = new List<Guid>();
                        var actionsInUI = new List<Guid>();
                        foreach ( var activity in Workflow.Activities )
                        {
                            activitiesInUi.Add( activity.Guid );
                            foreach ( var action in activity.Actions )
                            {
                                actionsInUI.Add( action.Guid );
                            }
                        }

                        // delete WorkflowActions that were removed in the UI
                        foreach ( var action in workflowActionService.Queryable()
                            .Where( a =>
                                a.Activity.WorkflowId.Equals( dbWorkflow.Id ) &&
                                !actionsInUI.Contains( a.Guid ) ) )
                        {
                            workflowActionService.Delete( action );
                        }

                        // delete WorkflowActivities that aren't assigned in the UI anymore
                        foreach ( var activity in workflowActivityService.Queryable()
                            .Where( a =>
                                a.WorkflowId.Equals( dbWorkflow.Id ) &&
                                !activitiesInUi.Contains( a.Guid ) ) )
                        {
                            workflowActivityService.Delete( activity );
                        }

                        rockContext.SaveChanges();

                        // add or update WorkflowActivities(and Actions) that are assigned in the UI
                        foreach ( var editorWorkflowActivity in Workflow.Activities )
                        {
                            // Add or Update the activity type
                            WorkflowActivity workflowActivity = dbWorkflow.Activities.FirstOrDefault( a => a.Guid.Equals( editorWorkflowActivity.Guid ) );
                            if ( workflowActivity == null )
                            {
                                workflowActivity = new WorkflowActivity();
                                workflowActivity.ActivityTypeId = editorWorkflowActivity.ActivityTypeId;
                                dbWorkflow.Activities.Add( workflowActivity );
                            }

                            workflowActivity.AssignedPersonAliasId = editorWorkflowActivity.AssignedPersonAliasId;
                            workflowActivity.AssignedGroupId = editorWorkflowActivity.AssignedGroupId;
                            workflowActivity.ActivatedDateTime = editorWorkflowActivity.ActivatedDateTime;
                            workflowActivity.LastProcessedDateTime = editorWorkflowActivity.LastProcessedDateTime;

                            if ( !workflowActivity.CompletedDateTime.HasValue && editorWorkflowActivity.CompletedDateTime.HasValue )
                            {
                                workflowActivity.AddLogEntry( "Activity Manually Completed." );
                                workflowActivity.CompletedDateTime = RockDateTime.Now;
                            }
                            if ( workflowActivity.CompletedDateTime.HasValue && !editorWorkflowActivity.CompletedDateTime.HasValue )
                            {
                                workflowActivity.AddLogEntry( "Activity Manually Re-Activated." );
                                workflowActivity.CompletedDateTime = null;
                            }

                            // Save Activity Type
                            rockContext.SaveChanges();

                            // Save ActivityType Attributes
                            workflowActivity.LoadAttributes( rockContext );
                            foreach ( var attributeValue in editorWorkflowActivity.AttributeValues )
                            {
                                workflowActivity.SetAttributeValue( attributeValue.Key, editorWorkflowActivity.GetAttributeValue( attributeValue.Key ) );
                            }
                            workflowActivity.SaveAttributeValues( rockContext );

                            foreach ( var editorWorkflowAction in editorWorkflowActivity.Actions )
                            {
                                WorkflowAction workflowAction = workflowActivity.Actions.FirstOrDefault( a => a.Guid.Equals( editorWorkflowAction.Guid ) );
                                if ( workflowAction == null )
                                {
                                    // New action
                                    workflowAction = new WorkflowAction();
                                    workflowAction.ActionTypeId = editorWorkflowAction.ActionTypeId;
                                    workflowActivity.Actions.Add( workflowAction );
                                }

                                workflowAction.LastProcessedDateTime = editorWorkflowAction.LastProcessedDateTime;
                                workflowAction.FormAction = editorWorkflowAction.FormAction;

                                if ( !workflowAction.CompletedDateTime.HasValue && editorWorkflowAction.CompletedDateTime.HasValue )
                                {
                                    workflowAction.AddLogEntry( "Action Manually Completed." );
                                    workflowAction.CompletedDateTime = RockDateTime.Now;
                                }
                                if ( workflowAction.CompletedDateTime.HasValue && !editorWorkflowAction.CompletedDateTime.HasValue )
                                {
                                    workflowAction.AddLogEntry( "Action Manually Re-Activated." );
                                    workflowAction.CompletedDateTime = null;
                                }
                            }

                            // Save action updates
                            rockContext.SaveChanges();

                        }

                    } );

                }

                Workflow = service
                    .Queryable("WorkflowType,Activities.ActivityType,Activities.Actions.ActionType")
                    .FirstOrDefault( w => w.Id == Workflow.Id );

                var errorMessages = new List<string>();
                service.Process( Workflow, out errorMessages );

            }

            ShowReadonlyDetails();
        }
        /// <summary>
        /// Binds the grid.
        /// </summary>
        private void BindGrid()
        {
            if ( _workflowType != null )
            {
                pnlWorkflowList.Visible = true;

                if ( _workflowType != null )
                {
                    var rockContext = new RockContext();
                    var workflowService = new WorkflowService( rockContext );

                    var qry = workflowService
                        .Queryable( "Activities.ActivityType,InitiatorPersonAlias.Person" ).AsNoTracking()
                        .Where( w => w.WorkflowTypeId.Equals( _workflowType.Id ) );

                    // Activated Date Range Filter
                    if ( drpActivated.LowerValue.HasValue )
                    {
                        qry = qry.Where( w => w.ActivatedDateTime >= drpActivated.LowerValue.Value );
                    }
                    if ( drpActivated.UpperValue.HasValue )
                    {
                        DateTime upperDate = drpActivated.UpperValue.Value.Date.AddDays( 1 );
                        qry = qry.Where( w => w.ActivatedDateTime.Value < upperDate );
                    }

                    // State Filter
                    List<string> states = cblState.SelectedValues;
                    if ( states.Count == 1 )    // Don't filter if none or both options are selected
                    {
                        if ( states[0] == "Active" )
                        {
                            qry = qry.Where( w => !w.CompletedDateTime.HasValue );
                        }
                        else
                        {
                            qry = qry.Where( w => w.CompletedDateTime.HasValue );
                        }
                    }

                    // Completed Date Range Filter
                    if ( drpCompleted.LowerValue.HasValue )
                    {
                        qry = qry.Where( w => w.CompletedDateTime.HasValue && w.CompletedDateTime.Value >= drpCompleted.LowerValue.Value );
                    }
                    if ( drpCompleted.UpperValue.HasValue )
                    {
                        DateTime upperDate = drpCompleted.UpperValue.Value.Date.AddDays( 1 );
                        qry = qry.Where( w => w.CompletedDateTime.HasValue && w.CompletedDateTime.Value < upperDate );
                    }

                    string name = tbName.Text;
                    if ( !string.IsNullOrWhiteSpace( name ) )
                    {
                        qry = qry.Where( w => w.Name.StartsWith( name ) );
                    }

                    int? personId = ppInitiator.SelectedValue;
                    if ( personId.HasValue )
                    {
                        qry = qry.Where( w => w.InitiatorPersonAlias.PersonId == personId.Value );
                    }

                    string status = tbStatus.Text;
                    if ( !string.IsNullOrWhiteSpace( status ) )
                    {
                        qry = qry.Where( w => w.Status.StartsWith( status ) );
                    }

                    // Filter query by any configured attribute filters
                    if ( AvailableAttributes != null && AvailableAttributes.Any() )
                    {
                        var attributeValueService = new AttributeValueService( rockContext );
                        var parameterExpression = attributeValueService.ParameterExpression;

                        foreach ( var attribute in AvailableAttributes )
                        {
                            var filterControl = phAttributeFilters.FindControl( "filter_" + attribute.Id.ToString() );
                            if ( filterControl != null )
                            {
                                var filterValues = attribute.FieldType.Field.GetFilterValues( filterControl, attribute.QualifierValues, Rock.Reporting.FilterMode.SimpleFilter );
                                var expression = attribute.FieldType.Field.AttributeFilterExpression( attribute.QualifierValues, filterValues, parameterExpression );
                                if ( expression != null )
                                {
                                    var attributeValues = attributeValueService
                                        .Queryable()
                                        .Where( v => v.Attribute.Id == attribute.Id );

                                    attributeValues = attributeValues.Where( parameterExpression, expression, null );

                                    qry = qry.Where( w => attributeValues.Select( v => v.EntityId ).Contains( w.Id ) );
                                }
                            }
                        }
                    }

                    IQueryable<Workflow> workflows = null;

                    var sortProperty = gWorkflows.SortProperty;
                    if ( sortProperty != null )
                    {
                        if ( sortProperty.Property == "Initiator" )
                        {
                            if ( sortProperty.Direction == SortDirection.Ascending )
                            {
                                workflows = qry
                                    .OrderBy( w => w.InitiatorPersonAlias.Person.LastName )
                                    .ThenBy( w => w.InitiatorPersonAlias.Person.NickName );
                            }
                            else
                            {
                                workflows = qry
                                    .OrderByDescending( w => w.InitiatorPersonAlias.Person.LastName )
                                    .ThenByDescending( w => w.InitiatorPersonAlias.Person.NickName );
                            }
                        }
                        else
                        {
                            workflows = qry.Sort( sortProperty );
                        }
                    }
                    else
                    {
                        workflows = qry.OrderByDescending( s => s.CreatedDateTime );
                    }

                    // Since we're not binding to actual workflow list, but are using AttributeField columns,
                    // we need to save the workflows into the grid's object list
                    var workflowObjectQry = workflows;
                    if ( gWorkflows.AllowPaging )
                    {
                        workflowObjectQry = workflowObjectQry.Skip( gWorkflows.PageIndex * gWorkflows.PageSize ).Take( gWorkflows.PageSize );
                    }

                    gWorkflows.ObjectList = workflowObjectQry.ToList().ToDictionary( k => k.Id.ToString(), v => v as object );

                    gWorkflows.EntityTypeId = EntityTypeCache.Read<Workflow>().Id;
                    var qryGrid = workflows.Select( w => new
                    {
                        w.Id,
                        w.Name,
                        Initiator = w.InitiatorPersonAlias.Person,
                        Activities = w.Activities.Where( a => a.ActivatedDateTime.HasValue && !a.CompletedDateTime.HasValue ).OrderBy( a => a.ActivityType.Order ).Select( a => a.ActivityType.Name ),
                        w.CreatedDateTime,
                        Status = w.Status,
                        IsCompleted = w.CompletedDateTime.HasValue
                    } );

                    gWorkflows.SetLinqDataSource( qryGrid );
                    gWorkflows.DataBind();
                }
                else
                {
                    pnlWorkflowList.Visible = false;
                }
            }
        }
Exemple #5
0
        /// <summary>
        /// Binds the grid.
        /// </summary>
        private void BindGrid()
        {
            WorkflowService workflowService = new WorkflowService();
            SortProperty sortProperty = gWorkflows.SortProperty;

            var qry = workflowService.Queryable();

            WorkflowType workflowType = this.ContextEntity<WorkflowType>();

            if ( workflowType == null )
            {
                pnlWorkflowList.Visible = false;
                return;
            }

            // if this isn't currently a persisted workflow type, and there are no records, hide the panel
            if ( !workflowType.IsPersisted )
            {
                if ( qry.Count() == 0 )
                {
                    pnlWorkflowList.Visible = false;
                    return;
                }
            }

            if (!string.IsNullOrWhiteSpace(workflowType.WorkTerm))
            {
                gWorkflows.RowItemText = workflowType.WorkTerm;
                lGridTitle.Text = workflowType.WorkTerm.Pluralize();
            }

            AttributeService attributeService = new AttributeService();

            // add attributes with IsGridColumn to grid
            string qualifierValue = workflowType.Id.ToString();
            var qryWorkflowTypeAttributes = attributeService.GetByEntityTypeId( new Workflow().TypeId ).AsQueryable()
                .Where( a => a.EntityTypeQualifierColumn.Equals( "WorkflowTypeId", StringComparison.OrdinalIgnoreCase )
                && a.EntityTypeQualifierValue.Equals( qualifierValue ) );

            qryWorkflowTypeAttributes = qryWorkflowTypeAttributes.Where( a => a.IsGridColumn );

            List<Attribute> gridItems = qryWorkflowTypeAttributes.ToList();

            foreach ( var item in gWorkflows.Columns.OfType<AttributeField>().ToList() )
            {
                gWorkflows.Columns.Remove( item );
            }

            foreach ( var item in gridItems.OrderBy( a => a.Order ).ThenBy( a => a.Name ) )
            {
                string dataFieldExpression = item.Key;
                bool columnExists = gWorkflows.Columns.OfType<AttributeField>().FirstOrDefault( a => a.DataField.Equals( dataFieldExpression ) ) != null;
                if ( !columnExists )
                {
                    AttributeField boundField = new AttributeField();
                    boundField.DataField = dataFieldExpression;
                    boundField.HeaderText = item.Name;
                    boundField.SortExpression = string.Empty;
                    int insertPos = gWorkflows.Columns.IndexOf( gWorkflows.Columns.OfType<DeleteField>().First() );
                    gWorkflows.Columns.Insert( insertPos, boundField );
                }
            }


            pnlWorkflowList.Visible = true;

            qry = qry.Where( a => a.WorkflowTypeId.Equals( workflowType.Id ) );

            if ( sortProperty != null )
            {
                gWorkflows.DataSource = qry.Sort( sortProperty ).ToList();
            }
            else
            {
                gWorkflows.DataSource = qry.OrderBy( s => s.Name ).ToList();
            }

            gWorkflows.DataBind();
        }
Exemple #6
0
        /// <summary>
        /// Called by the <see cref="IScheduler" /> when a <see cref="ITrigger" />
        /// fires that is associated with the <see cref="IJob" />.
        /// </summary>
        /// <param name="context">The execution context.</param>
        /// <remarks>
        /// The implementation may wish to set a  result object on the
        /// JobExecutionContext before this method exits.  The result itself
        /// is meaningless to Quartz, but may be informative to
        /// <see cref="IJobListener" />s or
        /// <see cref="ITriggerListener" />s that are watching the job's
        /// execution.
        /// </remarks>
        public virtual void Execute( IJobExecutionContext context )
        {
            int workflowsProcessed = 0;
            int workflowErrors = 0;
            int workflowExceptions = 0;
            var ProcessingErrors = new List<string>();
            var exceptionMsgs = new List<string>();

            foreach ( var workflowId in new WorkflowService( new RockContext() )
                .GetActive()
                .Select( w => w.Id )
                .ToList() )
            {
                try
                {
                    // create a new rockContext and service for every workflow to prevent a build-up of Context.ChangeTracker.Entries()
                    var rockContext = new RockContext();
                    var workflowService = new WorkflowService( rockContext );
                    var workflow = workflowService.Queryable( "WorkflowType" ).FirstOrDefault( a => a.Id == workflowId );
                    if ( workflow != null )
                    {
                        try
                        {
                            if ( !workflow.LastProcessedDateTime.HasValue ||
                                RockDateTime.Now.Subtract( workflow.LastProcessedDateTime.Value ).TotalSeconds >= ( workflow.WorkflowType.ProcessingIntervalSeconds ?? 0 ) )
                            {
                                var errorMessages = new List<string>();

                                var processed = workflowService.Process( workflow, out errorMessages );
                                if ( processed )
                                {
                                    workflowsProcessed++;
                                }
                                else
                                {
                                    workflowErrors++;
                                    ProcessingErrors.Add( string.Format( "{0} [{1}] - {2} [{3}]: {4}", workflow.WorkflowType.Name, workflow.WorkflowTypeId, workflow.Name, workflow.Id, errorMessages.AsDelimited( ", " ) ) );
                                }
                            }
                        }
                        catch ( Exception ex )
                        {
                            string workflowDetails = string.Format( "{0} [{1}] - {2} [{3}]", workflow.WorkflowType.Name, workflow.WorkflowTypeId, workflow.Name, workflow.Id );
                            exceptionMsgs.Add( workflowDetails + ": " + ex.Message );
                            throw new Exception( "Exception occurred processing workflow: " + workflowDetails, ex );
                        }
                    }
                }

                catch ( Exception ex )
                {
                    ExceptionLogService.LogException( ex, null );
                    workflowExceptions++;
                }
            }

            var resultMsg = new StringBuilder();
            resultMsg.AppendFormat( "{0} workflows processed", workflowsProcessed );
            if ( workflowErrors > 0 )
            {
                resultMsg.AppendFormat( ", {0} workflows reported an error", workflowErrors );
            }
            if ( workflowExceptions > 0 )
            {
                resultMsg.AppendFormat( ", {0} workflows caused an exception", workflowExceptions );
            }
            if ( ProcessingErrors.Any() )
            {
                resultMsg.Append( Environment.NewLine + ProcessingErrors.AsDelimited( Environment.NewLine ) );
            }

            if ( exceptionMsgs.Any() )
            {
                throw new Exception( "One or more exceptions occurred processing workflows..." + Environment.NewLine + exceptionMsgs.AsDelimited( Environment.NewLine ) );
            }

            context.Result = resultMsg.ToString();
        }
Exemple #7
0
        /// <summary>
        /// Binds the grid.
        /// </summary>
        private void BindGrid()
        {
            if ( _workflowType != null )
            {
                pnlWorkflowList.Visible = true;

                var rockContext = new RockContext();
                var workflowService = new WorkflowService( rockContext );

                var qry = workflowService.Queryable( "Activities.ActivityType,InitiatorPersonAlias.Person" )
                    .Where( w => w.WorkflowTypeId.Equals( _workflowType.Id ) );

                // Activated Date Range Filter
                var drp = new DateRangePicker();
                drp.DelimitedValues = gfWorkflows.GetUserPreference( "Activated" );
                if ( drp.LowerValue.HasValue )
                {
                    qry = qry.Where( w => w.ActivatedDateTime >= drp.LowerValue.Value );
                }
                if ( drp.UpperValue.HasValue )
                {
                    DateTime upperDate = drp.UpperValue.Value.Date.AddDays( 1 );
                    qry = qry.Where( w => w.ActivatedDateTime.Value < upperDate );
                }

                // State Filter
                string state = gfWorkflows.GetUserPreference( "State" );
                if (!string.IsNullOrWhiteSpace(state))
                {
                    // somewhat of a backwards comparison to account for value of "None"
                    if ( !state.Equals( "Active" ) )
                    {
                        qry = qry.Where( w => w.CompletedDateTime.HasValue );
                    }
                    if ( !state.Equals( "Completed" ) )
                    {
                        qry = qry.Where( w => !w.CompletedDateTime.HasValue );
                    }
                }

                // Completed Date Range Filter
                var drp2 = new DateRangePicker();
                drp2.DelimitedValues = gfWorkflows.GetUserPreference( "Completed" );
                if ( drp2.LowerValue.HasValue )
                {
                    qry = qry.Where( w => w.CompletedDateTime.HasValue && w.CompletedDateTime.Value >= drp2.LowerValue.Value );
                }
                if ( drp2.UpperValue.HasValue )
                {
                    DateTime upperDate = drp2.UpperValue.Value.Date.AddDays( 1 );
                    qry = qry.Where( w => w.CompletedDateTime.HasValue && w.CompletedDateTime.Value < upperDate );
                }

                string name = gfWorkflows.GetUserPreference( "Name" );
                if ( !string.IsNullOrWhiteSpace( name ) )
                {
                    qry = qry.Where( w => w.Name.StartsWith( name ) );
                }

                int? personId = gfWorkflows.GetUserPreference( "Initiator" ).AsIntegerOrNull();
                if ( personId.HasValue )
                {
                    qry = qry.Where( w => w.InitiatorPersonAlias.PersonId == personId.Value );
                }

                string status = gfWorkflows.GetUserPreference( "Status" );
                if ( !string.IsNullOrWhiteSpace( status ) )
                {
                    qry = qry.Where( w => w.Status.StartsWith( status ) );
                }

                List<Workflow> workflows = null;

                var sortProperty = gWorkflows.SortProperty;
                if ( sortProperty != null )
                {
                    if ( sortProperty.Property == "Initiator" )
                    {
                        if ( sortProperty.Direction == SortDirection.Ascending )
                        {
                            workflows = qry
                                .OrderBy( w => w.InitiatorPersonAlias.Person.LastName )
                                .ThenBy( w => w.InitiatorPersonAlias.Person.NickName )
                                .ToList();
                        }
                        else
                        {
                            workflows = qry
                                .OrderByDescending( w => w.InitiatorPersonAlias.Person.LastName )
                                .ThenByDescending( w => w.InitiatorPersonAlias.Person.NickName )
                                .ToList();
                        }
                    }
                    else
                    {
                        workflows = qry.Sort( sortProperty ).ToList();
                    }
                }
                else
                {
                    workflows = qry.OrderByDescending( s => s.CreatedDateTime ).ToList();
                }

                // Since we're not binding to actual workflow list, but are using AttributeField columns,
                // we need to save the workflows into the grid's object list
                gWorkflows.ObjectList = new Dictionary<string, object>();
                workflows.ForEach( w => gWorkflows.ObjectList.Add( w.Id.ToString(), w ) );

                gWorkflows.DataSource = workflows.Select( w => new
                {
                    w.Id,
                    w.Name,
                    Initiator = ( w.InitiatorPersonAlias != null ? w.InitiatorPersonAlias.Person.FullName : "" ),
                    Activities = w.ActiveActivities.Select( a => a.ActivityType.Name ).ToList().AsDelimited( "<br/>" ),
                    w.CreatedDateTime,
                    Status = string.Format( "<span class='label label-info'>{0}</span>", w.Status ),
                    State = ( w.CompletedDateTime.HasValue ? "<span class='label label-default'>Completed</span>" : "<span class='label label-success'>Active</span>" )
                } ).ToList();
                gWorkflows.DataBind();
            }
            else
            {
                pnlWorkflowList.Visible = false;
            }
        }
        /// <summary>
        /// Binds the grid.
        /// </summary>
        private void BindGrid()
        {
            if ( _workflowType != null )
            {
                pnlWorkflowList.Visible = true;

                var rockContext = new RockContext();
                var workflowService = new WorkflowService( rockContext );

                var qry = workflowService
                    .Queryable( "Activities.ActivityType,InitiatorPersonAlias.Person" ).AsNoTracking()
                    .Where( w => w.WorkflowTypeId.Equals( _workflowType.Id ) );

                // Activated Date Range Filter
                if ( drpActivated.LowerValue.HasValue )
                {
                    qry = qry.Where( w => w.ActivatedDateTime >= drpActivated.LowerValue.Value );
                }
                if ( drpActivated.UpperValue.HasValue )
                {
                    DateTime upperDate = drpActivated.UpperValue.Value.Date.AddDays( 1 );
                    qry = qry.Where( w => w.ActivatedDateTime.Value < upperDate );
                }

                // State Filter
                List<string> states = cblState.SelectedValues;
                if ( states.Count == 1 )    // Don't filter if none or both options are selected
                {
                    if ( states[0] == "Active" )
                    {
                        qry = qry.Where( w => !w.CompletedDateTime.HasValue );
                    }
                    else
                    {
                        qry = qry.Where( w => w.CompletedDateTime.HasValue );
                    }
                }

                // Completed Date Range Filter
                if ( drpCompleted.LowerValue.HasValue )
                {
                    qry = qry.Where( w => w.CompletedDateTime.HasValue && w.CompletedDateTime.Value >= drpCompleted.LowerValue.Value );
                }
                if ( drpCompleted.UpperValue.HasValue )
                {
                    DateTime upperDate = drpCompleted.UpperValue.Value.Date.AddDays( 1 );
                    qry = qry.Where( w => w.CompletedDateTime.HasValue && w.CompletedDateTime.Value < upperDate );
                }

                string name = tbName.Text;
                if ( !string.IsNullOrWhiteSpace( name ) )
                {
                    qry = qry.Where( w => w.Name.StartsWith( name ) );
                }

                int? personId = ppInitiator.SelectedValue;
                if ( personId.HasValue )
                {
                    qry = qry.Where( w => w.InitiatorPersonAlias.PersonId == personId.Value );
                }

                string status = tbStatus.Text;
                if ( !string.IsNullOrWhiteSpace( status ) )
                {
                    qry = qry.Where( w => w.Status.StartsWith( status ) );
                }

                // Filter query by any configured attribute filters
                if ( AvailableAttributes != null && AvailableAttributes.Any() )
                {
                    var attributeValueService = new AttributeValueService( rockContext );
                    var parameterExpression = attributeValueService.ParameterExpression;

                    foreach ( var attribute in AvailableAttributes )
                    {
                        var filterControl = phAttributeFilters.FindControl( "filter_" + attribute.Id.ToString() );
                        if ( filterControl != null )
                        {
                            var filterValues = attribute.FieldType.Field.GetFilterValues( filterControl, attribute.QualifierValues );
                            var expression = attribute.FieldType.Field.AttributeFilterExpression( attribute.QualifierValues, filterValues, parameterExpression );
                            if ( expression != null )
                            {
                                var attributeValues = attributeValueService
                                    .Queryable()
                                    .Where( v => v.Attribute.Id == attribute.Id );

                                attributeValues = attributeValues.Where( parameterExpression, expression, null );

                                qry = qry.Where( w => attributeValues.Select( v => v.EntityId ).Contains( w.Id ) );
                            }
                        }
                    }
                }

                List<Workflow> workflows = null;

                var sortProperty = gWorkflows.SortProperty;
                if ( sortProperty != null )
                {
                    if ( sortProperty.Property == "Initiator" )
                    {
                        if ( sortProperty.Direction == SortDirection.Ascending )
                        {
                            workflows = qry
                                .OrderBy( w => w.InitiatorPersonAlias.Person.LastName )
                                .ThenBy( w => w.InitiatorPersonAlias.Person.NickName )
                                .ToList();
                        }
                        else
                        {
                            workflows = qry
                                .OrderByDescending( w => w.InitiatorPersonAlias.Person.LastName )
                                .ThenByDescending( w => w.InitiatorPersonAlias.Person.NickName )
                                .ToList();
                        }
                    }
                    else
                    {
                        workflows = qry.Sort( sortProperty ).ToList();
                    }
                }
                else
                {
                    workflows = qry.OrderByDescending( s => s.CreatedDateTime ).ToList();
                }

                // Since we're not binding to actual workflow list, but are using AttributeField columns,
                // we need to save the workflows into the grid's object list
                gWorkflows.ObjectList = new Dictionary<string, object>();
                workflows.ForEach( w => gWorkflows.ObjectList.Add( w.Id.ToString(), w ) );

                gWorkflows.DataSource = workflows.Select( w => new
                {
                    w.Id,
                    w.Name,
                    Initiator = ( w.InitiatorPersonAlias != null ? w.InitiatorPersonAlias.Person.FullName : "" ),
                    Activities = w.ActiveActivities.Select( a => a.ActivityType.Name ).ToList().AsDelimited( "<br/>" ),
                    w.CreatedDateTime,
                    Status = string.Format( "<span class='label label-info'>{0}</span>", w.Status ),
                    State = ( w.CompletedDateTime.HasValue ? "<span class='label label-default'>Completed</span>" : "<span class='label label-success'>Active</span>" )
                } ).ToList();
                gWorkflows.DataBind();
            }
            else
            {
                pnlWorkflowList.Visible = false;
            }
        }