Skip to content

Commit

Permalink
Changed: updated Common
Browse files Browse the repository at this point in the history
  • Loading branch information
yagasoft committed Dec 19, 2018
1 parent 91cdcac commit 13f5ccc
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 41 deletions.
230 changes: 193 additions & 37 deletions LinkDev.AutoNumbering.Plugins/Common.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis.
// Author: Ahmed el-Sawalhy (Yagasoft.com)
// Version: 3.8.1
// Version: 3.8.4
// <auto-generated />

#region Imports
Expand Down Expand Up @@ -37,6 +37,7 @@
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Workflow;
using Match = System.Text.RegularExpressions.Match;
using RelationshipType = Microsoft.Xrm.Sdk.Metadata.RelationshipType;

#endregion

Expand Down Expand Up @@ -466,6 +467,14 @@ public static string ToTitleCase(this string str)
return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(str.ToLower());
}

/// <summary>
/// Author: Ahmed el-Sawalhy
/// </summary>
public static bool IsEmpty(this string str)
{
return string.IsNullOrWhiteSpace(str);
}

/// <summary>
/// Author: Ahmed el-Sawalhy
/// </summary>
Expand Down Expand Up @@ -1321,7 +1330,7 @@ public static T GetFromMemCacheAdd<T>(string key, Func<T> cacheFiller, CachePara
/// Author: Ahmed el-Sawalhy
/// </summary>
[ExcludeFromCodeCoverage]
//[DebuggerNonUserCode]
[DebuggerNonUserCode]
[GeneratedCode("Not generated code, but used to exclude from Code Analysis.", "0.0.0.0")]
public static class RandomGenerator
{
Expand Down Expand Up @@ -2267,7 +2276,8 @@ public static void LogAttributeValues(AttributeCollection attributes, Entity lab
#region Relations helpers

public static List<Entity> GetRelatedRecords(IOrganizationService service, EntityReference entity,
MetadataHelpers.RelationType[] relationTypes, string orgId, params string[] attributes)
MetadataHelpers.RelationType[] relationTypes, string orgId,
FilterExpression filter = null, params string[] attributes)
{
var related = new List<Entity>();

Expand All @@ -2286,49 +2296,79 @@ public static List<Entity> GetRelatedRecords(IOrganizationService service, Entit
MetadataHelpers.RelationAttribute.Entity2LogicalName, MetadataHelpers.RelationAttribute.Entity2IntersectAttribute
};

var relations = MetadataHelpers
.GetCustomRelationships(service, entity.LogicalName, relationTypes, relationAttributes, orgId)
.Select(r => MetadataHelpers.BuildRelationMetadata(r,
r.RelationshipType == RelationshipType.OneToManyRelationship
? MetadataHelpers.RelationType.OneToManyRelationships
: MetadataHelpers.RelationType.ManyToManyRelationships)).ToList();
return GetRelatedRecordsInner(service, entity, relations, orgId, filter, attributes);
}

public static List<Entity> GetRelatedRecords(IOrganizationService service, EntityReference entity,
string relationName, string orgId, FilterExpression filter = null, params string[] attributes)
{
service.Require(nameof(service));
entity.Require(nameof(entity));
relationName.RequireNotEmpty(nameof(relationName));

var relation = MetadataHelpers.GetRelation(service, entity.LogicalName, relationName, orgId);
return GetRelatedRecordsInner(service, entity, new List<RelationMetadata> { relation }, orgId,
filter, attributes);
}

private static List<Entity> GetRelatedRecordsInner(IOrganizationService service, EntityReference entity,
List<RelationMetadata> relations, string orgId, FilterExpression filter = null, params string[] attributes)
{
var related = new List<Entity>();

var idFieldName = MetadataHelpers.GetEntityAttribute<string>(service, entity.LogicalName,
MetadataHelpers.EntityAttribute.PrimaryIdAttribute, orgId);
var relations = MetadataHelpers.GetCustomRelationships(service, entity.LogicalName,
relationTypes, relationAttributes, orgId);

if (relationTypes.Contains(MetadataHelpers.RelationType.ManyToManyRelationships))
{
var manyToMany = relations.Where(
relation => relation.RelationshipType == Microsoft.Xrm.Sdk.Metadata.RelationshipType.ManyToManyRelationship)
.Cast<ManyToManyRelationshipMetadata>();
var manyToMany = relations.Where(relation => relation.Type == MetadataHelpers.RelationType.ManyToManyRelationships);

if (manyToMany.Any())
{
foreach (var rel in manyToMany)
{
var entity2Name = rel.Entity1LogicalName == entity.LogicalName
? rel.Entity2LogicalName
: rel.Entity1LogicalName;
var entity2Id = rel.Entity1LogicalName == entity.LogicalName
? rel.Entity2IntersectAttribute
: rel.Entity1IntersectAttribute;
var intersectEntity = rel.IntersectEntityName;
var entity2Name = rel.Entity1Name == entity.LogicalName
? rel.Entity2Name
: rel.Entity1Name;
var entity2Id = rel.Entity1Name == entity.LogicalName
? rel.Entity2FieldName
: rel.Entity1FieldName;
var intersectEntity = rel.IntersectingEntityName;

related.AddRange(GetRecords(entity, service, entity2Name, intersectEntity, entity2Id,
entity2Id, idFieldName, -1, -1, null, attributes));
entity2Id, idFieldName, -1, -1, filter, attributes));
}
}

if (relationTypes.Contains(MetadataHelpers.RelationType.OneToManyRelationships))
{
var oneToMany = relations
.Where(relation => relation.RelationshipType == Microsoft.Xrm.Sdk.Metadata.RelationshipType.OneToManyRelationship)
.Cast<OneToManyRelationshipMetadata>();
var oneToMany = relations.Where(relation => relation.Type == MetadataHelpers.RelationType.OneToManyRelationships);

if (oneToMany.Any())
{
foreach (var rel in oneToMany)
{
var entity2Name = rel.ReferencedEntity == entity.LogicalName
? rel.ReferencingEntity
: rel.ReferencedEntity;
var entity2LookupName = rel.ReferencedEntity == entity.LogicalName
? rel.ReferencingAttribute
: rel.ReferencedAttribute;
var entity2Name = rel.Entity2Name;
var entity2LookupName = rel.Entity2FieldName;

related.AddRange(GetRecords(entity, service, entity2Name, entity.LogicalName,
entity2LookupName, idFieldName, idFieldName, -1, -1, null, attributes));
entity2LookupName, idFieldName, idFieldName, -1, -1, filter, attributes));
}
}

var manyToOne = relations.Where(relation => relation.Type == MetadataHelpers.RelationType.ManyToOneRelationships);

if (manyToOne.Any())
{
foreach (var rel in manyToOne)
{
var entity2Name = rel.Entity1Name;
var entity2LookupName = rel.Entity1FieldName;

related.AddRange(GetRecords(entity, service, entity2Name, entity.LogicalName,
entity2LookupName, idFieldName, idFieldName, -1, -1, filter, attributes));
}
}

Expand Down Expand Up @@ -2527,7 +2567,7 @@ public static string ParseAttributeVariables(IOrganizationService service, strin
var placeholderGuards = BuildPlaceholderGuards(record, placeholderLabel);

var rawMatches = Regex.Matches(rawText,
@"{(?:((?>(?<c1>{)|[^{}]+?|(?<-c1>}))*?(?(c1)(?!))\?\?(?>(?<c2>{)|[^{}]+?|(?<-c2>}))*(?(c2)(?!))::(?>(?<c3>{)|[^{}]+?|(?<-c3>}))*?(?(c3)(?!)))|((?>(?<c2>{)|[^{}]+?|(?<-c2>}))*(?(c2)(?!))))}");
@"{(?:((?>(?<c1>{)|[^{}]+?|(?<-c1>}))+?(?(c1)(?!))\?\?(?>(?<c2>{)|[^{}]+?|(?<-c2>}))+(?(c2)(?!))::(?>(?<c3>{)|[^{}]+?|(?<-c3>}))+?(?(c3)(?!)))|((?>(?<c2>{)|[^{}]+?|(?<-c2>}))+(?(c2)(?!))))}");
var matchesList = new List<Match>();

foreach (Match match in rawMatches)
Expand All @@ -2544,7 +2584,7 @@ public static string ParseAttributeVariables(IOrganizationService service, strin
var parsedVars = Regex.Replace(
rawText,
@"{" + placeholderGuards +
@"(?:(?:((?>(?<c1>{)|[^{}]+?|(?<-c1>}))*?)(?(c1)(?!))\?\?((?>(?<c2>{)|[^{}]+?|(?<-c2>}))*)(?(c2)(?!))::((?>(?<c3>{)|[^{}]+?|(?<-c3>}))*?)(?(c3)(?!)))|((?>(?<c2>{)|[^{}]+?|(?<-c2>}))*)(?(c2)(?!)))"
@"(?:(?:((?>(?<c1>{)|[^{}]+?|(?<-c1>}))*?)(?(c1)(?!))\?\?((?>(?<c2>{)|[^{}]+?|(?<-c2>}))*)(?(c2)(?!))::((?>(?<c3>{)|[^{}]+?|(?<-c3>}))*?)(?(c3)(?!)))|((?>(?<c2>{)|[^{}]+?|(?<-c2>}))+)(?(c2)(?!)))"
+
"}",
match =>
Expand All @@ -2558,14 +2598,14 @@ public static string ParseAttributeVariables(IOrganizationService service, strin
var trueValue = match.Groups[2].Value;
var falseValue = match.Groups[3].Value;

if (!predicate.IsNotEmpty())
if (predicate.IsEmpty())
{
return ParseAttributeVariable(service, $"{{{match.Groups[4].Value}}}", record,
userIdForTimeZone, orgId, cachedValues, placeholderLabel);
}

var predicateMatch = Regex.Match(predicate,
@"^((?>(?<c1>{)|[^{}]+?|(?<-c1>}))*?)(?(c1)(?!))([=<>!]+=?)((?>(?<c2>{)|[^{}]+|(?<-c2>}))*?)(?(c2)(?!))$");
@"^((?>(?<c1>{)|[^{}]+?|(?<-c1>}))*?)(?(c1)(?!))(?:(==|[<>]=?|!=)((?>(?<c2>{)|[^{}]+|(?<-c2>}))*?)(?(c2)(?!)))?$");

var conditionLeft = predicateMatch.Groups[1].Value;
bool isTrue;
Expand Down Expand Up @@ -2646,7 +2686,7 @@ public static string ParseAttributeVariable(IOrganizationService service, string

var parsedVariable = Regex.Replace(
rawText, @"(?!{[0-9a-fA-F]{8}[-]?(?:[0-9a-fA-F]{4}[-]?){3}[0-9a-fA-F]{12}}){" + placeholderLabelRegex +
@"((?>(?<c1>{)|[^{}]+?|(?<-c1>}))*?)(?(c1)(?!))(?:@((?>(?<c2>{)|[^{}]+?|(?<-c2>}))*)(?(c2)(?!)))?(?:(\$(?>(?<c2>{)|[^{}]+?|(?<-c2>}))*)(?(c2)(?!)))*}",
@"((?>(?<c1>{)|[^{}]+?|(?<-c1>}))*?)(?(c1)(?!))(?:@((?>(?<c2>{)|[^{}]+?|(?<-c2>}))*)(?(c2)(?!)))?(?:(\$(?>(?<c3>{)|[^{}]+?|(?<-c3>}))*)(?(c3)(?!)))*}",
match =>
{
if (match.Success)
Expand Down Expand Up @@ -2769,6 +2809,122 @@ public static string GetFieldValueByPathAsString(IOrganizationService service,
// remove the first field from the string
variable = string.Join(".", field.Skip(1).ToArray()).Trim('.');

var relationMatch = Regex.Match(fieldName,
@"^>([^()]+?)(?:\((\*)?(?:\^(\w+?)(!)?)?(?:\|([^()]*?))?\))?(?:\(([^()]+?)(==|[<>]=?|!=)([^()]+?)\))?$");

// relation drill-through
if (relationMatch.Value.IsNotEmpty())
{
var relationName = relationMatch.Groups[1].Value;
var isDistinct = relationMatch.Groups[2].Value == "*";
var orderingField = relationMatch.Groups[3].Value;
var isDescending = relationMatch.Groups[4].Value == "!";
var separator = relationMatch.Groups[5].Value;
var conditionLeft = relationMatch.Groups[6].Value;
var comparator = relationMatch.Groups[7].Value;
var conditionRight = relationMatch.Groups[8].Value;

FilterExpression filter = null;

if (conditionLeft.IsNotEmpty())
{
conditionRight = conditionRight.IsNotEmpty() ? conditionRight : null;

if (comparator.IsNotEmpty() && conditionRight.IsNotEmpty())
{
ConditionOperator compareOp = ConditionOperator.Equal;

switch (comparator)
{
case "==":
compareOp = ConditionOperator.Equal;
break;
case "<":
compareOp = ConditionOperator.LessThan;
break;
case "<=":
compareOp = ConditionOperator.LessEqual;
break;
case ">":
compareOp = ConditionOperator.GreaterThan;
break;
case ">=":
compareOp = ConditionOperator.GreaterEqual;
break;
case "!=":
compareOp = ConditionOperator.NotEqual;
break;
default:
throw new Exception($"'{relationMatch.Value}' is in an invalid format.");
}

filter = new FilterExpression();
filter.AddCondition(conditionLeft, compareOp, conditionRight);
}
else
{
filter = new FilterExpression();
filter.AddCondition(conditionLeft, ConditionOperator.NotNull);
}
}

// get field name in related entity
field = variable.Split('.');
fieldName = field[0];
variable = string.Join(".", field.Skip(1).ToArray()).Trim('.');

var records = GetRelatedRecords(service, record.ToEntityReference(), relationName, orgId,
filter, new[] { fieldName, orderingField }.Where(f => f.IsNotEmpty()).Distinct().ToArray())
.AsQueryable();

if (orderingField.IsNotEmpty())
{
records = isDescending
? records.OrderByDescending(
r => GetTypedValue(GetAttributeStringValue(r.GetAttributeValue<object>(orderingField))))
: records.OrderBy(r => GetTypedValue(GetAttributeStringValue(r.GetAttributeValue<object>(orderingField))));
}

var relationStrings = new List<string>();

foreach (var relatedRecord in records)
{
// get the field value from the entity record
var relatedValue = relatedRecord.GetAttributeValue<object>(fieldName);

if (relatedValue == null)
{
continue;
}

if (variable.IsNotEmpty())
{
// if the field value is not a lookup, then we can't recurse
var reference = relatedValue as EntityReference;

if (reference == null)
{
throw new Exception($"Field \"{fieldName}\" is not a lookup.");
}

var refEntity =
new Entity(reference.LogicalName)
{
Id = reference.Id
};

relationStrings.Add(GetFieldValueByPathAsString(service, variable, modifier, stringModifier, refEntity,
userIdForTimeZone, orgId));
}
else
{
relationStrings.Add(GetAttributeStringValue(relatedValue));
}
}

return string.Join(separator, isDistinct ? relationStrings.Distinct() : relationStrings);
}

// get the field value from the entity record
var fieldValue = record.GetAttributeValue<object>(fieldName);

Expand All @@ -2785,7 +2941,7 @@ record = fieldValue == null
}

// variable is recursive, so we need to go deeper through the lookup
if (!string.IsNullOrWhiteSpace(variable))
if (variable.IsNotEmpty())
{
// if the field value is not a lookup, then we can't recurse
var reference = fieldValue as EntityReference;
Expand Down Expand Up @@ -2879,7 +3035,7 @@ record = fieldValue == null
result = parsedDecimalValue.ToString($"0.{new string('#', precision)}");
}

if (!cachedValues.ContainsKey(cacheKey))
if (result.IsEmpty())
{
result = fieldValue.ToString();
}
Expand Down
2 changes: 1 addition & 1 deletion LinkDev.AutoNumbering.Plugins/packages.config
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<packages>
<package id="CrmLogger.Fody" version="1.3.4" targetFramework="net452" developmentDependency="true" />
<package id="Fody" version="2.1.2" targetFramework="net452" developmentDependency="true" />
<package id="LinkDev.Libraries.Common.File" version="3.8.1" targetFramework="net452" developmentDependency="true" />
<package id="LinkDev.Libraries.Common.File" version="3.8.4" targetFramework="net452" developmentDependency="true" />
<package id="Microsoft.CrmSdk.CoreAssemblies" version="8.2.0.2" targetFramework="net452" />
<package id="Microsoft.CrmSdk.Workflow" version="8.2.0.2" targetFramework="net452" />
<package id="Microsoft.IdentityModel" version="6.1.7600.16394" targetFramework="net452" />
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/yagasoft/DynamicsCrm-AutoNumbering?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)

### Version: 3.2.1.1
### Version: 3.2.2.1
---

A CRM solution that gives a lot of flexibility in creating any pattern required for auto-numbering.
Expand Down Expand Up @@ -53,7 +53,7 @@ Please check the 'docs' folder for a guide PDF.

## Changes

#### _v3.2.1.1 (2018-12-18)_
#### _v3.2.2.1 (2018-12-19)_
+ Changed: upgraded to the new placeholder system
#### _v3.1.1.1 (2018-12-05)_
+ Added: index streams
Expand Down
2 changes: 1 addition & 1 deletion crm-solution/Other/Solution.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<Descriptions>
<Description description="Refer to Yagasoft.com for any enquiries." languagecode="1033" />
</Descriptions>
<Version>3.2.1.1</Version>
<Version>3.2.2.1</Version>
<Managed>0</Managed>
<Publisher>
<UniqueName>linkdevelopment</UniqueName>
Expand Down
Binary file not shown.
Binary file removed docs/Placeholders - Manual - v1.2.pdf
Binary file not shown.
Binary file added docs/Placeholders - Manual - v2.1.pdf
Binary file not shown.

0 comments on commit 13f5ccc

Please sign in to comment.