/// <summary>
        /// Returns a queryable collection of <see cref="Rock.Data.IEntity" /> target entities (related to the given source entity) for the given entity type and (optionally) also have a matching purpose key.
        /// </summary>
        /// <param name="sourceEntityId">A <see cref="System.Int32" /> representing the source entity identifier.</param>
        /// <param name="sourceEntityTypeId">A <see cref="System.Int32" /> representing the source entity type identifier.</param>
        /// <param name="relatedEntityTypeId">A <see cref="System.Int32" /> representing the related target entity type identifier.</param>
        /// <param name="purposeKey">The purpose key.</param>
        /// <returns>
        /// Returns a queryable collection of <see cref="Rock.Data.IEntity" /> entities.
        /// </returns>
        public IQueryable <IEntity> GetRelatedToSource(int sourceEntityId, int sourceEntityTypeId, int relatedEntityTypeId, string purposeKey)
        {
            EntityTypeCache relatedEntityTypeCache = EntityTypeCache.Get(relatedEntityTypeId);

            if (relatedEntityTypeCache.AssemblyName != null)
            {
                IQueryable <RelatedEntity> query = GetRelatedEntityRecordsToSource(sourceEntityId, sourceEntityTypeId, relatedEntityTypeId, purposeKey);

                var  rockContext       = this.Context as RockContext;
                Type relatedEntityType = relatedEntityTypeCache.GetEntityType();
                if (relatedEntityType != null)
                {
                    Rock.Data.IService serviceInstance = Reflection.GetServiceForEntityType(relatedEntityType, rockContext);
                    MethodInfo         qryMethod       = serviceInstance.GetType().GetMethod("Queryable", new Type[] { });
                    var entityQry = qryMethod.Invoke(serviceInstance, new object[] { }) as IQueryable <IEntity>;

                    entityQry = query.Join(
                        entityQry,
                        f => f.TargetEntityId,
                        e => e.Id,
                        (f, e) => e);

                    return(entityQry);
                }
            }

            return(null);
        }
        /// <summary>
        /// Gets the entity query
        /// For example: If the EntityTypeId is GroupMember, this will return a GroupMember query of group members that the person is following
        /// </summary>
        /// <param name="entityTypeId">The entity type identifier.</param>
        /// <param name="personId">The person identifier.</param>
        /// <returns></returns>
        public IQueryable <IEntity> GetFollowedItems(int entityTypeId, int personId)
        {
            EntityTypeCache itemEntityType   = EntityTypeCache.Get(entityTypeId);
            var             rockContext      = this.Context as RockContext;
            var             followedItemsQry = this.Queryable().Where(a => a.PersonAlias.PersonId == personId && a.EntityTypeId == entityTypeId);

            if (itemEntityType.AssemblyName != null)
            {
                Type entityType = itemEntityType.GetEntityType();
                if (entityType != null)
                {
                    Type[]             modelType          = { entityType };
                    Type               genericServiceType = typeof(Rock.Data.Service <>);
                    Type               modelServiceType   = genericServiceType.MakeGenericType(modelType);
                    Rock.Data.IService serviceInstance    = Activator.CreateInstance(modelServiceType, new object[] { rockContext }) as IService;

                    MethodInfo qryMethod = serviceInstance.GetType().GetMethod("Queryable", new Type[] { });
                    var        entityQry = qryMethod.Invoke(serviceInstance, new object[] { }) as IQueryable <IEntity>;

                    entityQry = followedItemsQry.Join(
                        entityQry,
                        f => f.EntityId,
                        e => e.Id,
                        (f, e) => e);

                    return(entityQry);
                }
            }

            return(null);
        }
Exemple #3
0
        /// <summary>
        /// Gets the entity query for the specified EntityTypeId
        /// </summary>
        /// <param name="entityTypeId">The entity type identifier.</param>
        /// <returns></returns>
        public IQueryable <IEntity> GetEntityQuery(int entityTypeId)
        {
            EntityTypeCache entityTypeCache = EntityTypeCache.Get(entityTypeId);

            var rockContext = this.Context as RockContext;

            if (entityTypeCache.AssemblyName != null)
            {
                Type entityType = entityTypeCache.GetEntityType();
                if (entityType != null)
                {
                    Type[]             modelType          = { entityType };
                    Type               genericServiceType = typeof(Rock.Data.Service <>);
                    Type               modelServiceType   = genericServiceType.MakeGenericType(modelType);
                    Rock.Data.IService serviceInstance    = Activator.CreateInstance(modelServiceType, new object[] { rockContext }) as IService;

                    MethodInfo qryMethod = serviceInstance.GetType().GetMethod("Queryable", new Type[] { });
                    var        entityQry = qryMethod.Invoke(serviceInstance, new object[] { }) as IQueryable <IEntity>;

                    return(entityQry);
                }
            }

            return(null);
        }
