Beispiel #1
0
        /// <summary>
        /// Create a duplicate copy of this entity path and return it.
        /// </summary>
        /// <returns>A duplicate of this entity path.</returns>
        public EntityPath Clone()
        {
            EntityPath path = new EntityPath();

            path.AddRange(this);

            return(path);
        }
Beispiel #2
0
        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="a">The instance of this object that will is the left side of the operator.</param>
        /// <param name="b">The EntityPathComponent instance that is the right side of the operator.</param>
        /// <returns>
        /// The result of the operator.
        /// </returns>
        public static EntityPath operator +(EntityPath a, EntityPathComponent b)
        {
            EntityPath path = a.Clone();

            path.Add(b);

            return(path);
        }
Beispiel #3
0
 /// <summary>
 /// Determines if the entity at the given path requires a new Guid value when it's imported
 /// onto the target system. On import, if an entity of that type and Guid already exists then
 /// it is not imported and a reference to the existing entity is used instead.
 /// </summary>
 /// <param name="path">The path to the queued entity object that is being checked.</param>
 /// <returns>
 ///   <c>true</c> if the path requires a new Guid value; otherwise, <c>false</c>
 /// </returns>
 public bool DoesPathNeedNewGuid(EntityPath path)
 {
     return(path == "" ||
            path == "AttributeTypes" ||
            path == "AttributeTypes.AttributeQualifiers" ||
            path == "ActivityTypes" ||
            path == "ActivityTypes.AttributeTypes" ||
            path == "ActivityTypes.AttributeTypes.AttributeQualifiers" ||
            path == "ActivityTypes.ActionTypes" ||
            path == "ActivityTypes.ActionTypes.AttributeValues" ||
            path == "ActivityTypes.ActionTypes.WorkflowFormId" ||
            path == "ActivityTypes.ActionTypes.WorkflowFormId.FormAttributes");
 }
Beispiel #4
0
        /// <summary>
        /// Determines if the property at the given path should be followed to it's referenced entity.
        /// This is called for both referenced entities and child entities.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <returns></returns>
        public bool ShouldFollowPathProperty(EntityPath path)
        {
            if (path == "CategoryId")
            {
                return(false);
            }

            if (path.Count > 0)
            {
                var lastComponent = path.Last();

                if (lastComponent.Entity.TypeName == "Rock.Model.DefinedType" && lastComponent.PropertyName == "DefinedValues")
                {
                    return(false);
                }
            }

            return(true);
        }
Beispiel #5
0
 /// <summary>
 /// Determines whether the path to an entity should be considered critical. A critical
 /// entity is one that MUST exist on the target system in order for the export/import to
 /// succeed, as such a critical entity is always included.
 /// </summary>
 /// <param name="path">The path to the queued entity object that is being checked.</param>
 /// <returns>
 ///   <c>true</c> if the path is critical; otherwise, <c>false</c>.
 /// </returns>
 public bool IsPathCritical(EntityPath path)
 {
     return(DoesPathNeedNewGuid(path));
 }
Beispiel #6
0
        /// <summary>
        /// Gets any custom references for the entity at the given path.
        /// </summary>
        /// <param name="parentEntity">The entity that will later be encoded.</param>
        /// <param name="path">The path to the parent entity.</param>
        /// <returns>
        /// A collection of references that should be applied to the encoded entity.
        /// </returns>
        public ICollection <Reference> GetUserReferencesForPath(IEntity parentEntity, EntityPath path)
        {
            if (path == "")
            {
                return(new List <Reference>
                {
                    Reference.UserDefinedReference("CategoryId", "WorkflowCategory")
                });
            }

            return(null);
        }
