/// <summary>
        /// Get a list of all inherited Attributes that should be applied to this entity.
        /// </summary>
        /// <returns>A list of all inherited AttributeCache objects.</returns>
        public override List <AttributeCache> GetInheritedAttributes(Rock.Data.RockContext rockContext)
        {
            var entityTypeCache = EntityTypeCache.Get(TypeId);

            // Get the registration
            var registration = this.Registration;

            if (registration == null && this.RegistrationId > 0)
            {
                registration = new RegistrationService(rockContext)
                               .Queryable().AsNoTracking()
                               .FirstOrDefault(r => r.Id == this.RegistrationId);
            }

            if (entityTypeCache == null || registration == null)
            {
                return(null);
            }

            // Get the instance
            var registrationInstance = registration.RegistrationInstance;

            if (registrationInstance == null && registration.RegistrationInstanceId > 0)
            {
                registrationInstance = new RegistrationInstanceService(rockContext)
                                       .Queryable().AsNoTracking()
                                       .FirstOrDefault(r => r.Id == registration.RegistrationInstanceId);
            }

            if (registrationInstance == null)
            {
                return(null);
            }

            // Get all attributes there were defined for instance's template.
            var attributes = new List <AttributeCache>();

            foreach (var entityTypeAttribute in AttributeCache.GetByEntityType(entityTypeCache.Id)
                     .Where(e =>
                            e.EntityTypeQualifierColumn == "RegistrationTemplateId" &&
                            e.EntityTypeQualifierValue.AsInteger() == registrationInstance.RegistrationTemplateId))
            {
                attributes.Add(entityTypeAttribute);
            }

            return(attributes);
        }
Example #2
0
        /// <summary>
        /// Get a list of all inherited Attributes that should be applied to this entity.
        /// </summary>
        /// <returns>A list of all inherited AttributeCache objects.</returns>
        public override List <AttributeCache> GetInheritedAttributes(Rock.Data.RockContext rockContext)
        {
            var calendarIds = this.EventCalendarItems.Select(c => c.EventCalendarId).ToList();

            if (!calendarIds.Any())
            {
                return(null);
            }

            var inheritedAttributes = new Dictionary <int, List <AttributeCache> >();

            calendarIds.ForEach(c => inheritedAttributes.Add(c, new List <AttributeCache>()));

            //
            // Check for any calendar item attributes that the event item inherits.
            //
            var calendarItemEntityType = EntityTypeCache.Get(typeof(EventCalendarItem));

            if (calendarItemEntityType != null)
            {
                foreach (var calendarItemEntityAttribute in AttributeCache
                         .GetByEntityType(calendarItemEntityType.Id)
                         .Where(a =>
                                a.EntityTypeQualifierColumn == "EventCalendarId" &&
                                calendarIds.Contains(a.EntityTypeQualifierValue.AsInteger())))
                {
                    inheritedAttributes[calendarItemEntityAttribute.EntityTypeQualifierValue.AsInteger()].Add(calendarItemEntityAttribute);
                }
            }

            //
            // Walk the generated list of attribute groups and put them, ordered, into a list
            // of inherited attributes.
            //
            var attributes = new List <AttributeCache>();

            foreach (var attributeGroup in inheritedAttributes)
            {
                foreach (var attribute in attributeGroup.Value.OrderBy(a => a.Order))
                {
                    attributes.Add(attribute);
                }
            }

            return(attributes);
        }