Exemple #4
0
        /// <summary>
        /// Gets the type of the i entity for entity.
        /// </summary>
        /// <param name="entityType">Type of the entity.</param>
        /// <param name="guid">The unique identifier.</param>
        /// <returns></returns>
        public static Rock.Data.IEntity GetIEntityForEntityType(Type entityType, Guid guid)
        {
            var dbContext = Reflection.GetDbContextForEntityType(entityType);

            Rock.Data.IService serviceInstance = Reflection.GetServiceForEntityType(entityType, dbContext);
            if (serviceInstance != null)
            {
                System.Reflection.MethodInfo getMethod = serviceInstance.GetType().GetMethod("Get", new Type[] { typeof(Guid) });
                return(getMethod.Invoke(serviceInstance, new object[] { guid }) as Rock.Data.IEntity);
            }

            return(null);
        }
        /// <summary>
        /// Gets the entity query
        /// For example: If the EntityTypeId is GroupMember, this will return a GroupMember query of group members that the person is following
        /// </summary>
        /// <param name="entityTypeId">The entity type identifier.</param>
        /// <param name="personId">The person identifier.</param>
        /// <param name="purposeKey">A purpose that defines how this following will be used.</param>
        /// <returns></returns>
        public IQueryable <IEntity> GetFollowedItems(int entityTypeId, int personId, string purposeKey)
        {
            EntityTypeCache itemEntityType = EntityTypeCache.Get(entityTypeId);
            var             rockContext    = this.Context as RockContext;

            purposeKey = purposeKey ?? string.Empty;

            var followedItemsQry = this.Queryable()
                                   .Where(a => a.PersonAlias.PersonId == personId && a.EntityTypeId == entityTypeId)
                                   .Where(f => (f.PurposeKey == null && purposeKey == "") || f.PurposeKey == purposeKey);

            if (itemEntityType.AssemblyName != null)
            {
                Type entityType = itemEntityType.GetEntityType();
                if (entityType != null)
                {
                    Rock.Data.IService serviceInstance = Reflection.GetServiceForEntityType(entityType, rockContext);
                    MethodInfo         qryMethod       = serviceInstance.GetType().GetMethod("Queryable", new Type[] { });
                    var entityQry = qryMethod.Invoke(serviceInstance, new object[] { }) as IQueryable <IEntity>;

                    entityQry = followedItemsQry.Join(
                        entityQry,
                        f => f.EntityId,
                        e => e.Id,
                        (f, e) => e);

                    int personEntityTypeId      = EntityTypeCache.Get <Rock.Model.Person>().Id;
                    int personAliasEntityTypeId = EntityTypeCache.Get <Rock.Model.PersonAlias>().Id;

                    // if requesting persons that the person is following, it is probably recorded as the PersonAlias records that the person is following, so get Person from that
                    if (entityTypeId == personEntityTypeId)
                    {
                        var followedItemsPersonAliasQry = this.Queryable().Where(a => a.PersonAlias.PersonId == personId && a.EntityTypeId == personAliasEntityTypeId);
                        var entityPersonAliasQry        = new PersonAliasService(rockContext).Queryable();

                        var entityFollowedPersons = followedItemsPersonAliasQry.Join(
                            entityPersonAliasQry,
                            f => f.EntityId,
                            e => e.Id,
                            (f, e) => e).Select(a => a.Person);

                        entityQry = entityQry.Union(entityFollowedPersons as IQueryable <IEntity>);
                    }

                    return(entityQry);
                }
            }

            return(null);
        }
        /// <summary>
        /// Gets the entity query, ordered by the EntitySetItem.Order
        /// For example: If the EntitySet.EntityType is Person, this will return a Person Query of the items in this set
        /// </summary>
        /// <param name="entitySetId">The entity set identifier.</param>
        /// <returns></returns>
        public IQueryable <IEntity> GetEntityQuery(int entitySetId)
        {
            var entitySet = this.Get(entitySetId);

            if (entitySet?.EntityTypeId == null)
            {
                // the EntitySet Items are not IEntity items
                return(null);
            }

            EntityTypeCache itemEntityType = EntityTypeCache.Get(entitySet.EntityTypeId.Value);

            var rockContext           = this.Context as RockContext;
            var entitySetItemsService = new EntitySetItemService(rockContext);
            var entityItemQry         = entitySetItemsService.Queryable().Where(a => a.EntitySetId == entitySetId).OrderBy(a => a.Order);

            bool isPersonEntitySet = itemEntityType.Guid == Rock.SystemGuid.EntityType.PERSON.AsGuid();

            if (itemEntityType.AssemblyName != null)
            {
                Type entityType = itemEntityType.GetEntityType();
                if (entityType != null)
                {
                    Type[]             modelType          = { entityType };
                    Type               genericServiceType = typeof(Rock.Data.Service <>);
                    Type               modelServiceType   = genericServiceType.MakeGenericType(modelType);
                    Rock.Data.IService serviceInstance    = Activator.CreateInstance(modelServiceType, new object[] { rockContext }) as IService;

                    MethodInfo qryMethod = serviceInstance.GetType().GetMethod("Queryable", new Type[] { });
                    var        entityQry = qryMethod.Invoke(serviceInstance, new object[] { }) as IQueryable <IEntity>;

                    var joinQry = entityItemQry.Join(entityQry, k => k.EntityId, i => i.Id, (setItem, item) => new
                    {
                        Item      = item,
                        ItemOrder = setItem.Order
                    }
                                                     ).OrderBy(a => a.ItemOrder).ThenBy(a => a.Item.Id);

                    return(joinQry.Select(a => a.Item));
                }
            }

            return(null);
        }
        /// <summary>
        /// Returns a queryable collection of <see cref="Rock.Data.IEntity"/> source entities (related to the given target entity) for the given entity type and (optionally) also have a matching purpose key.
        /// </summary>
        /// <param name="targetEntityId">A <see cref="System.Int32" /> representing the target entity identifier.</param>
        /// <param name="targetEntityTypeId">A <see cref="System.Int32" /> representing the <see cref="RelatedEntity.TargetEntityTypeId"/></param>
        /// <param name="relatedEntityTypeId">A <see cref="System.Int32" /> representing the related <see cref="RelatedEntity.SourceEntityTypeId"/></param>
        /// <param name="purposeKey">The purpose key.</param>
        /// <returns>
        /// Returns a queryable collection of <see cref="Rock.Data.IEntity" /> entities.
        /// </returns>
        public IQueryable <IEntity> GetRelatedToTarget(int targetEntityId, int targetEntityTypeId, int relatedEntityTypeId, string purposeKey)
        {
            EntityTypeCache relatedEntityTypeCache = EntityTypeCache.Get(relatedEntityTypeId);

            if (relatedEntityTypeCache.AssemblyName != null)
            {
                var query = Queryable()
                            .Where(a => a.TargetEntityTypeId == targetEntityTypeId &&
                                   a.TargetEntityId == targetEntityId &&
                                   a.SourceEntityTypeId == relatedEntityTypeId);

                if (purposeKey.IsNullOrWhiteSpace())
                {
                    query = query.Where(a => string.IsNullOrEmpty(a.PurposeKey));
                }
                else
                {
                    query = query.Where(a => a.PurposeKey == purposeKey);
                }

                var  rockContext       = this.Context as RockContext;
                Type relatedEntityType = relatedEntityTypeCache.GetEntityType();
                if (relatedEntityType != null)
                {
                    Rock.Data.IService serviceInstance = Reflection.GetServiceForEntityType(relatedEntityType, rockContext);
                    MethodInfo         qryMethod       = serviceInstance.GetType().GetMethod("Queryable", new Type[] { });
                    var entityQry = qryMethod.Invoke(serviceInstance, new object[] { }) as IQueryable <IEntity>;

                    entityQry = query.Join(
                        entityQry,
                        f => f.SourceEntityId,
                        e => e.Id,
                        (f, e) => e);

                    return(entityQry);
                }
            }

            return(null);
        }
        /// <summary>
        /// Executes the specified context.
        /// </summary>
        /// <param name="context">The context.</param>
        public void Execute(IJobExecutionContext context)
        {
            var exceptionMsgs = new List <string>();

            JobDataMap dataMap         = context.JobDetail.JobDataMap;
            Guid?      groupGuid       = dataMap.GetString("EligibleFollowers").AsGuidOrNull();
            Guid?      systemEmailGuid = dataMap.GetString("EmailTemplate").AsGuidOrNull();
            int        followingSuggestionsEmailsSent       = 0;
            int        followingSuggestionsSuggestionsTotal = 0;

            if (groupGuid.HasValue && systemEmailGuid.HasValue)
            {
                using (var rockContext = new RockContext())
                {
                    var followingService = new FollowingService(rockContext);

                    // The people who are eligible to get following suggestions based on the group type setting for this job
                    var eligiblePersonIds = new GroupMemberService(rockContext)
                                            .Queryable().AsNoTracking()
                                            .Where(m =>
                                                   m.Group != null &&
                                                   m.Group.Guid.Equals(groupGuid.Value) &&
                                                   m.GroupMemberStatus == GroupMemberStatus.Active &&
                                                   m.Person != null &&
                                                   m.Person.Email != null &&
                                                   m.Person.Email != "")
                                            .Select(m => m.PersonId)
                                            .Distinct();

                    // check to see if there are any event types that require notification
                    var followerPersonIds = new List <int>();
                    if (new FollowingEventTypeService(rockContext)
                        .Queryable().AsNoTracking()
                        .Any(e => e.IsNoticeRequired))
                    {
                        // if so, include all eligible people
                        followerPersonIds = eligiblePersonIds.ToList();
                    }
                    else
                    {
                        // if not, filter the list of eligible people down to only those that actually have subscribed to one or more following events
                        followerPersonIds = new FollowingEventSubscriptionService(rockContext)
                                            .Queryable().AsNoTracking()
                                            .Where(f => eligiblePersonIds.Contains(f.PersonAlias.PersonId))
                                            .Select(f => f.PersonAlias.PersonId)
                                            .Distinct()
                                            .ToList();
                    }

                    if (followerPersonIds.Any())
                    {
                        // Get the primary person alias id for each of the followers
                        var primaryAliasIds = new Dictionary <int, int>();
                        new PersonAliasService(rockContext)
                        .Queryable().AsNoTracking()
                        .Where(a =>
                               followerPersonIds.Contains(a.PersonId) &&
                               a.PersonId == a.AliasPersonId)
                        .ToList()
                        .ForEach(a => primaryAliasIds.AddOrIgnore(a.PersonId, a.Id));

                        // Get current date/time.
                        var timestamp = RockDateTime.Now;

                        var suggestionTypes = new FollowingSuggestionTypeService(rockContext)
                                              .Queryable().AsNoTracking()
                                              .Where(s => s.IsActive)
                                              .OrderBy(s => s.Name)
                                              .ToList();

                        var components        = new Dictionary <int, SuggestionComponent>();
                        var suggestedEntities = new Dictionary <int, Dictionary <int, IEntity> >();

                        foreach (var suggestionType in suggestionTypes)
                        {
                            try
                            {
                                // Get the suggestion type component
                                var suggestionComponent = suggestionType.GetSuggestionComponent();
                                if (suggestionComponent != null)
                                {
                                    components.Add(suggestionType.Id, suggestionComponent);

                                    // Get the entitytype for this suggestion type
                                    var suggestionEntityType = EntityTypeCache.Read(suggestionComponent.FollowedType);
                                    if (suggestionEntityType != null)
                                    {
                                        var entityIds = new List <int>();

                                        // Call the components method to return all of it's suggestions
                                        var personEntitySuggestions = suggestionComponent.GetSuggestions(suggestionType, followerPersonIds);

                                        // If any suggestions were returned by the component
                                        if (personEntitySuggestions.Any())
                                        {
                                            int    entityTypeId = suggestionEntityType.Id;
                                            string reasonNote   = suggestionType.ReasonNote;

                                            // Get the existing followings for any of the followers
                                            var existingFollowings = new Dictionary <int, List <int> >();
                                            foreach (var following in followingService.Queryable("PersonAlias").AsNoTracking()
                                                     .Where(f =>
                                                            f.EntityTypeId == entityTypeId &&
                                                            followerPersonIds.Contains(f.PersonAlias.PersonId)))
                                            {
                                                existingFollowings.AddOrIgnore(following.PersonAlias.PersonId, new List <int>());
                                                existingFollowings[following.PersonAlias.PersonId].Add(following.EntityId);
                                            }

                                            // Loop through each follower
                                            foreach (var followerPersonId in personEntitySuggestions
                                                     .Select(s => s.PersonId)
                                                     .Distinct())
                                            {
                                                using (var suggestionContext = new RockContext())
                                                {
                                                    var followingSuggestedService = new FollowingSuggestedService(suggestionContext);

                                                    // Read all the existing suggestions for this type and the returned followers
                                                    var existingSuggestions = followingSuggestedService
                                                                              .Queryable("PersonAlias")
                                                                              .Where(s =>
                                                                                     s.SuggestionTypeId == suggestionType.Id &&
                                                                                     s.PersonAlias.PersonId == followerPersonId)
                                                                              .ToList();

                                                    // Look  through the returned suggestions
                                                    foreach (var followedEntityId in personEntitySuggestions
                                                             .Where(s => s.PersonId == followerPersonId)
                                                             .Select(s => s.EntityId))
                                                    {
                                                        // Make sure person isn't already following this entity
                                                        if (!existingFollowings.ContainsKey(followerPersonId) ||
                                                            !existingFollowings[followerPersonId].Contains(followedEntityId))
                                                        {
                                                            // If this person had a primary alias id
                                                            if (primaryAliasIds.ContainsKey(followerPersonId))
                                                            {
                                                                entityIds.Add(followedEntityId);

                                                                // Look for existing suggestion for this person and entity
                                                                var suggestion = existingSuggestions
                                                                                 .Where(s => s.EntityId == followedEntityId)
                                                                                 .OrderByDescending(s => s.StatusChangedDateTime)
                                                                                 .FirstOrDefault();

                                                                // If not found, add one
                                                                if (suggestion == null)
                                                                {
                                                                    suggestion = new FollowingSuggested();
                                                                    suggestion.EntityTypeId          = entityTypeId;
                                                                    suggestion.EntityId              = followedEntityId;
                                                                    suggestion.PersonAliasId         = primaryAliasIds[followerPersonId];
                                                                    suggestion.SuggestionTypeId      = suggestionType.Id;
                                                                    suggestion.Status                = FollowingSuggestedStatus.PendingNotification;
                                                                    suggestion.StatusChangedDateTime = timestamp;
                                                                    followingSuggestedService.Add(suggestion);
                                                                }
                                                                else
                                                                {
                                                                    // If found, and it has not been ignored, and it's time to promote again, update the promote date
                                                                    if (suggestion.Status != FollowingSuggestedStatus.Ignored &&
                                                                        (
                                                                            !suggestionType.ReminderDays.HasValue ||
                                                                            !suggestion.LastPromotedDateTime.HasValue ||
                                                                            suggestion.LastPromotedDateTime.Value.AddDays(suggestionType.ReminderDays.Value) <= timestamp
                                                                        ))
                                                                    {
                                                                        if (suggestion.Status != FollowingSuggestedStatus.PendingNotification)
                                                                        {
                                                                            suggestion.StatusChangedDateTime = timestamp;
                                                                            suggestion.Status = FollowingSuggestedStatus.PendingNotification;
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }

                                                    // Save the suggestions for this type
                                                    suggestionContext.SaveChanges();
                                                }
                                            }
                                        }

                                        // If any entities are being suggested for this type, query database for them and save to dictionary
                                        if (entityIds.Any())
                                        {
                                            if (suggestionEntityType.AssemblyName != null)
                                            {
                                                // get the actual type of what is being followed
                                                Type entityType = suggestionEntityType.GetEntityType();
                                                if (entityType != null)
                                                {
                                                    // Get generic queryable method and query all the entities that are being followed
                                                    Type[]             modelType          = { entityType };
                                                    Type               genericServiceType = typeof(Rock.Data.Service <>);
                                                    Type               modelServiceType   = genericServiceType.MakeGenericType(modelType);
                                                    Rock.Data.IService serviceInstance    = Activator.CreateInstance(modelServiceType, new object[] { rockContext }) as IService;
                                                    MethodInfo         qryMethod          = serviceInstance.GetType().GetMethod("Queryable", new Type[] { });
                                                    var entityQry  = qryMethod.Invoke(serviceInstance, new object[] { }) as IQueryable <IEntity>;
                                                    var entityList = entityQry.AsNoTracking().Where(q => entityIds.Contains(q.Id)).ToList();
                                                    if (entityList != null && entityList.Any())
                                                    {
                                                        var entities = new Dictionary <int, IEntity>();
                                                        entityList.ForEach(e => entities.Add(e.Id, e));
                                                        suggestedEntities.Add(suggestionType.Id, entities);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                exceptionMsgs.Add(string.Format("An exception occurred calculating suggestions for the '{0}' suggestion type:{1}    {2}", suggestionType.Name, Environment.NewLine, ex.Messages().AsDelimited(Environment.NewLine + "   ")));
                                ExceptionLogService.LogException(ex, System.Web.HttpContext.Current);
                            }
                        }

                        var allSuggestions = new FollowingSuggestedService(rockContext)
                                             .Queryable("PersonAlias")
                                             .Where(s => s.Status == FollowingSuggestedStatus.PendingNotification)
                                             .ToList();

                        var suggestionPersonIds = allSuggestions
                                                  .Where(s => followerPersonIds.Contains(s.PersonAlias.PersonId))
                                                  .Select(s => s.PersonAlias.PersonId)
                                                  .Distinct()
                                                  .ToList();

                        var appRoot = Rock.Web.Cache.GlobalAttributesCache.Read(rockContext).GetValue("ExternalApplicationRoot");

                        foreach (var person in new PersonService(rockContext)
                                 .Queryable().AsNoTracking()
                                 .Where(p => suggestionPersonIds.Contains(p.Id))
                                 .ToList())
                        {
                            try
                            {
                                var personSuggestionNotices = new List <FollowingSuggestionNotices>();

                                foreach (var suggestionType in suggestionTypes)
                                {
                                    var component = components.ContainsKey(suggestionType.Id) ? components[suggestionType.Id] : null;
                                    if (component != null && suggestedEntities.ContainsKey(suggestionType.Id))
                                    {
                                        var entities = new List <IEntity>();
                                        foreach (var suggestion in allSuggestions
                                                 .Where(s =>
                                                        s.PersonAlias.PersonId == person.Id &&
                                                        s.SuggestionTypeId == suggestionType.Id)
                                                 .ToList())
                                        {
                                            if (suggestedEntities[suggestionType.Id].ContainsKey(suggestion.EntityId))
                                            {
                                                entities.Add(suggestedEntities[suggestionType.Id][suggestion.EntityId]);
                                                suggestion.LastPromotedDateTime = timestamp;
                                                suggestion.Status = FollowingSuggestedStatus.Suggested;
                                            }
                                        }

                                        var notices = new List <string>();
                                        foreach (var entity in component.SortEntities(entities))
                                        {
                                            notices.Add(component.FormatEntityNotification(suggestionType, entity));
                                        }

                                        if (notices.Any())
                                        {
                                            personSuggestionNotices.Add(new FollowingSuggestionNotices(suggestionType, notices));
                                        }
                                    }
                                }

                                if (personSuggestionNotices.Any())
                                {
                                    // Send the notice
                                    var recipients  = new List <RecipientData>();
                                    var mergeFields = new Dictionary <string, object>();
                                    mergeFields.Add("Person", person);
                                    mergeFields.Add("Suggestions", personSuggestionNotices.OrderBy(s => s.SuggestionType.Order).ToList());
                                    recipients.Add(new RecipientData(person.Email, mergeFields));
                                    Email.Send(systemEmailGuid.Value, recipients, appRoot);
                                    followingSuggestionsEmailsSent       += recipients.Count();
                                    followingSuggestionsSuggestionsTotal += personSuggestionNotices.Count();
                                }

                                rockContext.SaveChanges();
                            }
                            catch (Exception ex)
                            {
                                exceptionMsgs.Add(string.Format("An exception occurred sending suggestions to '{0}':{1}    {2}", person.FullName, Environment.NewLine, ex.Messages().AsDelimited(Environment.NewLine + "   ")));
                                ExceptionLogService.LogException(ex, System.Web.HttpContext.Current);
                            }
                        }
                    }
                }
            }

            context.Result = string.Format("A total of {0} following suggestions sent to {1} people", followingSuggestionsSuggestionsTotal, followingSuggestionsEmailsSent);

            if (exceptionMsgs.Any())
            {
                throw new Exception("One or more exceptions occurred calculating suggestions..." + Environment.NewLine + exceptionMsgs.AsDelimited(Environment.NewLine));
            }
        }
        /// <summary>
        /// Executes the specified context.
        /// </summary>
        /// <param name="context">The context.</param>
        public void Execute(IJobExecutionContext context)
        {
            JobDataMap dataMap         = context.JobDetail.JobDataMap;
            Guid?      groupGuid       = dataMap.GetString("EligibleFollowers").AsGuidOrNull();
            Guid?      systemEmailGuid = dataMap.GetString("EmailTemplate").AsGuidOrNull();

            if (groupGuid.HasValue && systemEmailGuid.HasValue)
            {
                var exceptionMsgs = new List <string>();

                using (var rockContext = new RockContext())
                {
                    var followingService                  = new FollowingService(rockContext);
                    var followingEventTypeService         = new FollowingEventTypeService(rockContext);
                    var followingEventNotificationService = new FollowingEventNotificationService(rockContext);

                    // Get all the active event types
                    var eventTypes = followingEventTypeService
                                     .Queryable().AsNoTracking()
                                     .Where(e =>
                                            e.EntityTypeId.HasValue &&
                                            e.IsActive)
                                     .OrderBy(e => e.Order)
                                     .ToList();

                    // Get the required event types
                    var requiredEventTypes = eventTypes
                                             .Where(e => e.IsNoticeRequired)
                                             .ToList();

                    // The people who are eligible to get following event notices based on the group type setting for this job
                    var eligiblePersonIds = new GroupMemberService(rockContext)
                                            .Queryable().AsNoTracking()
                                            .Where(m =>
                                                   m.Group != null &&
                                                   m.Group.Guid.Equals(groupGuid.Value) &&
                                                   m.GroupMemberStatus == GroupMemberStatus.Active &&
                                                   m.Person != null &&
                                                   m.Person.Email != null &&
                                                   m.Person.Email != "")
                                            .Select(m => m.PersonId)
                                            .Distinct()
                                            .ToList();

                    // Get all the subscriptions for the eligible people
                    var eventSubscriptions = new FollowingEventSubscriptionService(rockContext)
                                             .Queryable("PersonAlias").AsNoTracking()
                                             .Where(f => eligiblePersonIds.Contains(f.PersonAlias.PersonId))
                                             .ToList();

                    // Dictionaries used to store information that will be used to create notification
                    var personSubscriptions = new Dictionary <int, List <int> >();                  // Key: personId, Value: list of event type ids that person subscribes to
                    var personFollowings    = new Dictionary <int, List <int> >();                  // Key: personId, Value: list of following ids that person follows
                    var eventsThatHappened  = new Dictionary <int, Dictionary <int, string> >();    // Key: event type id Value: Dictionary of entity id and formatted event notice for the entity

                    //Get the subscriptions for each person
                    foreach (int personId in eligiblePersonIds)
                    {
                        var personEventTypes = eventSubscriptions
                                               .Where(s => s.PersonAlias.PersonId == personId)
                                               .Select(s => s.EventType)
                                               .ToList();
                        personEventTypes.AddRange(requiredEventTypes);
                        if (personEventTypes.Any())
                        {
                            personSubscriptions.AddOrIgnore(personId, personEventTypes
                                                            .OrderBy(e => e.Order)
                                                            .ThenBy(e => e.Name)
                                                            .Select(e => e.Id)
                                                            .Distinct()
                                                            .ToList());
                        }
                    }

                    // Get a distinct list of each entitytype/entity that is being followed by anyone that subscribes to events
                    var followings = followingService
                                     .Queryable("PersonAlias").AsNoTracking()
                                     .Where(f => personSubscriptions.Keys.Contains(f.PersonAlias.PersonId))
                                     .ToList();

                    // group the followings by their type
                    var followedEntityIds = new Dictionary <int, List <int> >();
                    foreach (var followedEntity in followings
                             .Select(f => new
                    {
                        f.EntityTypeId,
                        f.EntityId
                    })
                             .Distinct())
                    {
                        followedEntityIds.AddOrIgnore(followedEntity.EntityTypeId, new List <int>());
                        followedEntityIds[followedEntity.EntityTypeId].Add(followedEntity.EntityId);
                    }

                    // group the followings by the follower
                    foreach (int personId in personSubscriptions.Select(s => s.Key))
                    {
                        var personFollowing = followings
                                              .Where(f => f.PersonAlias.PersonId == personId)
                                              .Select(f => f.Id)
                                              .ToList();

                        personFollowings.Add(personId, personFollowing);
                    }

                    var timestamp = RockDateTime.Now;

                    // foreach followed entitytype
                    foreach (var keyVal in followedEntityIds)
                    {
                        // Get the entitytype
                        EntityTypeCache itemEntityType = EntityTypeCache.Read(keyVal.Key);
                        if (itemEntityType.AssemblyName != null)
                        {
                            // get the actual type of what is being followed
                            Type entityType = itemEntityType.GetEntityType();
                            if (entityType != null)
                            {
                                // Get generic queryable method and query all the entities that are being followed
                                Type[]             modelType          = { entityType };
                                Type               genericServiceType = typeof(Rock.Data.Service <>);
                                Type               modelServiceType   = genericServiceType.MakeGenericType(modelType);
                                Rock.Data.IService serviceInstance    = Activator.CreateInstance(modelServiceType, new object[] { rockContext }) as IService;
                                MethodInfo         qryMethod          = serviceInstance.GetType().GetMethod("Queryable", new Type[] { });
                                var entityQry  = qryMethod.Invoke(serviceInstance, new object[] { }) as IQueryable <IEntity>;
                                var entityList = entityQry.Where(q => keyVal.Value.Contains(q.Id)).ToList();

                                // If there are any followed entities of this type
                                if (entityList.Any())
                                {
                                    // Get the active event types for this entity type
                                    foreach (var eventType in eventTypes.Where(e => e.FollowedEntityTypeId == keyVal.Key))
                                    {
                                        try
                                        {
                                            // Get the component
                                            var eventComponent = eventType.GetEventComponent();
                                            if (eventComponent != null)
                                            {
                                                // Get the previous notificatoins for this event type
                                                var previousNotifications = followingEventNotificationService
                                                                            .Queryable()
                                                                            .Where(n => n.FollowingEventTypeId == eventType.Id)
                                                                            .ToList();

                                                // check each entity that is followed (by anyone)
                                                foreach (IEntity entity in entityList)
                                                {
                                                    var previousNotification = previousNotifications
                                                                               .Where(n => n.EntityId == entity.Id)
                                                                               .FirstOrDefault();
                                                    DateTime?lastNotification = previousNotification != null ? previousNotification.LastNotified : (DateTime?)null;

                                                    // if the event happened
                                                    if (eventComponent.HasEventHappened(eventType, entity, lastNotification))
                                                    {
                                                        // Store the event type id and the entity for later processing of notifications
                                                        eventsThatHappened.AddOrIgnore(eventType.Id, new Dictionary <int, string>());
                                                        eventsThatHappened[eventType.Id].Add(entity.Id, eventComponent.FormatEntityNotification(eventType, entity));

                                                        if (previousNotification == null)
                                                        {
                                                            previousNotification = new FollowingEventNotification();
                                                            previousNotification.FollowingEventTypeId = eventType.Id;
                                                            previousNotification.EntityId             = entity.Id;
                                                            followingEventNotificationService.Add(previousNotification);
                                                        }
                                                        previousNotification.LastNotified = timestamp;
                                                    }
                                                }

                                                rockContext.SaveChanges();
                                            }

                                            eventType.LastCheckDateTime = RockDateTime.Now;
                                        }

                                        catch (Exception ex)
                                        {
                                            exceptionMsgs.Add(string.Format("An exception occurred calculating events for the '{0}' suggestion type:{1}    {2}", eventType.Name, Environment.NewLine, ex.Messages().AsDelimited(Environment.NewLine + "   ")));
                                            ExceptionLogService.LogException(ex, System.Web.HttpContext.Current);
                                        }
                                    }
                                }
                            }
                        }
                    }

                    // send notificatons
                    var appRoot = Rock.Web.Cache.GlobalAttributesCache.Read(rockContext).GetValue("ExternalApplicationRoot");

                    var possibleRecipients = new PersonService(rockContext)
                                             .Queryable().AsNoTracking()
                                             .Where(p => personSubscriptions.Keys.Contains(p.Id))
                                             .ToList();

                    // Loop through the possible recipients that actually subscribe to events
                    foreach (var personSubscription in personSubscriptions)
                    {
                        // Get the recipient person
                        int personId = personSubscription.Key;
                        var person   = possibleRecipients.Where(p => p.Id == personId).FirstOrDefault();
                        if (person != null)
                        {
                            try
                            {
                                // Make sure person is actually following anything
                                if (personFollowings.ContainsKey(personId))
                                {
                                    // Dictionary to store the entities that had an event for each event type
                                    var personEventTypeNotices = new List <FollowingEventTypeNotices>();

                                    // Get the event types that person subscribes to
                                    foreach (var eventType in eventsThatHappened.Where(e => personSubscription.Value.Contains(e.Key)))
                                    {
                                        // Get the EntityTypeId for this event type
                                        int entityTypeId = eventTypes
                                                           .Where(e => e.Id == eventType.Key)
                                                           .Select(e => e.FollowedEntityTypeId.Value)
                                                           .FirstOrDefault();

                                        // Find all the entities with this event type that the person follows
                                        var personFollowedEntityIds = followings
                                                                      .Where(f =>
                                                                             personFollowings[personId].Contains(f.Id) &&
                                                                             f.EntityTypeId == entityTypeId)
                                                                      .Select(f => f.EntityId)
                                                                      .ToList();

                                        // Get any of those entities that had an event happen
                                        var personFollowedEntities = eventType.Value
                                                                     .Where(e => personFollowedEntityIds.Contains(e.Key))
                                                                     .ToList();

                                        // If any were found
                                        if (personFollowedEntities.Any())
                                        {
                                            // Add the entry
                                            var eventTypeObj = eventTypes.Where(e => e.Id == eventType.Key).FirstOrDefault();
                                            if (eventTypeObj != null)
                                            {
                                                personEventTypeNotices.Add(new FollowingEventTypeNotices(eventTypeObj, personFollowedEntities.Select(e => e.Value).ToList()));
                                            }
                                        }
                                    }

                                    // If there are any events for any of the entities that this person follows, send a notification
                                    if (personEventTypeNotices.Any())
                                    {
                                        // Send the notice
                                        var recipients  = new List <RecipientData>();
                                        var mergeFields = new Dictionary <string, object>();
                                        mergeFields.Add("Person", person);
                                        mergeFields.Add("EventTypes", personEventTypeNotices.OrderBy(e => e.EventType.Order).ToList());
                                        recipients.Add(new RecipientData(person.Email, mergeFields));
                                        Email.Send(systemEmailGuid.Value, recipients, appRoot);
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                exceptionMsgs.Add(string.Format("An exception occurred sending event notice to '{0}':{1}    {2}", person.FullName, Environment.NewLine, ex.Messages().AsDelimited(Environment.NewLine + "   ")));
                                ExceptionLogService.LogException(ex, System.Web.HttpContext.Current);
                            }
                        }
                    }
                }

                if (exceptionMsgs.Any())
                {
                    throw new Exception("One or more exceptions occurred calculating following events..." + Environment.NewLine + exceptionMsgs.AsDelimited(Environment.NewLine));
                }
            }
        }
Exemple #10
0
        /// <summary>
        /// Renders the specified context.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="result">The result.</param>
        /// <exception cref="System.Exception">Your Lava command must contain at least one valid filter. If you configured a filter it's possible that the property or attribute you provided does not exist.</exception>
        public override void Render(Context context, TextWriter result)
        {
            // first ensure that entity commands are allowed in the context
            if (!this.IsAuthorized(context))
            {
                result.Write(string.Format(RockLavaBlockBase.NotAuthorizedMessage, this.Name));
                base.Render(context, result);
                return;
            }

            bool hasFilter = false;

            // get a service for the entity based off it's friendly name
            var entityTypes = EntityTypeCache.All();

            var model = string.Empty;

            if (_entityName == "business")
            {
                model = "Rock.Model.Person";
            }
            else
            {
                model = "Rock.Model." + _entityName;
            }

            // Check first to see if this is a core model
            var entityTypeCache = entityTypes.Where(e => String.Equals(e.Name, model, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

            // If not, look for first plugin model that has same friendly name
            if (entityTypeCache == null)
            {
                entityTypeCache = entityTypes
                                  .Where(e =>
                                         e.IsEntity &&
                                         !e.Name.StartsWith("Rock.Model") &&
                                         e.FriendlyName != null &&
                                         e.FriendlyName.RemoveSpaces().ToLower() == _entityName)
                                  .OrderBy(e => e.Id)
                                  .FirstOrDefault();
            }

            // If still null check to see if this was a duplicate class and full class name was used as entity name
            if (entityTypeCache == null)
            {
                model           = _entityName.Replace('_', '.');
                entityTypeCache = entityTypes.Where(e => String.Equals(e.Name, model, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
            }

            if (entityTypeCache != null)
            {
                Type entityType = entityTypeCache.GetEntityType();
                if (entityType != null)
                {
                    // Get the database context
                    Type contextType = null;
                    Rock.Data.DbContext dbContext = null;
                    var contexts = Rock.Reflection.SearchAssembly(entityType.Assembly, typeof(Rock.Data.DbContext));
                    if (contexts.Any())
                    {
                        contextType = contexts.First().Value;
                        dbContext   = Activator.CreateInstance(contextType) as Rock.Data.DbContext;
                    }
                    if (dbContext == null)
                    {
                        dbContext = _rockContext;
                    }

                    // create an instance of the entity's service
                    Type[]             modelType          = { entityType };
                    Type               genericServiceType = typeof(Rock.Data.Service <>);
                    Type               modelServiceType   = genericServiceType.MakeGenericType(modelType);
                    Rock.Data.IService serviceInstance    = Activator.CreateInstance(modelServiceType, new object[] { dbContext }) as IService;

                    ParameterExpression paramExpression = Expression.Parameter(entityType, "x");
                    Expression          queryExpression = null; // the base expression we'll use to build our query from

                    // parse markup
                    var parms = ParseMarkup(_markup, context);

                    if (parms.Any(p => p.Key == "id"))
                    {
                        string propertyName = "Id";

                        List <string> selectionParms = new List <string>();
                        selectionParms.Add(PropertyComparisonConverstion("==").ToString());
                        selectionParms.Add(parms["id"].ToString());
                        selectionParms.Add(propertyName);

                        var entityProperty = entityType.GetProperty(propertyName);
                        queryExpression = ExpressionHelper.PropertyFilterExpression(selectionParms, paramExpression, propertyName, entityProperty.PropertyType);

                        hasFilter = true;
                    }
                    else
                    {
                        // where clause expression
                        if (parms.Any(p => p.Key == "where"))
                        {
                            queryExpression = ParseWhere(parms["where"], entityType, serviceInstance, paramExpression, entityType, entityTypeCache);

                            if (queryExpression != null)
                            {
                                hasFilter = true;
                            }
                        }

                        // dataview expression
                        if (parms.Any(p => p.Key == "dataview"))
                        {
                            var dataViewId = parms["dataview"].AsIntegerOrNull();

                            if (dataViewId.HasValue)
                            {
                                var dataViewExpression = GetDataViewExpression(dataViewId.Value, serviceInstance, paramExpression, entityTypeCache);

                                if (queryExpression == null)
                                {
                                    queryExpression = dataViewExpression;
                                    hasFilter       = true;
                                }
                                else
                                {
                                    queryExpression = Expression.AndAlso(queryExpression, dataViewExpression);
                                }
                            }
                        }

                        // process dynamic filter expressions (from the query string)
                        if (parms.Any(p => p.Key == "dynamicparameters"))
                        {
                            var dynamicFilters = parms["dynamicparameters"].Split(',')
                                                 .Select(x => x.Trim())
                                                 .Where(x => !string.IsNullOrWhiteSpace(x))
                                                 .ToList();

                            foreach (var dynamicFilter in dynamicFilters)
                            {
                                var dynamicFilterValue      = HttpContext.Current.Request[dynamicFilter];
                                var dynamicFilterExpression = GetDynamicFilterExpression(dynamicFilter, dynamicFilterValue, entityType, serviceInstance, paramExpression);
                                if (dynamicFilterExpression != null)
                                {
                                    if (queryExpression == null)
                                    {
                                        queryExpression = dynamicFilterExpression;
                                        hasFilter       = true;
                                    }
                                    else
                                    {
                                        queryExpression = Expression.AndAlso(queryExpression, dynamicFilterExpression);
                                    }
                                }
                            }
                        }
                    }

                    // make the query from the expression
                    MethodInfo getMethod = serviceInstance.GetType().GetMethod("Get", new Type[] { typeof(ParameterExpression), typeof(Expression), typeof(Rock.Web.UI.Controls.SortProperty), typeof(int?) });
                    if (getMethod != null)
                    {
                        var queryResult = getMethod.Invoke(serviceInstance, new object[] { paramExpression, queryExpression, null, null }) as IQueryable <IEntity>;

                        // process entity specific filters
                        switch (_entityName)
                        {
                        case "person":
                        {
                            queryResult = PersonFilters((IQueryable <Person>)queryResult, parms);
                            break;
                        }

                        case "business":
                        {
                            queryResult = BusinessFilters((IQueryable <Person>)queryResult, parms);
                            break;
                        }
                        }

                        // if there was a dynamic expression add it now
                        if (parms.Any(p => p.Key == "expression"))
                        {
                            queryResult = queryResult.Where(parms["expression"]);
                            hasFilter   = true;
                        }

                        // get a listing of ids
                        if (parms.Any(p => p.Key == "ids"))
                        {
                            var value = parms["ids"].ToString().Split(',').Select(int.Parse).ToList();
                            queryResult = queryResult.Where(x => value.Contains(x.Id));
                            hasFilter   = true;
                        }

                        var queryResultExpression = queryResult.Expression;

                        // add sort expressions
                        if (parms.Any(p => p.Key == "sort"))
                        {
                            string orderByMethod = "OrderBy";


                            foreach (var column in parms["sort"].Split(',').Select(x => x.Trim()).Where(x => !string.IsNullOrWhiteSpace(x)).ToList())
                            {
                                string propertyName;
                                var    direction = SortDirection.Ascending;

                                if (column.EndsWith(" desc", StringComparison.OrdinalIgnoreCase))
                                {
                                    direction    = SortDirection.Descending;
                                    propertyName = column.Left(column.Length - 5);
                                }
                                else
                                {
                                    propertyName = column;
                                }

                                string methodName = direction == SortDirection.Descending ? orderByMethod + "Descending" : orderByMethod;

                                if (entityType.GetProperty(propertyName) != null)
                                {
                                    // sorting a entity property
                                    var memberExpression          = Expression.Property(paramExpression, propertyName);
                                    LambdaExpression sortSelector = Expression.Lambda(memberExpression, paramExpression);
                                    queryResultExpression = Expression.Call(typeof(Queryable), methodName, new Type[] { queryResult.ElementType, sortSelector.ReturnType }, queryResultExpression, sortSelector);
                                }
                                else
                                {
                                    // sorting on an attribute

                                    // get attribute id
                                    int?attributeId = null;
                                    foreach (var id in AttributeCache.GetByEntity(entityTypeCache.Id).SelectMany(a => a.AttributeIds))
                                    {
                                        var attribute = AttributeCache.Get(id);
                                        if (attribute.Key == propertyName)
                                        {
                                            attributeId = id;
                                        }
                                    }

                                    if (attributeId.HasValue)
                                    {
                                        // get AttributeValue queryable and parameter
                                        var attributeValues = _rockContext.Set <AttributeValue>();
                                        ParameterExpression attributeValueParameter = Expression.Parameter(typeof(AttributeValue), "v");
                                        MemberExpression    idExpression            = Expression.Property(paramExpression, "Id");
                                        var attributeExpression = Attribute.Helper.GetAttributeValueExpression(attributeValues, attributeValueParameter, idExpression, attributeId.Value);

                                        LambdaExpression sortSelector = Expression.Lambda(attributeExpression, paramExpression);
                                        queryResultExpression = Expression.Call(typeof(Queryable), methodName, new Type[] { queryResult.ElementType, sortSelector.ReturnType }, queryResultExpression, sortSelector);
                                    }
                                }

                                orderByMethod = "ThenBy";
                            }
                        }

                        // reassemble the queryable with the sort expressions
                        queryResult = queryResult.Provider.CreateQuery(queryResultExpression) as IQueryable <IEntity>;

                        if (parms.GetValueOrNull("count").AsBoolean())
                        {
                            int countResult = queryResult.Count();
                            context.Scopes.Last()["count"] = countResult;
                        }
                        else
                        {
                            // run security check on each result
                            var items        = queryResult.ToList();
                            var itemsSecured = new List <IEntity>();

                            Person person = GetCurrentPerson(context);

                            foreach (IEntity item in items)
                            {
                                ISecured itemSecured = item as ISecured;
                                if (itemSecured == null || itemSecured.IsAuthorized(Authorization.VIEW, person))
                                {
                                    itemsSecured.Add(item);
                                }
                            }

                            queryResult = itemsSecured.AsQueryable();

                            // offset
                            if (parms.Any(p => p.Key == "offset"))
                            {
                                queryResult = queryResult.Skip(parms["offset"].AsInteger());
                            }

                            // limit, default to 1000
                            if (parms.Any(p => p.Key == "limit"))
                            {
                                queryResult = queryResult.Take(parms["limit"].AsInteger());
                            }
                            else
                            {
                                queryResult = queryResult.Take(1000);
                            }

                            // check to ensure we had some form of filter (otherwise we'll return all results in the table)
                            if (!hasFilter)
                            {
                                throw new Exception("Your Lava command must contain at least one valid filter. If you configured a filter it's possible that the property or attribute you provided does not exist.");
                            }

                            var resultList = queryResult.ToList();

                            // if there is only one item to return set an alternative non-array based variable
                            if (resultList.Count == 1)
                            {
                                context.Scopes.Last()[_entityName] = resultList.FirstOrDefault();
                            }

                            context.Scopes.Last()[parms["iterator"]] = resultList;
                        }
                    }
                }
            }
            else
            {
                result.Write(string.Format("Could not find a model for {0}.", _entityName));
                base.Render(context, result);
            }

            base.Render(context, result);
        }