Beispiel #7
0
        /// <summary>
        /// Generate the list of entities that reference this parent entity. These are entities that
        /// must be created after this entity has been created.
        /// </summary>
        /// <param name="parentEntity">The parent entity to find reverse-references to.</param>
        /// <param name="path">The property path that led us to this final property.</param>
        /// <param name="exporter">The object that handles filtering during an export process.</param>
        /// <returns>A list of KeyValuePairs that identify the property names and entites to be followed.</returns>
        protected List <KeyValuePair <string, IEntity> > FindChildEntities(IEntity parentEntity, EntityPath path, IExporter exporter)
        {
            List <KeyValuePair <string, IEntity> > children = new List <KeyValuePair <string, IEntity> >();

            var properties = GetEntityProperties(parentEntity);

            //
            // Take a stab at any properties that are an ICollection<IEntity> and treat those
            // as child entities.
            //
            foreach (var property in properties)
            {
                if (property.PropertyType.GetInterface("IEnumerable") != null && property.PropertyType.GetGenericArguments().Length == 1)
                {
                    if (typeof(IEntity).IsAssignableFrom(property.PropertyType.GetGenericArguments()[0]) && exporter.ShouldFollowPathProperty(path + new EntityPathComponent(parentEntity, property.Name)))
                    {
                        IEnumerable childEntities = ( IEnumerable )property.GetValue(parentEntity);

                        foreach (IEntity childEntity in childEntities)
                        {
                            children.Add(new KeyValuePair <string, IEntity>(property.Name, childEntity));
                        }
                    }
                }
            }

            //
            // We also need to pull in any attribute values. We have to pull attributes as well
            // since we might not have an actual value for that attribute yet and would need
            // it to pull the default value and definition.
            //
            if (parentEntity is IHasAttributes attributedEntity)
            {
                if (attributedEntity.Attributes == null)
                {
                    attributedEntity.LoadAttributes(RockContext);
                }

                foreach (var item in attributedEntity.Attributes)
                {
                    var attrib = new AttributeService(RockContext).Get(item.Value.Guid);

                    children.Add(new KeyValuePair <string, IEntity>("Attributes", attrib));

                    var value = new AttributeValueService(RockContext).Queryable()
                                .Where(v => v.AttributeId == attrib.Id && v.EntityId == attributedEntity.Id)
                                .FirstOrDefault();
                    if (value != null)
                    {
                        children.Add(new KeyValuePair <string, IEntity>("AttributeValues", value));
                    }
                }
            }

            //
            // Allow for processors to adjust the list of children.
            //
            foreach (var processor in FindEntityProcessors(GetEntityType(parentEntity)))
            {
                processor.EvaluateChildEntities(parentEntity, children, this);
            }

            return(children);
        }
Beispiel #8
0
        /// <summary>
        /// Find entities that this object references directly. These are entities that must be
        /// created before this entity can be re-created.
        /// </summary>
        /// <param name="parentEntity">The parent entity whose references we need to find.</param>
        /// <param name="path">The property path that led us to this final property.</param>
        /// <param name="exporter">The object that handles filtering during an export process.</param>
        /// <returns>A dictionary that identify the property names and entites to be followed.</returns>
        protected List <KeyValuePair <string, IEntity> > FindReferencedEntities(IEntity parentEntity, EntityPath path, IExporter exporter)
        {
            var references = new List <KeyValuePair <string, IEntity> >();
            var properties = GetEntityProperties(parentEntity);

            //
            // Take a stab at any properties that end in "Id" and likely reference another
            // entity, such as a property called "WorkflowId" probably references the Workflow
            // entity and should be linked by Guid.
            //
            foreach (var property in properties)
            {
                if (property.Name.EndsWith("Id") && (property.PropertyType == typeof(int) || property.PropertyType == typeof(Nullable <int>)))
                {
                    var entityProperty = parentEntity.GetType().GetProperty(property.Name.Substring(0, property.Name.Length - 2));

                    if (entityProperty == null || !typeof(IEntity).IsAssignableFrom(entityProperty.PropertyType))
                    {
                        continue;
                    }

                    var value = ( IEntity )entityProperty.GetValue(parentEntity);

                    if (exporter.ShouldFollowPathProperty(path + new EntityPathComponent(parentEntity, property.Name)))
                    {
                        references.Add(new KeyValuePair <string, IEntity>(property.Name, value));
                    }
                    else
                    {
                        references.Add(new KeyValuePair <string, IEntity>(property.Name, null));
                    }
                }
            }

            //
            // Allow for processors to adjust the list of children.
            //
            foreach (var processor in FindEntityProcessors(GetEntityType(parentEntity)))
            {
                processor.EvaluateReferencedEntities(parentEntity, references, this);
            }

            return(references);
        }