Example #3
0
        /// <summary>
        /// Gets a list of all attributes defined for the GroupTypes specified that
        /// match the entityTypeQualifierColumn and the GroupType Ids.
        /// </summary>
        /// <param name="rockContext">The database context to operate in.</param>
        /// <param name="entityTypeId">The Entity Type Id for which Attributes to load.</param>
        /// <param name="entityTypeQualifierColumn">The EntityTypeQualifierColumn value to match against.</param>
        /// <returns>A list of attributes defined in the inheritance tree.</returns>
        public List <AttributeCache> GetInheritedAttributesForQualifier(Rock.Data.RockContext rockContext, int entityTypeId, string entityTypeQualifierColumn)
        {
            var groupTypeIds = GetInheritedGroupTypeIds(rockContext);

            var inheritedAttributes = new Dictionary <int, List <AttributeCache> >();

            groupTypeIds.ForEach(g => inheritedAttributes.Add(g, new List <AttributeCache>()));

            //
            // Walk each group type and generate a list of matching attributes.
            //
            foreach (var entityTypeAttribute in AttributeCache.GetByEntityType(entityTypeId))
            {
                // group type ids exist and qualifier is for a group type id
                if (string.Compare(entityTypeAttribute.EntityTypeQualifierColumn, entityTypeQualifierColumn, true) == 0)
                {
                    int groupTypeIdValue = int.MinValue;
                    if (int.TryParse(entityTypeAttribute.EntityTypeQualifierValue, out groupTypeIdValue) && groupTypeIds.Contains(groupTypeIdValue))
                    {
                        inheritedAttributes[groupTypeIdValue].Add(entityTypeAttribute);
                    }
                }
            }

            //
            // Walk the generated list of attribute groups and put them, ordered, into a list
            // of inherited attributes.
            //
            var attributes = new List <AttributeCache>();

            foreach (var attributeGroup in inheritedAttributes)
            {
                foreach (var attribute in attributeGroup.Value.OrderBy(a => a.Order))
                {
                    attributes.Add(attribute);
                }
            }

            return(attributes);
        }
        /// <summary>
        /// Filters the attributes.
        /// </summary>
        /// <param name="rockContext">The rock context.</param>
        /// <param name="items">The items.</param>
        /// <param name="person">The person.</param>
        private static void FilterAttributes(Data.RockContext rockContext, IEnumerable <Attribute.IHasAttributes> items, Rock.Model.Person person)
        {
            if (!items.Any())
            {
                return;
            }

            var itemType = items.First().GetType();

            var entityType = EntityTypeCache.Get(itemType);

            if (entityType == null)
            {
                // shouldn't happen
                return;
            }

            var entityTypeAttributes = AttributeCache.GetByEntityType(entityType.Id);

            // only return attributes that the person has VIEW auth to
            // NOTE: There are some Attributes that even Admin doesn't have VIEW auth so (For example, some of obsolete DISC attributes)
            foreach (var attribute in entityTypeAttributes)
            {
                if (!attribute.IsAuthorized(Rock.Security.Authorization.VIEW, person))
                {
                    foreach (var item in items)
                    {
                        if (item.AttributeValues.ContainsKey(attribute.Key))
                        {
                            item.AttributeValues.Remove(attribute.Key);
                        }

                        if (item.Attributes.ContainsKey(attribute.Key))
                        {
                            item.Attributes.Remove(attribute.Key);
                        }
                    }
                }
            }
        }
Example #5
0
        /// <summary>
        /// Gets a list of all attributes defined for the ConnectionTypes specified that
        /// match the entityTypeQualifierColumn and the ConnectionRequest Ids.
        /// </summary>
        /// <param name="rockContext">The database context to operate in.</param>
        /// <param name="entityTypeId">The Entity Type Id for which Attributes to load.</param>
        /// <param name="entityTypeQualifierColumn">The EntityTypeQualifierColumn value to match against.</param>
        /// <returns>A list of attributes defined in the inheritance tree.</returns>
        public List <AttributeCache> GetInheritedAttributesForQualifier(Rock.Data.RockContext rockContext, int entityTypeId, string entityTypeQualifierColumn)
        {
            var attributes = new List <AttributeCache>();

            //
            // Walk each group type and generate a list of matching attributes.
            //
            foreach (var attribute in AttributeCache.GetByEntityType(entityTypeId))
            {
                // group type ids exist and qualifier is for a group type id
                if (string.Compare(attribute.EntityTypeQualifierColumn, entityTypeQualifierColumn, true) == 0)
                {
                    int groupTypeIdValue = int.MinValue;
                    if (int.TryParse(attribute.EntityTypeQualifierValue, out groupTypeIdValue) && this.Id == groupTypeIdValue)
                    {
                        attributes.Add(attribute);
                    }
                }
            }

            return(attributes.OrderBy(a => a.Order).ToList());
        }
Example #6
0
        /// <summary>
        /// Parses the where.
        /// </summary>
        /// <param name="whereClause">The where clause.</param>
        /// <param name="type">The type.</param>
        /// <param name="service">The service.</param>
        /// <param name="parmExpression">The parm expression.</param>
        /// <param name="entityType">Type of the entity.</param>
        /// <param name="entityTypeCache">The entity type cache.</param>
        /// <returns></returns>
        private Expression ParseWhere(string whereClause, Type type, IService service, ParameterExpression parmExpression, Type entityType, EntityTypeCache entityTypeCache)
        {
            Expression returnExpression = null;

            // find locations of and/or's
            var expressionComponents = Regex.Split(whereClause, @"(\|\||&&)");

            var currentExpressionComparisonType = ExpressionComparisonType.And;

            foreach (var component in expressionComponents)
            {
                if (component == "||")
                {
                    currentExpressionComparisonType = ExpressionComparisonType.Or;
                    continue;
                }

                if (component == "&&")
                {
                    currentExpressionComparisonType = ExpressionComparisonType.And;
                    continue;
                }

                // parse the part to get the expression
                string regexPattern    = @"([a-zA-Z]+)|(==|<=|>=|<|!=|\^=|\*=|\*!|_=|_!|>|\$=|#=)|("".*""|\d+)";
                var    expressionParts = Regex.Matches(component, regexPattern)
                                         .Cast <Match>()
                                         .Select(m => m.Value)
                                         .ToList();

                if (expressionParts.Count == 3)
                {
                    var property     = expressionParts[0];
                    var operatorType = expressionParts[1];
                    var value        = expressionParts[2].Replace("\"", "");

                    List <string> selectionParms = new List <string>();
                    selectionParms.Add(PropertyComparisonConversion(operatorType).ToString());
                    selectionParms.Add(value);
                    selectionParms.Add(property);

                    Expression expression = null;

                    if (entityType.GetProperty(property) != null)
                    {
                        var entityProperty = entityType.GetProperty(property);
                        expression = ExpressionHelper.PropertyFilterExpression(selectionParms, parmExpression, property, entityProperty.PropertyType);
                    }
                    else
                    {
                        AttributeCache filterAttribute          = null;
                        Expression     attributeWhereExpression = null;

                        var attributeKey = property;

                        // We would really love to further qualify this beyond the EntityType by including the
                        // EntityTypeQualifier and EntityTypeQualifierValue but we can't easily do that so, we
                        // will do that "Just in case..." code below (because this actually happened in our Spark
                        // environment.
                        // Also, there could be multiple attributes that have the same key (due to attribute qualifiers or just simply a duplicate key)
                        var entityAttributeListForAttributeKey = AttributeCache.GetByEntityType(entityTypeCache.Id)
                                                                 .Where(a => a != null && a.Key == attributeKey).ToList();

                        // Just in case this EntityType has multiple attributes with the same key, create a OR'd clause for each attribute that has this key
                        // NOTE: this could easily happen if doing an entity command against a DefinedValue, and the same attribute key is used in more than one defined type
                        foreach (var attribute in entityAttributeListForAttributeKey)
                        {
                            filterAttribute = attribute;
                            var attributeEntityField = EntityHelper.GetEntityFieldForAttribute(filterAttribute);

                            if (attributeWhereExpression == null)
                            {
                                attributeWhereExpression = ExpressionHelper.GetAttributeExpression(service, parmExpression, attributeEntityField, selectionParms);
                            }
                            else
                            {
                                attributeWhereExpression = Expression.OrElse(attributeWhereExpression, ExpressionHelper.GetAttributeExpression(service, parmExpression, attributeEntityField, selectionParms));
                            }
                        }

                        if (attributeWhereExpression != null)
                        {
                            expression = attributeWhereExpression;
                        }
                    }

                    if (returnExpression == null)
                    {
                        returnExpression = expression;
                    }
                    else
                    {
                        if (expression == null)
                        {
                            // unable to match to property or attribute, so just return what we got
                            return(returnExpression);
                        }

                        if (currentExpressionComparisonType == ExpressionComparisonType.And)
                        {
                            returnExpression = Expression.AndAlso(returnExpression, expression);
                        }
                        else
                        {
                            returnExpression = Expression.OrElse(returnExpression, expression);
                        }
                    }
                }
                else
                {
                    // error in parsing expression
                    throw new Exception("Error in Where expression");
                }
            }

            return(returnExpression);
        }