Beispiel #9
0
        /// <summary>
        /// Adds an entity to the queue list. This provides circular reference checking as
        /// well as ensuring that proper order is maintained for all entities.
        /// </summary>
        /// <param name="entity">The entity that is to be included in the export.</param>
        /// <param name="path">The entity path that lead to this entity being encoded.</param>
        /// <param name="entityIsCritical">True if the entity is critical, that is referenced directly.</param>
        /// <param name="exporter">The exporter that will handle processing for this entity.</param>
        protected void EnqueueEntity(IEntity entity, EntityPath path, bool entityIsCritical, IExporter exporter)
        {
            //
            // These are system generated rows, we should never try to backup or restore them.
            //
            if (entity.TypeName == "Rock.Model.EntityType" || entity.TypeName == "Rock.Model.FieldType")
            {
                return;
            }

            //
            // If the entity is already in our path that means we are beginning a circular
            // reference so we can just ignore this one.
            //
            if (path.Where(e => e.Entity.Guid == entity.Guid).Any())
            {
                return;
            }

            //
            // Find the entities that this entity references, in other words entities that must
            // exist before this one can be created, and queue them up.
            //
            var referencedEntities = FindReferencedEntities(entity, path, exporter);

            foreach (var r in referencedEntities)
            {
                if (r.Value != null)
                {
                    var refPath = path + new EntityPathComponent(entity, r.Key);
                    EnqueueEntity(r.Value, refPath, true, exporter);
                }
            }

            //
            // If we already know about the entity, add a reference to it and return.
            //
            var queuedEntity = Entities.Where(e => e.Entity.Guid == entity.Guid).FirstOrDefault();

            if (queuedEntity == null)
            {
                queuedEntity = new QueuedEntity(entity, path.Clone());
                Entities.Add(queuedEntity);
            }
            else
            {
                //
                // We have already visited this entity from the same parent. Not sure why we are here.
                //
                if (path.Any() && queuedEntity.ReferencePaths.Where(r => r.Any() && r.Last().Entity.Guid == path.Last().Entity.Guid).Any())
                {
                    return;
                }

                queuedEntity.AddReferencePath(path.Clone());
            }

            //
            // Add any new referenced properties/entities that may have been supplied for this
            // entity, as it's possible that has changed based on the path we took to get here.
            //
            foreach (var r in referencedEntities)
            {
                if (!queuedEntity.ReferencedEntities.Any(e => e.Key == r.Key && e.Value == r.Value))
                {
                    queuedEntity.ReferencedEntities.Add(new KeyValuePair <string, IEntity>(r.Key, r.Value));
                }
            }

            //
            // Mark the entity as critical if it's a root entity or is otherwise specified as critical.
            //
            if (path.Count == 0 || entityIsCritical || exporter.IsPathCritical(path))
            {
                queuedEntity.IsCritical = true;
            }

            //
            // Mark the entity as requiring a new Guid value if so indicated.
            //
            if (exporter.DoesPathNeedNewGuid(path))
            {
                queuedEntity.RequiresNewGuid = true;
            }

            //
            // Find the entities that this entity has as children. This is usually the many side
            // of a one-to-many reference (such as a Workflow has many WorkflowActions, this would
            // get a list of the WorkflowActions).
            //
            var children = FindChildEntities(entity, path, exporter);

            children.ForEach(e => EnqueueEntity(e.Value, path + new EntityPathComponent(entity, e.Key), false, exporter));

            //
            // Allow the exporter a chance to add custom reference values.
            //
            var userReferences = exporter.GetUserReferencesForPath(entity, path);

            if (userReferences != null && userReferences.Any())
            {
                foreach (var r in userReferences)
                {
                    queuedEntity.UserReferences.AddOrReplace(r.Property, r);
                }
            }
        }
Beispiel #10
0
 public bool DoesPathNeedNewGuid(EntityPath path)
 {
     return(path == "" ||
            path == "AttributeTypes" ||
            path == "AttributeTypes.AttributeQualifiers");
 }