Example #7
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;

            var modelName = string.Empty;

            // get a service for the entity based off it's friendly name
            if (_entityName == "business")
            {
                modelName = "Rock.Model.Person";
            }
            else
            {
                modelName = "Rock.Model." + _entityName;
            }

            // Check first to see if this is a core model. use the createIfNotFound = false option
            var entityTypeCache = EntityTypeCache.Get(modelName, false);

            if (entityTypeCache == null)
            {
                var entityTypes = EntityTypeCache.All();

                // If not, look for first plug-in model that has same friendly name
                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)
                {
                    modelName       = _entityName.Replace('_', '.');
                    entityTypeCache = entityTypes.Where(e => String.Equals(e.Name, modelName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
                }
            }

            if (entityTypeCache != null)
            {
                Type entityType = entityTypeCache.GetEntityType();
                if (entityType != null)
                {
                    // Get the appropriate database context for this entity type.
                    // Note that this may be different from the standard RockContext if the entity is sourced from a plug-in.
                    var dbContext = Reflection.GetDbContextForEntityType(entityType);

                    // Check if there is a RockContext in the Lava context. If so then use the RockContext passed in.
                    if (dbContext is RockContext)
                    {
                        var lavaContext = context.Registers["RockContext"];

                        if (lavaContext.IsNotNull())
                        {
                            dbContext = (DbContext)lavaContext;
                        }
                    }

                    // Disable change-tracking for this data context to improve performance - objects supplied to a Lava context are read-only.
                    dbContext.Configuration.AutoDetectChangesEnabled = false;

                    // Create an instance of the entity's service
                    IService serviceInstance = Reflection.GetServiceForEntityType(entityType, dbContext);

                    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(PropertyComparisonConversion("==").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.

                    /* [2020-10-08] DL
                     * "Get" is intentionally used here rather than "GetNoTracking" to allow lazy-loading of navigation properties from the Lava context.
                     * (Refer https://github.com/SparkDevNetwork/Rock/issues/4293)
                     */
                    MethodInfo getMethod = serviceInstance.GetType().GetMethod("Get", new Type[] { typeof(ParameterExpression), typeof(Expression), typeof(Rock.Web.UI.Controls.SortProperty), typeof(int?) });

                    if (getMethod != null)
                    {
                        // get a listing of ids and build it into the query expression
                        if (parms.Any(p => p.Key == "ids"))
                        {
                            List <int>         value = parms["ids"].ToString().Split(',').Select(int.Parse).ToList();
                            MemberExpression   propertyExpression = Expression.Property(paramExpression, "Id");
                            ConstantExpression constantExpression = Expression.Constant(value, typeof(List <int>));
                            Expression         containsExpression = Expression.Call(constantExpression, typeof(List <int>).GetMethod("Contains", new Type[] { typeof(int) }), propertyExpression);
                            if (queryExpression != null)
                            {
                                queryExpression = Expression.AndAlso(queryExpression, containsExpression);
                            }
                            else
                            {
                                queryExpression = containsExpression;
                            }

                            hasFilter = true;
                        }

                        var getResult   = getMethod.Invoke(serviceInstance, new object[] { paramExpression, queryExpression, null, null });
                        var queryResult = getResult 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;
                        }

                        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 attribute in AttributeCache.GetByEntityType(entityTypeCache.Id))
                                    {
                                        if (attribute.Key == propertyName)
                                        {
                                            attributeId = attribute.Id;
                                            break;
                                        }
                                    }

                                    if (attributeId.HasValue)
                                    {
                                        // get AttributeValue queryable and parameter
                                        if (dbContext is RockContext)
                                        {
                                            var attributeValues = new AttributeValueService(dbContext as RockContext).Queryable();
                                            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);
                                        }
                                        else
                                        {
                                            throw new Exception(string.Format("The database context for type {0} does not support RockContext attribute value queries.", entityTypeCache.FriendlyName));
                                        }
                                    }
                                }

                                orderByMethod = "ThenBy";
                            }
                        }

                        // 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.");
                        }

                        // 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 if enabled and entity is not a person (we do not check security on people)
                            if (parms["securityenabled"].AsBoolean() && _entityName != "person")
                            {
                                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);

                                        /*
                                         *      8/13/2020 - JME
                                         *      It might seem logical to break out of the loop if there is limit parameter provided once the
                                         *  limit is reached. This though has two issues.
                                         *
                                         *  FIRST
                                         *  Depending how it was implemented it can have the effect of breaking when an offset is
                                         *  provided.
                                         *          {% contentchannelitem where:'ContentChannelId == 1' limit:'3' %}
                                         *          {% for item in contentchannelitemItems %}
                                         *              {{ item.Id }} - {{ item.Title }}<br>
                                         *          {% endfor %}
                                         *      {% endcontentchannelitem %}
                                         *  Returns 3 items (correct)
                                         *
                                         *      {% contentchannelitem where:'ContentChannelId == 1' limit:'3' offset:'1' %}
                                         *          {% for item in contentchannelitemItems %}
                                         *              {{ item.Id }} - {{ item.Title }}<br>
                                         *          {% endfor %}
                                         *      {% endcontentchannelitem %}
                                         *  Returns only 2 items (incorrect) - because of the offset
                                         *
                                         *  SECOND
                                         *  If the limit is moved before the security check it's possible that the security checks
                                         *  will remove items and will therefore not give you the amount of items that you asked for.
                                         *
                                         *  Unfortunately this has to be an inefficent process to ensure pagination works. I will also
                                         *  add a detailed note to the documentation to encourage people to disable security checks,
                                         *  especially when used with pagination, in the Lava docs.
                                         */
                                    }
                                }

                                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);
                            }

                            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);
        }
        /// <summary>
        /// Called during serialization to write an object of the specified type to the specified stream.
        /// </summary>
        /// <param name="type">The type of the object to write.</param>
        /// <param name="value">The object to write.</param>
        /// <param name="writeStream">The stream to write to.</param>
        /// <param name="effectiveEncoding">The encoding to use when writing.</param>
        public override void WriteToStream(Type type, object value, System.IO.Stream writeStream, Encoding effectiveEncoding)
        {
            bool          isSelectAndExpand   = false;
            List <object> selectAndExpandList = null;

            if (value is IQueryable <object> && typeof(IQueryable <Rock.Data.IEntity>).IsAssignableFrom(type))
            {
                Type valueType = value.GetType();

                // if this is an OData 'SelectAndExpand', we have convert stuff to Dictionary manually to get the stuff to serialize the way we want
                if (valueType.IsGenericType && valueType.GenericTypeArguments[0].Name == "SelectAllAndExpand`1")
                {
                    isSelectAndExpand = true;

                    var selectExpandQry = value as IQueryable <object>;

                    selectAndExpandList = selectExpandQry.ToList();
                }
            }

            var loadAttributesOptions = HttpContext.Current.Items[LoadAttributesOptions.ContextItemsKey] as LoadAttributesOptions;

            if (loadAttributesOptions == null)
            {
                // shouldn't happen, but just in case
                loadAttributesOptions = new LoadAttributesOptions(false, false, null, null);
            }

            // query should be filtered by now, so iterate thru items and load attributes before the response is serialized
            if (loadAttributesOptions.LoadAttributesEnabled)
            {
                IEnumerable <Attribute.IHasAttributes> items = null;

                if (value is IEnumerable <Attribute.IHasAttributes> )
                {
                    // if the REST call specified that Attributes should be loaded and we are returning a list of IHasAttributes..
                    // Also, do a ToList() to fetch the query into a list (instead re-querying multiple times)
                    items = (value as IEnumerable <Attribute.IHasAttributes>).ToList();

                    // Assign the items list back to value
                    value = items;
                }
                else if (value is Attribute.IHasAttributes)
                {
                    // if the REST call specified that Attributes should be loaded and we are returning a single IHasAttributes..
                    items = new List <Attribute.IHasAttributes>(new Attribute.IHasAttributes[] { value as Attribute.IHasAttributes });
                }
                else if (isSelectAndExpand && selectAndExpandList != null)
                {
                    //// 'SelectAndExpand' buries the Entity in a private field called 'Instance',
                    //// so use reflection to get that and load the attributes for each

                    var itemsList = new List <Attribute.IHasAttributes>();
                    foreach (var selectExpandItem in selectAndExpandList)
                    {
                        var entityProperty = selectExpandItem.GetType().GetProperty("Instance");
                        var entity         = entityProperty.GetValue(selectExpandItem) as Attribute.IHasAttributes;
                        if (entity != null)
                        {
                            itemsList.Add(entity);
                        }

                        entityProperty.SetValue(selectExpandItem, entity);
                    }

                    items = itemsList;
                }

                List <AttributeCache> limitToAttributes = null;
                if (loadAttributesOptions.LimitToAttributeKeyList?.Any() == true && type.IsGenericType)
                {
                    var entityTypeType = type.GenericTypeArguments[0];
                    if (entityTypeType != null)
                    {
                        var entityType = EntityTypeCache.Get(entityTypeType);
                        var entityTypeAttributesList = AttributeCache.GetByEntityType(entityType.Id);
                        limitToAttributes = entityTypeAttributesList?.Where(a => loadAttributesOptions.LimitToAttributeKeyList.Contains(a.Key, StringComparer.OrdinalIgnoreCase)).ToList();
                    }
                }

                if (items != null)
                {
                    using (var rockContext = new Rock.Data.RockContext())
                    {
                        foreach (var item in items)
                        {
                            Rock.Attribute.Helper.LoadAttributes(item, rockContext, limitToAttributes);
                        }

                        FilterAttributes(rockContext, items, loadAttributesOptions.Person);
                    }
                }
            }

            // Special Code if an $expand clause is specified and a $select clause is NOT specified
            // This fixes a couple of issues:
            //  1) our special loadAttributes stuff didn't work if $expand is specified
            //  2) Only non-virtual,non-inherited fields were included (for example: Person.PrimaryAliasId, etc, wasn't getting included) if $expand was specified
            if (isSelectAndExpand)
            {
                using (var rockContext = new Rock.Data.RockContext())
                {
                    List <Dictionary <string, object> > valueAsDictionary = GetSelectAndExpandDictionaryObject(type, selectAndExpandList, rockContext, loadAttributesOptions);
                    base.WriteToStream(type, valueAsDictionary, writeStream, effectiveEncoding);
                }

                return;
            }

            base.WriteToStream(type, value, writeStream, effectiveEncoding);
        }
Example #9
0
        /// <summary>
        /// Gets the entity fields for a specific Entity
        /// </summary>
        /// <param name="entityType">Type of the entity.</param>
        /// <param name="includeOnlyReportingFields">if set to <c>true</c> [include only reporting fields].</param>
        /// <param name="limitToFilterableFields">if set to <c>true</c> [limit to filterable fields].</param>
        /// <returns></returns>
        public static List <EntityField> GetEntityFields(Type entityType, bool includeOnlyReportingFields = true, bool limitToFilterableFields = true)
        {
            List <EntityField> entityFields = null;

            _workflowTypeNameLookup = null;

            if (HttpContext.Current != null)
            {
                entityFields = HttpContext.Current.Items[EntityHelper.GetCacheKey(entityType, includeOnlyReportingFields, limitToFilterableFields)] as List <EntityField>;
                if (entityFields != null)
                {
                    return(entityFields);
                }
            }

            if (entityFields == null)
            {
                entityFields = new List <EntityField>();
            }

            List <PropertyInfo> entityProperties = entityType.GetProperties().ToList();

            // filter the properties to narrow down the ones that we want to include in EntityFields
            var filteredEntityProperties = entityProperties
                                           .Where((p) =>
            {
                var includeForReportingAttribute = p.GetCustomAttribute <IncludeForReportingAttribute>() != null;

                // if the property has an IncludeForReportingAttribute, include it regardless
                if (includeForReportingAttribute)
                {
                    return(true);
                }

                bool hideFromReporting = false;
                if (includeOnlyReportingFields)
                {
                    hideFromReporting = p.GetCustomAttribute <HideFromReportingAttribute>() != null;
                }

                // if the property should be hidden from reporting, don't show it
                if (hideFromReporting)
                {
                    return(false);
                }

                bool isMappedDatabaseField = Reflection.IsMappedDatabaseProperty(p);
                if (!isMappedDatabaseField)
                {
                    return(false);
                }

                return(true);
            }).ToList();

            // Go thru filteredEntityProperties and create the list of EntityFields that we can include
            foreach (var property in filteredEntityProperties)
            {
                EntityField entityField = new EntityField(property.Name, FieldKind.Property, property);
                entityField.IsPreviewable = property.GetCustomAttributes(typeof(PreviewableAttribute), true).Any();
                var fieldTypeAttribute = property.GetCustomAttribute <Rock.Data.FieldTypeAttribute>();

                // check if we can set it from the fieldTypeAttribute
                if ((fieldTypeAttribute != null) && SetEntityFieldFromFieldTypeAttribute(entityField, fieldTypeAttribute))
                {
                    // intentionally blank, entity field is already setup
                }

                // Enum Properties
                else if (property.PropertyType.IsEnum)
                {
                    entityField.FieldType = FieldTypeCache.Get(SystemGuid.FieldType.SINGLE_SELECT.AsGuid());

                    var list = new List <string>();
                    foreach (var value in Enum.GetValues(property.PropertyType))
                    {
                        list.Add(string.Format("{0}^{1}", value, value.ToString().SplitCase()));
                    }

                    var listSource = string.Join(",", list);
                    entityField.FieldConfig.Add("values", new Field.ConfigurationValue(listSource));
                    entityField.FieldConfig.Add("fieldtype", new Field.ConfigurationValue("rb"));
                }

                // Boolean properties
                else if (property.PropertyType == typeof(bool) || property.PropertyType == typeof(bool?))
                {
                    entityField.FieldType = FieldTypeCache.Get(SystemGuid.FieldType.BOOLEAN.AsGuid());
                }

                // Datetime properties
                else if (property.PropertyType == typeof(DateTime) || property.PropertyType == typeof(DateTime? ))
                {
                    var colAttr = property.GetCustomAttributes(typeof(ColumnAttribute), true).FirstOrDefault();
                    if (colAttr != null && (( ColumnAttribute )colAttr).TypeName == "Date")
                    {
                        entityField.FieldType = FieldTypeCache.Get(SystemGuid.FieldType.DATE.AsGuid());
                    }
                    else
                    {
                        entityField.FieldType = FieldTypeCache.Get(SystemGuid.FieldType.DATE_TIME.AsGuid());
                    }
                }

                // Decimal properties
                else if (property.PropertyType == typeof(decimal) || property.PropertyType == typeof(decimal? ))
                {
                    entityField.FieldType = FieldTypeCache.Get(SystemGuid.FieldType.DECIMAL.AsGuid());
                }

                // Text Properties
                else if (property.PropertyType == typeof(string))
                {
                    entityField.FieldType = FieldTypeCache.Get(SystemGuid.FieldType.TEXT.AsGuid());
                }

                // Integer Properties (which may be a DefinedValue)
                else if (property.PropertyType == typeof(int) || property.PropertyType == typeof(int?))
                {
                    entityField.FieldType = FieldTypeCache.Get(SystemGuid.FieldType.INTEGER.AsGuid());

                    var definedValueAttribute = property.GetCustomAttribute <Rock.Data.DefinedValueAttribute>();
                    if (definedValueAttribute != null)
                    {
                        // Defined Value Properties
                        Guid?definedTypeGuid = definedValueAttribute.DefinedTypeGuid;
                        if (definedTypeGuid.HasValue)
                        {
                            var definedType = DefinedTypeCache.Get(definedTypeGuid.Value);
                            entityField.Title = definedType != null ? definedType.Name : property.Name.Replace("ValueId", string.Empty).SplitCase();
                            if (definedType != null)
                            {
                                entityField.FieldType = FieldTypeCache.Get(SystemGuid.FieldType.DEFINED_VALUE.AsGuid());
                                entityField.FieldConfig.Add("definedtype", new Field.ConfigurationValue(definedType.Id.ToString()));
                            }
                        }
                    }
                }

                if (entityField != null && entityField.FieldType != null)
                {
                    entityFields.Add(entityField);
                }
            }

            // Get Attributes
            var entityTypeCache = EntityTypeCache.Get(entityType, true);

            if (entityTypeCache != null)
            {
                int entityTypeId = entityTypeCache.Id;

                IEnumerable <AttributeCache> cacheAttributeList = AttributeCache.GetByEntityType(entityTypeCache.Id);

                if (entityType == typeof(Group) || entityType == typeof(GroupMember))
                {
                    // in the case of Group or GroupMember, show attributes that are entity global, but also ones that are qualified by GroupTypeId
                    cacheAttributeList = cacheAttributeList
                                         .Where(a =>
                                                a.EntityTypeQualifierColumn == null ||
                                                a.EntityTypeQualifierColumn == string.Empty ||
                                                a.EntityTypeQualifierColumn == "GroupTypeId");
                }
                else if (entityType == typeof(ConnectionRequest))
                {
                    // in the case of Connection Requests, show attributes that are entity global, but also ones that are qualified by ConnectionOpportunityId or ConnectionTypeId
                    cacheAttributeList = cacheAttributeList
                                         .Where(a =>
                                                a.EntityTypeQualifierColumn == null ||
                                                a.EntityTypeQualifierColumn == string.Empty ||
                                                a.EntityTypeQualifierColumn == "ConnectionOpportunityId" ||
                                                a.EntityTypeQualifierColumn == "ConnectionTypeId"
                                                );
                }
                else if (entityType == typeof(Registration))
                {
                    // in the case of Registrations, show attributes that are entity global, but also ones that are qualified by RegistrationTemplateId
                    cacheAttributeList = cacheAttributeList
                                         .Where(a =>
                                                a.EntityTypeQualifierColumn == null ||
                                                a.EntityTypeQualifierColumn == string.Empty ||
                                                a.EntityTypeQualifierColumn == "RegistrationTemplateId"
                                                );
                }
                else if (entityType == typeof(ContentChannelItem))
                {
                    // in the case of ContentChannelItem, show attributes that are entity global, but also ones that are qualified by ContentChannelTypeId or ContentChannelId
                    cacheAttributeList = cacheAttributeList
                                         .Where(a =>
                                                a.EntityTypeQualifierColumn == null ||
                                                a.EntityTypeQualifierColumn == string.Empty ||
                                                a.EntityTypeQualifierColumn == "ContentChannelTypeId" ||
                                                a.EntityTypeQualifierColumn == "ContentChannelId"
                                                );
                }
                else if (entityType == typeof(Rock.Model.Workflow))
                {
                    // in the case of Workflow, show attributes that are entity global, but also ones that are qualified by WorkflowTypeId (and have a valid WorkflowTypeId)
                    var validWorkflowTypeIds = WorkflowTypeCache.All().Select(a => a.Id.ToString()).ToArray();
                    cacheAttributeList = cacheAttributeList
                                         .Where(a =>
                                                a.EntityTypeQualifierColumn == null ||
                                                a.EntityTypeQualifierColumn == string.Empty ||
                                                (a.EntityTypeQualifierColumn == "WorkflowTypeId" && validWorkflowTypeIds.Contains(a.EntityTypeQualifierValue))).ToList();
                }
                else if (entityType == typeof(Note))
                {
                    // in the case of notes, show attributes that are entity global, but also ones that are qualified by ConnectionOpportunityId
                    cacheAttributeList = cacheAttributeList
                                         .Where(a =>
                                                a.EntityTypeQualifierColumn == null ||
                                                a.EntityTypeQualifierColumn == string.Empty ||
                                                a.EntityTypeQualifierColumn == "NoteTypeId"
                                                );
                }
                else
                {
                    cacheAttributeList = cacheAttributeList.Where(a => string.IsNullOrEmpty(a.EntityTypeQualifierColumn) && string.IsNullOrEmpty(a.EntityTypeQualifierValue)).ToList();
                }

                EntityHelper.AddEntityFieldsForAttributeList(entityFields, cacheAttributeList.ToList());
            }

            // Order the fields by title, name
            int index        = 0;
            var sortedFields = new List <EntityField>();

            foreach (var entityField in entityFields.OrderBy(p => !string.IsNullOrEmpty(p.AttributeEntityTypeQualifierName)).ThenBy(p => p.Title).ThenBy(p => p.Name))
            {
                entityField.Index = index;
                index++;
                sortedFields.Add(entityField);
            }

            if (HttpContext.Current != null)
            {
                HttpContext.Current.Items[EntityHelper.GetCacheKey(entityType, includeOnlyReportingFields, limitToFilterableFields)] = sortedFields;
            }

            return(sortedFields);
        }