| 18 | 111 | | } |
| | 112 | |
|
| 2 | 113 | | public static FunctionHandler IsEqual = (primary, service, tracing, organizationConfig, parameters) => |
- | 11 | 114 | | { |
- | 11 | 115 | | if (parameters.Count != 2) |
+ | 17 | 114 | | { |
+ | 17 | 115 | | if (parameters.Count != 2) |
| 2 | 116 | | { |
| 2 | 117 | | throw new InvalidPluginExecutionException("IsEqual expects exactly 2 parameters!"); |
| 2 | 118 | | } |
| 2 | 119 | |
|
- | 11 | 120 | | var expected = parameters[0]; |
- | 11 | 121 | | var actual = parameters[1]; |
- | 2 | 122 | |
|
- | 11 | 123 | | var falseReturn = new ValueExpression(bool.FalseString, false); |
- | 11 | 124 | | var trueReturn = new ValueExpression(bool.TrueString, true); |
- | 2 | 125 | |
|
- | 11 | 126 | | if (expected.Value == null && actual.Value == null) |
- | 2 | 127 | | { |
- | 2 | 128 | | return trueReturn; |
- | 2 | 129 | | } |
- | 2 | 130 | |
|
- | 11 | 131 | | if (expected.Value == null && actual.Value != null) |
- | 2 | 132 | | { |
- | 2 | 133 | | return falseReturn; |
- | 2 | 134 | | } |
- | 2 | 135 | |
|
- | 11 | 136 | | if (expected.Value != null && actual.Value == null) |
- | 2 | 137 | | { |
- | 2 | 138 | | return falseReturn; |
- | 2 | 139 | | } |
- | 2 | 140 | |
|
- | 24 | 141 | | if (new[] { expected.Value, actual.Value }.All(v => v is int || v is OptionSetValue)) |
- | 6 | 142 | | { |
- | 6 | 143 | | var values = new[] { expected.Value, actual.Value } |
- | 14 | 144 | | .Select(v => v is OptionSetValue ? ((OptionSetValue)v).Value : (int)v) |
- | 6 | 145 | | .ToList(); |
- | 2 | 146 | |
|
- | 6 | 147 | | var optionSetResult = values[0].Equals(values[1]); |
- | 6 | 148 | | return new ValueExpression(optionSetResult.ToString(CultureInfo.InvariantCulture), optionSetResult); |
- | 2 | 149 | | } |
- | 2 | 150 | | else |
- | 7 | 151 | | { |
- | 7 | 152 | | var result = expected.Value.Equals(actual.Value); |
- | 2 | 153 | |
|
- | 7 | 154 | | return new ValueExpression(result.ToString(CultureInfo.InvariantCulture), result); |
- | 2 | 155 | | } |
- | 11 | 156 | | }; |
- | | 157 | |
|
- | 2 | 158 | | public static FunctionHandler And = (primary, service, tracing, organizationConfig, parameters) => |
- | 5 | 159 | | { |
- | 5 | 160 | | if (parameters.Count < 2) |
- | 2 | 161 | | { |
- | 2 | 162 | | throw new InvalidPluginExecutionException("And expects at least 2 conditions!"); |
- | 2 | 163 | | } |
- | 2 | 164 | |
|
- | 11 | 165 | | if (parameters.Any(p => !(p.Value is bool))) |
- | 2 | 166 | | { |
- | 2 | 167 | | throw new InvalidPluginExecutionException("And: All conditions must be booleans!"); |
- | 2 | 168 | | } |
- | 2 | 169 | |
|
- | 9 | 170 | | if (parameters.All(p => (bool)p.Value)) |
- | 3 | 171 | | { |
- | 3 | 172 | | return new ValueExpression(bool.TrueString, true); |
- | 2 | 173 | | } |
- | 2 | 174 | |
|
- | 4 | 175 | | return new ValueExpression(bool.FalseString, false); |
- | 5 | 176 | | }; |
- | | 177 | |
|
- | 2 | 178 | | public static FunctionHandler Or = (primary, service, tracing, organizationConfig, parameters) => |
- | 5 | 179 | | { |
- | 5 | 180 | | if (parameters.Count < 2) |
- | 2 | 181 | | { |
- | 2 | 182 | | throw new InvalidPluginExecutionException("Or expects at least 2 conditions!"); |
- | 2 | 183 | | } |
- | 2 | 184 | |
|
- | 11 | 185 | | if (parameters.Any(p => !(p.Value is bool))) |
- | 2 | 186 | | { |
- | 2 | 187 | | throw new InvalidPluginExecutionException("Or: All conditions must be booleans!"); |
+ | 17 | 120 | | var expected = parameters[0]; |
+ | 17 | 121 | | var actual = parameters[1]; |
+ | 17 | 122 | | var tempGuid = Guid.Empty; |
+ | 2 | 123 | |
|
+ | 17 | 124 | | var falseReturn = new ValueExpression(bool.FalseString, false); |
+ | 17 | 125 | | var trueReturn = new ValueExpression(bool.TrueString, true); |
+ | 2 | 126 | |
|
+ | 17 | 127 | | if (expected.Value == null && actual.Value == null) |
+ | 2 | 128 | | { |
+ | 2 | 129 | | return trueReturn; |
+ | 2 | 130 | | } |
+ | 2 | 131 | |
|
+ | 17 | 132 | | if (expected.Value == null && actual.Value != null) |
+ | 2 | 133 | | { |
+ | 2 | 134 | | return falseReturn; |
+ | 2 | 135 | | } |
+ | 2 | 136 | |
|
+ | 17 | 137 | | if (expected.Value != null && actual.Value == null) |
+ | 2 | 138 | | { |
+ | 2 | 139 | | return falseReturn; |
+ | 2 | 140 | | } |
+ | 2 | 141 | |
|
+ | 36 | 142 | | if (new[] { expected.Value, actual.Value }.All(v => v is int || v is OptionSetValue)) |
+ | 6 | 143 | | { |
+ | 6 | 144 | | var values = new[] { expected.Value, actual.Value } |
+ | 14 | 145 | | .Select(v => v is OptionSetValue ? ((OptionSetValue)v).Value : (int)v) |
+ | 6 | 146 | | .ToList(); |
+ | 2 | 147 | |
|
+ | 6 | 148 | | var optionSetResult = values[0].Equals(values[1]); |
+ | 6 | 149 | | return new ValueExpression(optionSetResult.ToString(CultureInfo.InvariantCulture), optionSetResult); |
+ | 2 | 150 | | } |
+ | 28 | 151 | | else if (new[] { expected.Value, actual.Value }.All(v => v is Guid || (v is string && Guid.TryParse((string) |
+ | 6 | 152 | | { |
+ | 6 | 153 | | var values = new[] { expected.Value, actual.Value } |
+ | 6 | 154 | | .Select(v => |
+ | 14 | 155 | | { |
+ | 14 | 156 | | if (v is Guid) |
+ | 10 | 157 | | { |
+ | 10 | 158 | | return (Guid) v; |
+ | 6 | 159 | | } |
+ | 6 | 160 | |
|
+ | 10 | 161 | | if (v is string) |
+ | 10 | 162 | | { |
+ | 10 | 163 | | return tempGuid; |
+ | 6 | 164 | | } |
+ | 6 | 165 | |
|
+ | 6 | 166 | | if (v is EntityReference) |
+ | 6 | 167 | | { |
+ | 6 | 168 | | return ((EntityReference) v).Id; |
+ | 6 | 169 | | } |
+ | 6 | 170 | |
|
+ | 6 | 171 | | if (v is Entity) |
+ | 6 | 172 | | { |
+ | 6 | 173 | | return ((Entity) v).Id; |
+ | 6 | 174 | | } |
+ | 6 | 175 | |
|
+ | 6 | 176 | | return Guid.Empty; |
+ | 14 | 177 | | }) |
+ | 6 | 178 | | .ToList(); |
+ | 2 | 179 | |
|
+ | 6 | 180 | | var guidResult = values[0].Equals(values[1]); |
+ | 6 | 181 | | return new ValueExpression(guidResult.ToString(CultureInfo.InvariantCulture), guidResult); |
+ | 2 | 182 | | } |
+ | 2 | 183 | | else |
+ | 9 | 184 | | { |
+ | 9 | 185 | | var result = expected.Value.Equals(actual.Value); |
+ | 2 | 186 | |
|
+ | 9 | 187 | | return new ValueExpression(result.ToString(CultureInfo.InvariantCulture), result); |
| 2 | 188 | | } |
- | 2 | 189 | |
|
- | 10 | 190 | | if (parameters.Any(p => (bool)p.Value)) |
- | 4 | 191 | | { |
- | 4 | 192 | | return new ValueExpression(bool.TrueString, true); |
- | 2 | 193 | | } |
- | 2 | 194 | |
|
- | 3 | 195 | | return new ValueExpression(bool.FalseString, false); |
- | 5 | 196 | | }; |
- | | 197 | |
|
- | 2 | 198 | | public static FunctionHandler IsNull = (primary, service, tracing, organizationConfig, parameters) => |
- | 22 | 199 | | { |
- | 22 | 200 | | var target = parameters.FirstOrDefault(); |
- | 2 | 201 | |
|
- | 22 | 202 | | if (target.Value == null) |
- | 10 | 203 | | { |
- | 10 | 204 | | return new ValueExpression(bool.TrueString, true); |
- | 2 | 205 | | } |
- | 2 | 206 | |
|
- | 14 | 207 | | return new ValueExpression(bool.FalseString, false); |
- | 22 | 208 | | }; |
- | | 209 | |
|
- | 2 | 210 | | public static FunctionHandler If = (primary, service, tracing, organizationConfig, parameters) => |
- | 21 | 211 | | { |
- | 21 | 212 | | if (parameters.Count != 3) |
- | 2 | 213 | | { |
- | 2 | 214 | | throw new InvalidPluginExecutionException("If-Then-Else expects exactly three parameters: Condition, Tru |
- | 2 | 215 | | } |
- | 2 | 216 | |
|
- | 21 | 217 | | var condition = CheckedCast<bool>(parameters[0]?.Value, "If condition must be a boolean!"); |
- | 21 | 218 | | var trueAction = parameters[1]; |
- | 21 | 219 | | var falseAction = parameters[2]; |
- | 2 | 220 | |
|
- | 21 | 221 | | if (condition) |
- | 12 | 222 | | { |
- | 12 | 223 | | tracing.Trace("Executing true condition"); |
- | 22 | 224 | | return new ValueExpression(new Lazy<ValueExpression>(() => trueAction)); |
- | 2 | 225 | | } |
- | 2 | 226 | |
|
- | 11 | 227 | | tracing.Trace("Executing false condition"); |
- | 20 | 228 | | return new ValueExpression(new Lazy<ValueExpression>(() => falseAction)); |
- | 21 | 229 | | }; |
+ | 17 | 189 | | }; |
+ | | 190 | |
|
+ | 2 | 191 | | public static FunctionHandler And = (primary, service, tracing, organizationConfig, parameters) => |
+ | 8 | 192 | | { |
+ | 8 | 193 | | if (parameters.Count < 2) |
+ | 2 | 194 | | { |
+ | 2 | 195 | | throw new InvalidPluginExecutionException("And expects at least 2 conditions!"); |
+ | 2 | 196 | | } |
+ | 2 | 197 | |
|
+ | 26 | 198 | | if (parameters.Any(p => !(p.Value is bool))) |
+ | 2 | 199 | | { |
+ | 2 | 200 | | throw new InvalidPluginExecutionException("And: All conditions must be booleans!"); |
+ | 2 | 201 | | } |
+ | 2 | 202 | |
|
+ | 21 | 203 | | if (parameters.All(p => (bool)p.Value)) |
+ | 4 | 204 | | { |
+ | 4 | 205 | | return new ValueExpression(bool.TrueString, true); |
+ | 2 | 206 | | } |
+ | 2 | 207 | |
|
+ | 6 | 208 | | return new ValueExpression(bool.FalseString, false); |
+ | 8 | 209 | | }; |
+ | | 210 | |
|
+ | 2 | 211 | | public static FunctionHandler Or = (primary, service, tracing, organizationConfig, parameters) => |
+ | 6 | 212 | | { |
+ | 6 | 213 | | if (parameters.Count < 2) |
+ | 2 | 214 | | { |
+ | 2 | 215 | | throw new InvalidPluginExecutionException("Or expects at least 2 conditions!"); |
+ | 2 | 216 | | } |
+ | 2 | 217 | |
|
+ | 17 | 218 | | if (parameters.Any(p => !(p.Value is bool))) |
+ | 2 | 219 | | { |
+ | 2 | 220 | | throw new InvalidPluginExecutionException("Or: All conditions must be booleans!"); |
+ | 2 | 221 | | } |
+ | 2 | 222 | |
|
+ | 14 | 223 | | if (parameters.Any(p => (bool)p.Value)) |
+ | 5 | 224 | | { |
+ | 5 | 225 | | return new ValueExpression(bool.TrueString, true); |
+ | 2 | 226 | | } |
+ | 2 | 227 | |
|
+ | 3 | 228 | | return new ValueExpression(bool.FalseString, false); |
+ | 6 | 229 | | }; |
| | 230 | |
|
- | 2 | 231 | | public static FunctionHandler GetPrimaryRecord = (primary, service, tracing, organizationConfig, parameters) => |
- | 5 | 232 | | { |
- | 5 | 233 | | if (primary == null) |
- | 2 | 234 | | { |
- | 2 | 235 | | return new ValueExpression(null); |
- | 2 | 236 | | } |
- | 2 | 237 | |
|
- | 5 | 238 | | return new ValueExpression(string.Empty, primary); |
- | 5 | 239 | | }; |
- | | 240 | |
|
- | 2 | 241 | | public static FunctionHandler GetRecordUrl = (primary, service, tracing, organizationConfig, parameters) => |
- | 13 | 242 | | { |
- | 13 | 243 | | if (organizationConfig == null || string.IsNullOrEmpty(organizationConfig.OrganizationUrl)) |
- | 2 | 244 | | { |
- | 2 | 245 | | throw new InvalidPluginExecutionException("GetRecordUrl can't find the Organization Url inside the plugi |
- | 2 | 246 | | } |
- | 2 | 247 | |
|
- | 32 | 248 | | if (!parameters.All(p => p.Value is EntityReference || p.Value is Entity || p.Value is Dictionary<string, ob |
- | 2 | 249 | | { |
- | 2 | 250 | | throw new InvalidPluginExecutionException("Only Entity Reference and Entity ValueExpressions are support |
- | 2 | 251 | | } |
- | 2 | 252 | |
|
- | 32 | 253 | | var refs = parameters.Where(p => p != null && !(p?.Value is Dictionary<string, object>)).Select(e => |
- | 24 | 254 | | { |
- | 24 | 255 | | var entityReference = e.Value as EntityReference; |
- | 13 | 256 | |
|
- | 26 | 257 | | if (entityReference != null) { |
- | 15 | 258 | | return new |
- | 15 | 259 | | { |
- | 15 | 260 | | Id = entityReference.Id, |
- | 15 | 261 | | LogicalName = entityReference.LogicalName |
- | 15 | 262 | | }; |
- | 13 | 263 | | } |
- | 13 | 264 | |
|
- | 22 | 265 | | var entity = e.Value as Entity; |
- | 13 | 266 | |
|
- | 22 | 267 | | return new |
- | 22 | 268 | | { |
- | 22 | 269 | | Id = entity.Id, |
- | 22 | 270 | | LogicalName = entity.LogicalName |
- | 22 | 271 | | }; |
- | 24 | 272 | | }); |
- | 13 | 273 | | var organizationUrl = organizationConfig.OrganizationUrl.EndsWith("/") ? organizationConfig.OrganizationUrl |
- | 2 | 274 | |
|
- | 13 | 275 | | var config = GetConfig(parameters); |
- | 13 | 276 | | var linkText = config.GetValue<string>("linkText", "linkText must be a string"); |
- | 13 | 277 | | var appId = config.GetValue<string>("appId", "appId must be a string"); |
- | 2 | 278 | |
|
- | 13 | 279 | | var urls = string.Join(Environment.NewLine, refs.Select(e => |
- | 24 | 280 | | { |
- | 24 | 281 | | var url = $"{organizationUrl}main.aspx?etn={e.LogicalName}&id={e.Id}&newWindow=true&pagetype=entityrecor |
- | 24 | 282 | | return $"<a href=\"{url}\">{(string.IsNullOrEmpty(linkText) ? url : linkText)}</a>"; |
- | 24 | 283 | | })); |
- | 2 | 284 | |
|
- | 13 | 285 | | return new ValueExpression(urls, urls); |
- | 13 | 286 | | }; |
- | | 287 | |
|
- | 2 | 288 | | public static FunctionHandler GetOrganizationUrl = (primary, service, tracing, organizationConfig, parameters) = |
- | 4 | 289 | | { |
- | 4 | 290 | | if (organizationConfig == null || string.IsNullOrEmpty(organizationConfig.OrganizationUrl)) |
- | 2 | 291 | | { |
- | 2 | 292 | | throw new InvalidPluginExecutionException("GetOrganizationUrl can't find the Organization Url inside the |
- | 2 | 293 | | } |
- | 2 | 294 | |
|
- | 4 | 295 | | var config = GetConfig(parameters); |
- | 4 | 296 | | var linkText = config.GetValue<string>("linkText", "linkText must be a string", string.Empty); |
- | 4 | 297 | | var urlSuffix = config.GetValue<string>("urlSuffix", "urlSuffix must be a string", string.Empty); |
- | 4 | 298 | | var asHtml = config.GetValue<bool>("asHtml", "asHtml must be a boolean"); |
- | 2 | 299 | |
|
- | 4 | 300 | | if (asHtml) |
- | 4 | 301 | | { |
- | 4 | 302 | | var url = $"{organizationConfig.OrganizationUrl}{urlSuffix}"; |
- | 4 | 303 | | var href = $"<a href=\"{url}\">{(string.IsNullOrEmpty(linkText) ? url : linkText)}</a>"; |
- | 4 | 304 | | return new ValueExpression(href, href); |
- | 2 | 305 | | } |
- | 2 | 306 | | else |
- | 2 | 307 | | { |
- | 2 | 308 | | return new ValueExpression(organizationConfig.OrganizationUrl, organizationConfig.OrganizationUrl); |
- | 2 | 309 | | } |
- | 4 | 310 | | }; |
- | | 311 | |
|
- | 2 | 312 | | public static FunctionHandler Union = (primary, service, tracing, organizationConfig, parameters) => |
- | 6 | 313 | | { |
- | 6 | 314 | | if (parameters.Count < 2) |
- | 2 | 315 | | { |
- | 2 | 316 | | throw new InvalidPluginExecutionException("Union function needs at least two parameters: Arrays to union |
- | 2 | 317 | | } |
- | 2 | 318 | |
|
- | 6 | 319 | | var union = parameters.Select(p => |
- | 16 | 320 | | { |
- | 16 | 321 | | if (p == null) |
- | 6 | 322 | | { |
- | 6 | 323 | | return null; |
- | 6 | 324 | | } |
- | 6 | 325 | |
|
- | 16 | 326 | | return p.Value as List<ValueExpression>; |
- | 16 | 327 | | }) |
- | 16 | 328 | | .Where(p => p != null) |
- | 16 | 329 | | .SelectMany(p => p) |
- | 6 | 330 | | .ToList(); |
- | 2 | 331 | |
|
- | 6 | 332 | | return new ValueExpression(null, union); |
- | 6 | 333 | | }; |
- | | 334 | |
|
- | 2 | 335 | | public static FunctionHandler Map = (primary, service, tracing, organizationConfig, parameters) => |
- | 3 | 336 | | { |
- | 3 | 337 | | if (parameters.Count < 2) |
- | 2 | 338 | | { |
- | 2 | 339 | | throw new InvalidPluginExecutionException("Map function needs at least an array with data and a function |
- | 2 | 340 | | } |
- | 2 | 341 | |
|
- | 3 | 342 | | var config = GetConfig(parameters); |
- | 2 | 343 | |
|
- | 3 | 344 | | var values = parameters[0].Value as List<ValueExpression>; |
- | 2 | 345 | |
|
- | 3 | 346 | | if (!(values is IEnumerable)) |
- | 2 | 347 | | { |
- | 2 | 348 | | throw new InvalidPluginExecutionException("Map needs an array as first parameter."); |
- | 2 | 349 | | } |
- | 2 | 350 | |
|
- | 3 | 351 | | var lambda = parameters[1].Value as Func<List<ValueExpression>, ValueExpression>; |
- | 2 | 352 | |
|
- | 3 | 353 | | if (lambda == null) |
- | 2 | 354 | | { |
- | 2 | 355 | | throw new InvalidPluginExecutionException("Lambda function must be a proper arrow function"); |
- | 2 | 356 | | } |
- | 2 | 357 | |
|
- | 7 | 358 | | return new ValueExpression(null, values.Select(v => lambda(new List<ValueExpression> { v })).ToList()); |
- | 3 | 359 | | }; |
- | | 360 | |
|
- | 2 | 361 | | public static FunctionHandler Sort = (primary, service, tracing, organizationConfig, parameters) => |
- | 6 | 362 | | { |
- | 6 | 363 | | if (parameters.Count < 1) |
- | 2 | 364 | | { |
- | 2 | 365 | | throw new InvalidPluginExecutionException("Sort function needs at least an array to sort and optionally |
- | 2 | 366 | | } |
- | 2 | 367 | |
|
- | 6 | 368 | | var config = GetConfig(parameters); |
- | 2 | 369 | |
|
- | 6 | 370 | | var values = parameters[0].Value as List<ValueExpression>; |
- | 2 | 371 | |
|
- | 6 | 372 | | if (!(values is IEnumerable)) |
- | 2 | 373 | | { |
- | 2 | 374 | | throw new InvalidPluginExecutionException("Sort needs an array as first parameter."); |
- | 2 | 375 | | } |
+ | 2 | 231 | | public static FunctionHandler IsNull = (primary, service, tracing, organizationConfig, parameters) => |
+ | 22 | 232 | | { |
+ | 22 | 233 | | var target = parameters.FirstOrDefault(); |
+ | 2 | 234 | |
|
+ | 22 | 235 | | if (target.Value == null) |
+ | 10 | 236 | | { |
+ | 10 | 237 | | return new ValueExpression(bool.TrueString, true); |
+ | 2 | 238 | | } |
+ | 2 | 239 | |
|
+ | 14 | 240 | | return new ValueExpression(bool.FalseString, false); |
+ | 22 | 241 | | }; |
+ | | 242 | |
|
+ | 2 | 243 | | public static FunctionHandler If = (primary, service, tracing, organizationConfig, parameters) => |
+ | 27 | 244 | | { |
+ | 27 | 245 | | if (parameters.Count != 3) |
+ | 2 | 246 | | { |
+ | 2 | 247 | | throw new InvalidPluginExecutionException("If-Then-Else expects exactly three parameters: Condition, Tru |
+ | 2 | 248 | | } |
+ | 2 | 249 | |
|
+ | 27 | 250 | | var condition = CheckedCast<bool>(parameters[0]?.Value, "If condition must be a boolean!"); |
+ | 27 | 251 | | var trueAction = parameters[1]; |
+ | 27 | 252 | | var falseAction = parameters[2]; |
+ | 2 | 253 | |
|
+ | 27 | 254 | | if (condition) |
+ | 15 | 255 | | { |
+ | 15 | 256 | | tracing.Trace("Executing true condition"); |
+ | 28 | 257 | | return new ValueExpression(new Lazy<ValueExpression>(() => trueAction)); |
+ | 2 | 258 | | } |
+ | 2 | 259 | |
|
+ | 14 | 260 | | tracing.Trace("Executing false condition"); |
+ | 26 | 261 | | return new ValueExpression(new Lazy<ValueExpression>(() => falseAction)); |
+ | 27 | 262 | | }; |
+ | | 263 | |
|
+ | 2 | 264 | | public static FunctionHandler GetPrimaryRecord = (primary, service, tracing, organizationConfig, parameters) => |
+ | 12 | 265 | | { |
+ | 12 | 266 | | if (primary == null) |
+ | 2 | 267 | | { |
+ | 2 | 268 | | return new ValueExpression(null); |
+ | 2 | 269 | | } |
+ | 2 | 270 | |
|
+ | 12 | 271 | | return new ValueExpression(string.Empty, primary); |
+ | 12 | 272 | | }; |
+ | | 273 | |
|
+ | 2 | 274 | | public static FunctionHandler GetRecordUrl = (primary, service, tracing, organizationConfig, parameters) => |
+ | 13 | 275 | | { |
+ | 13 | 276 | | if (organizationConfig == null || string.IsNullOrEmpty(organizationConfig.OrganizationUrl)) |
+ | 2 | 277 | | { |
+ | 2 | 278 | | throw new InvalidPluginExecutionException("GetRecordUrl can't find the Organization Url inside the plugi |
+ | 2 | 279 | | } |
+ | 2 | 280 | |
|
+ | 32 | 281 | | if (!parameters.All(p => p.Value is EntityReference || p.Value is Entity || p.Value is Dictionary<string, ob |
+ | 2 | 282 | | { |
+ | 2 | 283 | | throw new InvalidPluginExecutionException("Only Entity Reference and Entity ValueExpressions are support |
+ | 2 | 284 | | } |
+ | 2 | 285 | |
|
+ | 32 | 286 | | var refs = parameters.Where(p => p != null && !(p?.Value is Dictionary<string, object>)).Select(e => |
+ | 24 | 287 | | { |
+ | 24 | 288 | | var entityReference = e.Value as EntityReference; |
+ | 13 | 289 | |
|
+ | 26 | 290 | | if (entityReference != null) { |
+ | 15 | 291 | | return new |
+ | 15 | 292 | | { |
+ | 15 | 293 | | Id = entityReference.Id, |
+ | 15 | 294 | | LogicalName = entityReference.LogicalName |
+ | 15 | 295 | | }; |
+ | 13 | 296 | | } |
+ | 13 | 297 | |
|
+ | 22 | 298 | | var entity = e.Value as Entity; |
+ | 13 | 299 | |
|
+ | 22 | 300 | | return new |
+ | 22 | 301 | | { |
+ | 22 | 302 | | Id = entity.Id, |
+ | 22 | 303 | | LogicalName = entity.LogicalName |
+ | 22 | 304 | | }; |
+ | 24 | 305 | | }); |
+ | 13 | 306 | | var organizationUrl = organizationConfig.OrganizationUrl.EndsWith("/") ? organizationConfig.OrganizationUrl |
+ | 2 | 307 | |
|
+ | 13 | 308 | | var config = GetConfig(parameters); |
+ | 13 | 309 | | var linkText = config.GetValue<string>("linkText", "linkText must be a string"); |
+ | 13 | 310 | | var appId = config.GetValue<string>("appId", "appId must be a string"); |
+ | 2 | 311 | |
|
+ | 13 | 312 | | var urls = string.Join(Environment.NewLine, refs.Select(e => |
+ | 24 | 313 | | { |
+ | 24 | 314 | | var url = $"{organizationUrl}main.aspx?etn={e.LogicalName}&id={e.Id}&newWindow=true&pagetype=entityrecor |
+ | 24 | 315 | | return $"<a href=\"{url}\">{(string.IsNullOrEmpty(linkText) ? url : linkText)}</a>"; |
+ | 24 | 316 | | })); |
+ | 2 | 317 | |
|
+ | 13 | 318 | | return new ValueExpression(urls, urls); |
+ | 13 | 319 | | }; |
+ | | 320 | |
|
+ | 2 | 321 | | public static FunctionHandler GetOrganizationUrl = (primary, service, tracing, organizationConfig, parameters) = |
+ | 4 | 322 | | { |
+ | 4 | 323 | | if (organizationConfig == null || string.IsNullOrEmpty(organizationConfig.OrganizationUrl)) |
+ | 2 | 324 | | { |
+ | 2 | 325 | | throw new InvalidPluginExecutionException("GetOrganizationUrl can't find the Organization Url inside the |
+ | 2 | 326 | | } |
+ | 2 | 327 | |
|
+ | 4 | 328 | | var config = GetConfig(parameters); |
+ | 4 | 329 | | var linkText = config.GetValue<string>("linkText", "linkText must be a string", string.Empty); |
+ | 4 | 330 | | var urlSuffix = config.GetValue<string>("urlSuffix", "urlSuffix must be a string", string.Empty); |
+ | 4 | 331 | | var asHtml = config.GetValue<bool>("asHtml", "asHtml must be a boolean"); |
+ | 2 | 332 | |
|
+ | 4 | 333 | | if (asHtml) |
+ | 4 | 334 | | { |
+ | 4 | 335 | | var url = $"{organizationConfig.OrganizationUrl}{urlSuffix}"; |
+ | 4 | 336 | | var href = $"<a href=\"{url}\">{(string.IsNullOrEmpty(linkText) ? url : linkText)}</a>"; |
+ | 4 | 337 | | return new ValueExpression(href, href); |
+ | 2 | 338 | | } |
+ | 2 | 339 | | else |
+ | 2 | 340 | | { |
+ | 2 | 341 | | return new ValueExpression(organizationConfig.OrganizationUrl, organizationConfig.OrganizationUrl); |
+ | 2 | 342 | | } |
+ | 4 | 343 | | }; |
+ | | 344 | |
|
+ | 2 | 345 | | public static FunctionHandler Union = (primary, service, tracing, organizationConfig, parameters) => |
+ | 6 | 346 | | { |
+ | 6 | 347 | | if (parameters.Count < 2) |
+ | 2 | 348 | | { |
+ | 2 | 349 | | throw new InvalidPluginExecutionException("Union function needs at least two parameters: Arrays to union |
+ | 2 | 350 | | } |
+ | 2 | 351 | |
|
+ | 6 | 352 | | var union = parameters.Select(p => |
+ | 16 | 353 | | { |
+ | 16 | 354 | | if (p == null) |
+ | 6 | 355 | | { |
+ | 6 | 356 | | return null; |
+ | 6 | 357 | | } |
+ | 6 | 358 | |
|
+ | 16 | 359 | | return p.Value as List<ValueExpression>; |
+ | 16 | 360 | | }) |
+ | 16 | 361 | | .Where(p => p != null) |
+ | 16 | 362 | | .SelectMany(p => p) |
+ | 6 | 363 | | .ToList(); |
+ | 2 | 364 | |
|
+ | 6 | 365 | | return new ValueExpression(null, union); |
+ | 6 | 366 | | }; |
+ | | 367 | |
|
+ | 2 | 368 | | public static FunctionHandler Map = (primary, service, tracing, organizationConfig, parameters) => |
+ | 3 | 369 | | { |
+ | 3 | 370 | | if (parameters.Count < 2) |
+ | 2 | 371 | | { |
+ | 2 | 372 | | throw new InvalidPluginExecutionException("Map function needs at least an array with data and a function |
+ | 2 | 373 | | } |
+ | 2 | 374 | |
|
+ | 3 | 375 | | var config = GetConfig(parameters); |
| 2 | 376 | |
|
- | 6 | 377 | | var descending = config.GetValue<bool>("descending", "descending must be a bool"); |
- | 6 | 378 | | var property = config.GetValue<string>("property", "property must be a string"); |
- | 2 | 379 | |
|
- | 6 | 380 | | if (string.IsNullOrEmpty(property)) |
- | 3 | 381 | | { |
- | 3 | 382 | | if (descending) |
- | 2 | 383 | | { |
- | 2 | 384 | | return new ValueExpression(null, values.OrderByDescending(v => v.Value).ToList()); |
- | 2 | 385 | | } |
- | 2 | 386 | | else |
- | 3 | 387 | | { |
- | 7 | 388 | | return new ValueExpression(null, values.OrderBy(v => v.Value).ToList()); |
- | 2 | 389 | | } |
- | 2 | 390 | | } |
- | 2 | 391 | | else |
- | 5 | 392 | | { |
- | 5 | 393 | | if (descending) |
- | 3 | 394 | | { |
- | 5 | 395 | | return new ValueExpression(null, values.OrderByDescending(v => (v.Value as Entity)?.GetAttributeValu |
- | 2 | 396 | | } |
- | 2 | 397 | | else |
- | 4 | 398 | | { |
- | 8 | 399 | | return new ValueExpression(null, values.OrderBy(v => (v.Value as Entity)?.GetAttributeValue<object>( |
- | 2 | 400 | | } |
- | 2 | 401 | | } |
- | 6 | 402 | | }; |
- | | 403 | |
|
- | 2 | 404 | | private static Func<string, IOrganizationService, Dictionary<string, string>> RetrieveColumnNames = (entityName, |
- | 19 | 405 | | { |
- | 19 | 406 | | return ((RetrieveEntityResponse)service.Execute(new RetrieveEntityRequest |
- | 19 | 407 | | { |
- | 19 | 408 | | EntityFilters = EntityFilters.Attributes, |
- | 19 | 409 | | LogicalName = entityName, |
- | 19 | 410 | | RetrieveAsIfPublished = false |
- | 19 | 411 | | })) |
- | 19 | 412 | | .EntityMetadata |
- | 19 | 413 | | .Attributes |
- | 87 | 414 | | .ToDictionary(a => a.LogicalName, a => a?.DisplayName?.UserLocalizedLabel?.Label ?? a.LogicalName); |
- | 19 | 415 | | }; |
- | | 416 | |
|
- | 2 | 417 | | public static FunctionHandler RenderRecordTable = (primary, service, tracing, organizationConfig, parameters) => |
- | 19 | 418 | | { |
- | 19 | 419 | | tracing.Trace("Parsing parameters"); |
- | 2 | 420 | |
|
- | 19 | 421 | | if (parameters.Count < 3) |
- | 2 | 422 | | { |
- | 2 | 423 | | throw new InvalidPluginExecutionException("RecordTable needs at least 3 parameters: Entities, entity nam |
- | 2 | 424 | | } |
- | 2 | 425 | |
|
- | 19 | 426 | | var records = CheckedCast<List<ValueExpression>>(parameters[0].Value, "RecordTable requires the first parame |
- | 48 | 427 | | .Select(p => (p as ValueExpression)?.Value) |
- | 19 | 428 | | .Cast<Entity>() |
- | 19 | 429 | | .ToList(); |
- | 2 | 430 | |
|
- | 19 | 431 | | tracing.Trace($"Records: {records.Count}"); |
- | 2 | 432 | |
|
- | 2 | 433 | | // We need the entity name although it should be set in the record. If no records are passed, we would fail |
- | 19 | 434 | | var entityName = CheckedCast<string>(parameters[1]?.Value, "Second parameter of the RecordTable function nee |
- | 2 | 435 | |
|
- | 19 | 436 | | if (string.IsNullOrEmpty(entityName)) |
- | 2 | 437 | | { |
- | 2 | 438 | | throw new InvalidPluginExecutionException("Second parameter of the RecordTable function needs to be the |
- | 2 | 439 | | } |
- | 2 | 440 | |
|
- | 2 | 441 | | // We need the column names explicitly, since CRM does not return new ValueExpression(null)-valued columns, |
- | 19 | 442 | | var displayColumns = CheckedCast<List<ValueExpression>>(parameters[2]?.Value, "List of column names for reco |
- | 48 | 443 | | .Select(p => p.Value) |
- | 48 | 444 | | .Select(p => p is Dictionary<string, object> ? (Dictionary<string, object>) p : new Dictionary<string, o |
- | 19 | 445 | | .ToList(); |
- | 2 | 446 | |
|
- | 19 | 447 | | tracing.Trace("Retrieving column names"); |
- | 19 | 448 | | var columnNames = RetrieveColumnNames(entityName, service); |
- | 19 | 449 | | tracing.Trace($"Column names done"); |
- | 2 | 450 | |
|
- | 19 | 451 | | var config = GetConfig(parameters); |
- | 19 | 452 | | var addRecordUrl = config.GetValue<bool>("addRecordUrl", "When setting addRecordUrl, value must be a boolean |
+ | 3 | 377 | | var values = parameters[0].Value as List<ValueExpression>; |
+ | 2 | 378 | |
|
+ | 3 | 379 | | if (!(values is IEnumerable)) |
+ | 2 | 380 | | { |
+ | 2 | 381 | | throw new InvalidPluginExecutionException("Map needs an array as first parameter."); |
+ | 2 | 382 | | } |
+ | 2 | 383 | |
|
+ | 3 | 384 | | var lambda = parameters[1].Value as Func<List<ValueExpression>, ValueExpression>; |
+ | 2 | 385 | |
|
+ | 3 | 386 | | if (lambda == null) |
+ | 2 | 387 | | { |
+ | 2 | 388 | | throw new InvalidPluginExecutionException("Lambda function must be a proper arrow function"); |
+ | 2 | 389 | | } |
+ | 2 | 390 | |
|
+ | 7 | 391 | | return new ValueExpression(null, values.Select(v => lambda(new List<ValueExpression> { v })).ToList()); |
+ | 3 | 392 | | }; |
+ | | 393 | |
|
+ | 2 | 394 | | public static FunctionHandler Sort = (primary, service, tracing, organizationConfig, parameters) => |
+ | 6 | 395 | | { |
+ | 6 | 396 | | if (parameters.Count < 1) |
+ | 2 | 397 | | { |
+ | 2 | 398 | | throw new InvalidPluginExecutionException("Sort function needs at least an array to sort and optionally |
+ | 2 | 399 | | } |
+ | 2 | 400 | |
|
+ | 6 | 401 | | var config = GetConfig(parameters); |
+ | 2 | 402 | |
|
+ | 6 | 403 | | var values = parameters[0].Value as List<ValueExpression>; |
+ | 2 | 404 | |
|
+ | 6 | 405 | | if (!(values is IEnumerable)) |
+ | 2 | 406 | | { |
+ | 2 | 407 | | throw new InvalidPluginExecutionException("Sort needs an array as first parameter."); |
+ | 2 | 408 | | } |
+ | 2 | 409 | |
|
+ | 6 | 410 | | var descending = config.GetValue<bool>("descending", "descending must be a bool"); |
+ | 6 | 411 | | var property = config.GetValue<string>("property", "property must be a string"); |
+ | 2 | 412 | |
|
+ | 6 | 413 | | if (string.IsNullOrEmpty(property)) |
+ | 3 | 414 | | { |
+ | 3 | 415 | | if (descending) |
+ | 2 | 416 | | { |
+ | 2 | 417 | | return new ValueExpression(null, values.OrderByDescending(v => v.Value).ToList()); |
+ | 2 | 418 | | } |
+ | 2 | 419 | | else |
+ | 3 | 420 | | { |
+ | 7 | 421 | | return new ValueExpression(null, values.OrderBy(v => v.Value).ToList()); |
+ | 2 | 422 | | } |
+ | 2 | 423 | | } |
+ | 2 | 424 | | else |
+ | 5 | 425 | | { |
+ | 5 | 426 | | if (descending) |
+ | 3 | 427 | | { |
+ | 5 | 428 | | return new ValueExpression(null, values.OrderByDescending(v => (v.Value as Entity)?.GetAttributeValu |
+ | 2 | 429 | | } |
+ | 2 | 430 | | else |
+ | 4 | 431 | | { |
+ | 8 | 432 | | return new ValueExpression(null, values.OrderBy(v => (v.Value as Entity)?.GetAttributeValue<object>( |
+ | 2 | 433 | | } |
+ | 2 | 434 | | } |
+ | 6 | 435 | | }; |
+ | | 436 | |
|
+ | 2 | 437 | | private static Func<string, IOrganizationService, Dictionary<string, string>> RetrieveColumnNames = (entityName, |
+ | 19 | 438 | | { |
+ | 19 | 439 | | return ((RetrieveEntityResponse)service.Execute(new RetrieveEntityRequest |
+ | 19 | 440 | | { |
+ | 19 | 441 | | EntityFilters = EntityFilters.Attributes, |
+ | 19 | 442 | | LogicalName = entityName, |
+ | 19 | 443 | | RetrieveAsIfPublished = false |
+ | 19 | 444 | | })) |
+ | 19 | 445 | | .EntityMetadata |
+ | 19 | 446 | | .Attributes |
+ | 87 | 447 | | .ToDictionary(a => a.LogicalName, a => a?.DisplayName?.UserLocalizedLabel?.Label ?? a.LogicalName); |
+ | 19 | 448 | | }; |
+ | | 449 | |
|
+ | 2 | 450 | | public static FunctionHandler RenderRecordTable = (primary, service, tracing, organizationConfig, parameters) => |
+ | 19 | 451 | | { |
+ | 19 | 452 | | tracing.Trace("Parsing parameters"); |
| 2 | 453 | |
|
- | 19 | 454 | | var tableStyle = config.GetValue("tableStyle", "tableStyle must be a string!", string.Empty); |
- | 2 | 455 | |
|
- | 19 | 456 | | if (!string.IsNullOrEmpty(tableStyle)) |
- | 4 | 457 | | { |
- | 4 | 458 | | tableStyle = $" style=\"{tableStyle}\""; |
- | 4 | 459 | | } |
- | 2 | 460 | |
|
- | 19 | 461 | | var tableHeadStyle = config.GetValue("headerStyle", "headerStyle must be a string!", @"border:1px solid blac |
- | 19 | 462 | | var tableDataStyle = config.GetValue("dataStyle", "dataStyle must be a string!", @"border:1px solid black;pa |
+ | 19 | 454 | | if (parameters.Count < 3) |
+ | 2 | 455 | | { |
+ | 2 | 456 | | throw new InvalidPluginExecutionException("RecordTable needs at least 3 parameters: Entities, entity nam |
+ | 2 | 457 | | } |
+ | 2 | 458 | |
|
+ | 19 | 459 | | var records = CheckedCast<List<ValueExpression>>(parameters[0].Value, "RecordTable requires the first parame |
+ | 48 | 460 | | .Select(p => (p as ValueExpression)?.Value) |
+ | 19 | 461 | | .Cast<Entity>() |
+ | 19 | 462 | | .ToList(); |
| 2 | 463 | |
|
- | 19 | 464 | | var evenDataStyle = config.GetValue<string>("evenDataStyle", "evenDataStyle must be a string!"); |
- | 19 | 465 | | var unevenDataStyle = config.GetValue<string>("unevenDataStyle", "unevenDataStyle must be a string!"); |
- | 2 | 466 | |
|
- | 19 | 467 | | tracing.Trace("Parsed parameters"); |
+ | 19 | 464 | | tracing.Trace($"Records: {records.Count}"); |
+ | 2 | 465 | |
|
+ | 2 | 466 | | // We need the entity name although it should be set in the record. If no records are passed, we would fail |
+ | 19 | 467 | | var entityName = CheckedCast<string>(parameters[1]?.Value, "Second parameter of the RecordTable function nee |
| 2 | 468 | |
|
- | 2 | 469 | | // Create table header |
- | 19 | 470 | | var stringBuilder = new StringBuilder($"<table{tableStyle}>\n<tr>"); |
- | 111 | 471 | | foreach (var column in displayColumns) |
- | 31 | 472 | | { |
- | 31 | 473 | | var name = string.Empty; |
- | 31 | 474 | | var columnName = column.ContainsKey("name") ? column["name"] as string : string.Empty; |
- | 2 | 475 | |
|
- | 31 | 476 | | if (columnName.Contains(":")) |
- | 3 | 477 | | { |
- | 3 | 478 | | name = columnName.Substring(columnName.IndexOf(':') + 1); |
- | 3 | 479 | | } |
- | 2 | 480 | | else |
- | 30 | 481 | | { |
- | 30 | 482 | | name = columnNames.ContainsKey(columnName) ? columnNames[columnName] : columnName; |
- | 30 | 483 | | } |
- | 2 | 484 | |
|
- | 31 | 485 | | if (column.ContainsKey("label")) |
- | 9 | 486 | | { |
- | 9 | 487 | | name = column["label"] as string; |
- | 9 | 488 | | } |
- | 2 | 489 | |
|
- | 31 | 490 | | if (column.ContainsKey("style")) |
- | 6 | 491 | | { |
- | 6 | 492 | | if (!column.ContainsKey("mergeStyle") || (bool) column["mergeStyle"]) |
- | 4 | 493 | | { |
- | 4 | 494 | | stringBuilder.AppendLine($"<th style=\"{tableHeadStyle}{column["style"]}\">{name}</th>"); |
- | 4 | 495 | | } |
- | 2 | 496 | | else |
- | 4 | 497 | | { |
- | 4 | 498 | | stringBuilder.AppendLine($"<th style=\"{column["style"]}\">{name}</th>"); |
- | 4 | 499 | | } |
- | 6 | 500 | | } |
- | 2 | 501 | | else |
- | 27 | 502 | | { |
- | 27 | 503 | | stringBuilder.AppendLine($"<th style=\"{tableHeadStyle}\">{name}</th>"); |
- | 27 | 504 | | } |
- | 31 | 505 | | } |
- | 2 | 506 | |
|
- | 2 | 507 | | // Add column for url if wanted |
- | 19 | 508 | | if (addRecordUrl) |
- | 5 | 509 | | { |
- | 5 | 510 | | stringBuilder.AppendLine($"<th style=\"{tableHeadStyle}\">URL</th>"); |
- | 5 | 511 | | } |
- | 19 | 512 | | stringBuilder.AppendLine("<tr />"); |
- | 2 | 513 | |
|
- | 19 | 514 | | if (records != null) |
- | 19 | 515 | | { |
- | 94 | 516 | | for (var i = 0; i < records.Count; i++) |
- | 31 | 517 | | { |
- | 31 | 518 | | var record = records[i]; |
- | 31 | 519 | | var isEven = i % 2 == 0; |
- | 31 | 520 | | var lineStyle = (isEven ? evenDataStyle : unevenDataStyle) ?? tableDataStyle; |
- | 2 | 521 | |
|
- | 31 | 522 | | stringBuilder.AppendLine("<tr>"); |
- | 2 | 523 | |
|
- | 191 | 524 | | foreach (var column in displayColumns) |
- | 53 | 525 | | { |
- | 53 | 526 | | var columnName = column.ContainsKey("name") ? column["name"] as string : string.Empty; |
- | 53 | 527 | | columnName = columnName.Contains(":") ? columnName.Substring(0, columnName.IndexOf(':')) : colum |
- | 2 | 528 | |
|
- | 53 | 529 | | var renderFunction = column.ContainsKey("renderFunction") ? column["renderFunction"] as Func<Lis |
- | 53 | 530 | | var entityConfig = column.ContainsKey("nameByEntity") ? column["nameByEntity"] as Dictionary<str |
- | 2 | 531 | |
|
- | 53 | 532 | | if (entityConfig != null && entityConfig.ContainsKey(record.LogicalName)) |
- | 4 | 533 | | { |
- | 4 | 534 | | columnName = entityConfig[record.LogicalName] as string; |
- | 4 | 535 | | } |
- | 2 | 536 | |
|
- | 53 | 537 | | var staticValues = column.ContainsKey("staticValueByEntity") ? column["staticValueByEntity"] as |
- | 2 | 538 | |
|
- | 2 | 539 | | string value; |
- | 2 | 540 | |
|
- | 53 | 541 | | if (staticValues != null && staticValues.ContainsKey(record.LogicalName)) |
- | 4 | 542 | | { |
- | 4 | 543 | | value = staticValues[record.LogicalName] as string; |
- | 4 | 544 | | } |
- | 49 | 545 | | else if (renderFunction != null) |
- | 3 | 546 | | { |
- | 3 | 547 | | var rowRecord = new ValueExpression(null, record); |
- | 3 | 548 | | var rowColumnName = new ValueExpression(columnName, columnName); |
- | | 549 | |
|
- | 3 | 550 | | value = renderFunction(new List<ValueExpression> { rowRecord, rowColumnName })?.Text; |
- | 3 | 551 | | } |
- | | 552 | | else |
- | 46 | 553 | | { |
- | 46 | 554 | | value = PropertyStringifier.Stringify(columnName, record, service, config); |
- | 46 | 555 | | } |
- | | 556 | |
|
- | 51 | 557 | | if (column.ContainsKey("style")) |
- | 8 | 558 | | { |
- | 8 | 559 | | if (!column.ContainsKey("mergeStyle") || (bool)column["mergeStyle"]) |
- | 4 | 560 | | { |
- | 4 | 561 | | stringBuilder.AppendLine($"<td style=\"{lineStyle}{column["style"]}\">{value}</td>"); |
- | 4 | 562 | | } |
- | | 563 | | else |
- | 4 | 564 | | { |
- | 4 | 565 | | stringBuilder.AppendLine($"<td style=\"{column["style"]}\">{value}</td>"); |
- | 4 | 566 | | } |
- | 8 | 567 | | } |
- | | 568 | | else |
- | 43 | 569 | | { |
- | 43 | 570 | | stringBuilder.AppendLine($"<td style=\"{lineStyle}\">{value}</td>"); |
- | 43 | 571 | | } |
- | 51 | 572 | | } |
- | | 573 | |
|
- | 29 | 574 | | if (addRecordUrl) |
- | 6 | 575 | | { |
- | 6 | 576 | | stringBuilder.AppendLine($"<td style=\"{lineStyle}\">{GetRecordUrl(primary, service, tracing, or |
- | 6 | 577 | | } |
- | | 578 | |
|
- | 29 | 579 | | stringBuilder.AppendLine("<tr />"); |
- | 29 | 580 | | } |
- | 17 | 581 | | } |
+ | 19 | 469 | | if (string.IsNullOrEmpty(entityName)) |
+ | 2 | 470 | | { |
+ | 2 | 471 | | throw new InvalidPluginExecutionException("Second parameter of the RecordTable function needs to be the |
+ | 2 | 472 | | } |
+ | 2 | 473 | |
|
+ | 2 | 474 | | // We need the column names explicitly, since CRM does not return new ValueExpression(null)-valued columns, |
+ | 19 | 475 | | var displayColumns = CheckedCast<List<ValueExpression>>(parameters[2]?.Value, "List of column names for reco |
+ | 48 | 476 | | .Select(p => p.Value) |
+ | 48 | 477 | | .Select(p => p is Dictionary<string, object> ? (Dictionary<string, object>) p : new Dictionary<string, o |
+ | 19 | 478 | | .ToList(); |
+ | 2 | 479 | |
|
+ | 19 | 480 | | tracing.Trace("Retrieving column names"); |
+ | 19 | 481 | | var columnNames = RetrieveColumnNames(entityName, service); |
+ | 19 | 482 | | tracing.Trace($"Column names done"); |
+ | 2 | 483 | |
|
+ | 19 | 484 | | var config = GetConfig(parameters); |
+ | 19 | 485 | | var addRecordUrl = config.GetValue<bool>("addRecordUrl", "When setting addRecordUrl, value must be a boolean |
+ | 2 | 486 | |
|
+ | 19 | 487 | | var tableStyle = config.GetValue("tableStyle", "tableStyle must be a string!", string.Empty); |
+ | 2 | 488 | |
|
+ | 19 | 489 | | if (!string.IsNullOrEmpty(tableStyle)) |
+ | 4 | 490 | | { |
+ | 4 | 491 | | tableStyle = $" style=\"{tableStyle}\""; |
+ | 4 | 492 | | } |
+ | 2 | 493 | |
|
+ | 19 | 494 | | var tableHeadStyle = config.GetValue("headerStyle", "headerStyle must be a string!", @"border:1px solid blac |
+ | 19 | 495 | | var tableDataStyle = config.GetValue("dataStyle", "dataStyle must be a string!", @"border:1px solid black;pa |
+ | 2 | 496 | |
|
+ | 19 | 497 | | var evenDataStyle = config.GetValue<string>("evenDataStyle", "evenDataStyle must be a string!"); |
+ | 19 | 498 | | var unevenDataStyle = config.GetValue<string>("unevenDataStyle", "unevenDataStyle must be a string!"); |
+ | 2 | 499 | |
|
+ | 19 | 500 | | tracing.Trace("Parsed parameters"); |
+ | 2 | 501 | |
|
+ | 2 | 502 | | // Create table header |
+ | 19 | 503 | | var stringBuilder = new StringBuilder($"<table{tableStyle}>\n<tr>"); |
+ | 111 | 504 | | foreach (var column in displayColumns) |
+ | 31 | 505 | | { |
+ | 31 | 506 | | var name = string.Empty; |
+ | 31 | 507 | | var columnName = column.ContainsKey("name") ? column["name"] as string : string.Empty; |
+ | 2 | 508 | |
|
+ | 31 | 509 | | if (columnName.Contains(":")) |
+ | 3 | 510 | | { |
+ | 3 | 511 | | name = columnName.Substring(columnName.IndexOf(':') + 1); |
+ | 3 | 512 | | } |
+ | 2 | 513 | | else |
+ | 30 | 514 | | { |
+ | 30 | 515 | | name = columnNames.ContainsKey(columnName) ? columnNames[columnName] : columnName; |
+ | 30 | 516 | | } |
+ | 2 | 517 | |
|
+ | 31 | 518 | | if (column.ContainsKey("label")) |
+ | 9 | 519 | | { |
+ | 9 | 520 | | name = column["label"] as string; |
+ | 9 | 521 | | } |
+ | 2 | 522 | |
|
+ | 31 | 523 | | if (column.ContainsKey("style")) |
+ | 6 | 524 | | { |
+ | 6 | 525 | | if (!column.ContainsKey("mergeStyle") || (bool) column["mergeStyle"]) |
+ | 4 | 526 | | { |
+ | 4 | 527 | | stringBuilder.AppendLine($"<th style=\"{tableHeadStyle}{column["style"]}\">{name}</th>"); |
+ | 4 | 528 | | } |
+ | 2 | 529 | | else |
+ | 4 | 530 | | { |
+ | 4 | 531 | | stringBuilder.AppendLine($"<th style=\"{column["style"]}\">{name}</th>"); |
+ | 4 | 532 | | } |
+ | 6 | 533 | | } |
+ | 2 | 534 | | else |
+ | 27 | 535 | | { |
+ | 27 | 536 | | stringBuilder.AppendLine($"<th style=\"{tableHeadStyle}\">{name}</th>"); |
+ | 27 | 537 | | } |
+ | 31 | 538 | | } |
+ | 2 | 539 | |
|
+ | 2 | 540 | | // Add column for url if wanted |
+ | 19 | 541 | | if (addRecordUrl) |
+ | 5 | 542 | | { |
+ | 5 | 543 | | stringBuilder.AppendLine($"<th style=\"{tableHeadStyle}\">URL</th>"); |
+ | 5 | 544 | | } |
+ | 19 | 545 | | stringBuilder.AppendLine("</tr>"); |
+ | 2 | 546 | |
|
+ | 19 | 547 | | if (records != null) |
+ | 19 | 548 | | { |
+ | 94 | 549 | | for (var i = 0; i < records.Count; i++) |
+ | 31 | 550 | | { |
+ | 31 | 551 | | var record = records[i]; |
+ | 31 | 552 | | var isEven = i % 2 == 0; |
+ | 31 | 553 | | var lineStyle = (isEven ? evenDataStyle : unevenDataStyle) ?? tableDataStyle; |
+ | 2 | 554 | |
|
+ | 31 | 555 | | stringBuilder.AppendLine("<tr>"); |
+ | 2 | 556 | |
|
+ | 191 | 557 | | foreach (var column in displayColumns) |
+ | 53 | 558 | | { |
+ | 53 | 559 | | var columnName = column.ContainsKey("name") ? column["name"] as string : string.Empty; |
+ | 53 | 560 | | columnName = columnName.Contains(":") ? columnName.Substring(0, columnName.IndexOf(':')) : colum |
+ | 2 | 561 | |
|
+ | 53 | 562 | | var renderFunction = column.ContainsKey("renderFunction") ? column["renderFunction"] as Func<Lis |
+ | 53 | 563 | | var entityConfig = column.ContainsKey("nameByEntity") ? column["nameByEntity"] as Dictionary<str |
+ | 2 | 564 | |
|
+ | 53 | 565 | | if (entityConfig != null && entityConfig.ContainsKey(record.LogicalName)) |
+ | 4 | 566 | | { |
+ | 4 | 567 | | columnName = entityConfig[record.LogicalName] as string; |
+ | 4 | 568 | | } |
+ | 2 | 569 | |
|
+ | 53 | 570 | | var staticValues = column.ContainsKey("staticValueByEntity") ? column["staticValueByEntity"] as |
+ | 2 | 571 | |
|
+ | 2 | 572 | | string value; |
+ | 2 | 573 | |
|
+ | 53 | 574 | | if (staticValues != null && staticValues.ContainsKey(record.LogicalName)) |
+ | 4 | 575 | | { |
+ | 4 | 576 | | value = staticValues[record.LogicalName] as string; |
+ | 4 | 577 | | } |
+ | 49 | 578 | | else if (renderFunction != null) |
+ | 3 | 579 | | { |
+ | 3 | 580 | | var rowRecord = new ValueExpression(null, record); |
+ | 3 | 581 | | var rowColumnName = new ValueExpression(columnName, columnName); |
| | 582 | |
|
- | 17 | 583 | | stringBuilder.AppendLine("</table>"); |
- | 17 | 584 | | var table = stringBuilder.ToString(); |
- | | 585 | |
|
- | 17 | 586 | | return new ValueExpression(table, table); |
- | 17 | 587 | | }; |
- | | 588 | |
|
- | 2 | 589 | | public static FunctionHandler Fetch = (primary, service, tracing, organizationConfig, parameters) => |
- | 24 | 590 | | { |
- | 24 | 591 | | if (parameters.Count < 1) |
- | 2 | 592 | | { |
- | 2 | 593 | | throw new InvalidPluginExecutionException("Fetch needs at least one parameter: Fetch XML."); |
- | 2 | 594 | | } |
- | 2 | 595 | |
|
- | 24 | 596 | | var fetch = parameters[0].Value as string; |
- | 2 | 597 | |
|
- | 24 | 598 | | if (string.IsNullOrEmpty(fetch)) |
- | 2 | 599 | | { |
- | 2 | 600 | | throw new InvalidPluginExecutionException("First parameter of Fetch function needs to be a fetchXml stri |
- | 2 | 601 | | } |
- | 2 | 602 | |
|
- | 24 | 603 | | var references = new List<object> { primary.Id }; |
- | 2 | 604 | |
|
- | 24 | 605 | | if (parameters.Count > 1) |
- | 5 | 606 | | { |
- | 5 | 607 | | if (!(parameters[1].Value is List<ValueExpression>)) |
- | 2 | 608 | | { |
- | 2 | 609 | | throw new InvalidPluginExecutionException("Fetch parameters must be an array expression"); |
- | 2 | 610 | | } |
- | 2 | 611 | |
|
- | 5 | 612 | | var @params = parameters[1].Value as List<ValueExpression>; |
- | 2 | 613 | |
|
- | 5 | 614 | | if (@params is IEnumerable) |
- | 5 | 615 | | { |
- | 17 | 616 | | foreach (var item in @params) |
- | 5 | 617 | | { |
- | 5 | 618 | | var reference = item.Value as EntityReference; |
- | 5 | 619 | | if (reference != null) |
- | 4 | 620 | | { |
- | 4 | 621 | | references.Add(reference.Id); |
- | 4 | 622 | | continue; |
- | 2 | 623 | | } |
- | 2 | 624 | |
|
- | 3 | 625 | | var entity = item.Value as Entity; |
- | 3 | 626 | | if (entity != null) |
- | 2 | 627 | | { |
- | 2 | 628 | | references.Add(entity.Id); |
- | 2 | 629 | | continue; |
- | 2 | 630 | | } |
- | 2 | 631 | |
|
- | 3 | 632 | | var optionSet = item.Value as OptionSetValue; |
- | 3 | 633 | | if (optionSet != null) |
- | 2 | 634 | | { |
- | 2 | 635 | | references.Add(optionSet.Value); |
- | 2 | 636 | | continue; |
- | 2 | 637 | | } |
- | 2 | 638 | |
|
- | 3 | 639 | | references.Add(item.Value); |
- | 3 | 640 | | } |
- | 5 | 641 | | } |
- | 5 | 642 | | } |
- | 2 | 643 | |
|
- | 24 | 644 | | var records = new List<object>(); |
- | 2 | 645 | |
|
- | 24 | 646 | | var query = fetch; |
- | 2 | 647 | |
|
- | 24 | 648 | | if (primary != null) |
- | 24 | 649 | | { |
- | 24 | 650 | | query = query.Replace("{0}", references[0].ToString()); |
- | 24 | 651 | | } |
- | 2 | 652 | |
|
- | 24 | 653 | | tracing.Trace("Replacing references"); |
- | 2 | 654 | |
|
- | 24 | 655 | | var referenceRegex = new Regex("{([0-9]+)}", RegexOptions.Compiled | RegexOptions.CultureInvariant); |
- | 24 | 656 | | query = referenceRegex.Replace(query, match => |
- | 27 | 657 | | { |
- | 27 | 658 | | var capture = match.Groups[1].Value; |
- | 27 | 659 | | var referenceNumber = int.Parse(capture); |
- | 24 | 660 | |
|
- | 27 | 661 | | if (referenceNumber >= references.Count) |
- | 24 | 662 | | { |
- | 24 | 663 | | throw new InvalidPluginExecutionException($"You tried using reference {referenceNumber} in fetch, bu |
- | 24 | 664 | | } |
- | 24 | 665 | |
|
- | 27 | 666 | | return references[referenceNumber]?.ToString(); |
- | 27 | 667 | | }); |
- | 2 | 668 | |
|
- | 24 | 669 | | tracing.Trace("References replaced"); |
- | 24 | 670 | | tracing.Trace($"Executing fetch: {query}"); |
- | 24 | 671 | | records.AddRange(service.RetrieveMultiple(new FetchExpression(query)).Entities); |
- | 2 | 672 | |
|
- | 54 | 673 | | return new ValueExpression(string.Empty, records.Select(r => new ValueExpression(string.Empty, r)).ToList()) |
- | 24 | 674 | | }; |
- | | 675 | |
|
- | 2 | 676 | | public static FunctionHandler GetValue = (primary, service, tracing, organizationConfig, parameters) => |
- | 103 | 677 | | { |
- | 103 | 678 | | if (primary == null) |
- | 2 | 679 | | { |
- | 2 | 680 | | return new ValueExpression(null); |
- | 2 | 681 | | } |
- | 2 | 682 | |
|
- | 103 | 683 | | var field = parameters.FirstOrDefault()?.Text; |
- | 2 | 684 | |
|
- | 103 | 685 | | if (string.IsNullOrEmpty(field)) |
- | 2 | 686 | | { |
- | 2 | 687 | | throw new InvalidPluginExecutionException("First parameter of Value function needs to be the field name |
- | 2 | 688 | | } |
- | 2 | 689 | |
|
- | 103 | 690 | | var target = primary; |
- | 103 | 691 | | var config = GetConfig(parameters); |
- | 2 | 692 | |
|
- | 103 | 693 | | if (config.Contains("explicitTarget")) |
- | 10 | 694 | | { |
- | 10 | 695 | | if (config.IsSet("explicitTarget")) |
- | 9 | 696 | | { |
- | 9 | 697 | | var explicitTarget = config.GetValue<Entity>("explicitTarget", "explicitTarget must be an entity!"); |
- | 2 | 698 | |
|
- | 9 | 699 | | target = explicitTarget; |
- | 9 | 700 | | } |
- | 2 | 701 | | else |
- | 3 | 702 | | { |
- | 3 | 703 | | return new ValueExpression(string.Empty, string.Empty); |
- | 2 | 704 | | } |
- | 9 | 705 | | } |
- | 2 | 706 | |
|
- | 102 | 707 | | if (field == null) |
- | 2 | 708 | | { |
- | 2 | 709 | | throw new InvalidPluginExecutionException("Value requires a field target string as input"); |
- | 2 | 710 | | } |
- | 2 | 711 | |
|
- | 102 | 712 | | return DataRetriever.ResolveTokenValue(field, target, service, config); |
- | 103 | 713 | | }; |
- | | 714 | |
|
- | 2 | 715 | | public static FunctionHandler Join = (primary, service, tracing, organizationConfig, parameters) => |
- | 9 | 716 | | { |
- | 9 | 717 | | if (parameters.Count < 2) |
- | 2 | 718 | | { |
- | 2 | 719 | | throw new InvalidPluginExecutionException("Join function needs at lease two parameters: Separator and an |
- | 2 | 720 | | } |
- | 2 | 721 | |
|
- | 9 | 722 | | var separator = parameters.FirstOrDefault()?.Text; |
- | 2 | 723 | |
|
- | 9 | 724 | | if (string.IsNullOrEmpty(separator)) |
- | 2 | 725 | | { |
- | 2 | 726 | | throw new InvalidPluginExecutionException("First parameter of Join function needs to be the separator st |
- | 2 | 727 | | } |
- | 2 | 728 | |
|
- | 9 | 729 | | var values = parameters[1].Value as List<ValueExpression>; |
- | 2 | 730 | |
|
- | 9 | 731 | | if (!(values is IEnumerable)) |
- | 2 | 732 | | { |
- | 2 | 733 | | throw new InvalidPluginExecutionException("The values parameter needs to be an enumerable, please wrap t |
- | 2 | 734 | | } |
- | 2 | 735 | |
|
- | 9 | 736 | | var config = GetConfig(parameters); |
- | 9 | 737 | | var removeEmptyEntries = false; |
- | 2 | 738 | |
|
- | 9 | 739 | | if (parameters.Count > 2 && parameters[2].Value is bool) |
- | 5 | 740 | | { |
- | 5 | 741 | | removeEmptyEntries = (bool) parameters[2].Value; |
- | 5 | 742 | | } |
- | 2 | 743 | |
|
- | 9 | 744 | | var valuesToConcatenate = values |
- | 33 | 745 | | .Where(v => !removeEmptyEntries || !string.IsNullOrEmpty(v.Text)) |
- | 31 | 746 | | .Select(v => v.Text); |
- | 2 | 747 | |
|
- | 9 | 748 | | var joined = string.Join(separator, valuesToConcatenate); |
- | 2 | 749 | |
|
- | 9 | 750 | | return new ValueExpression(joined, joined); |
- | 9 | 751 | | }; |
- | | 752 | |
|
- | 2 | 753 | | public static FunctionHandler NewLine = (primary, service, tracing, organizationConfig, parameters) => |
- | 3 | 754 | | { |
- | 3 | 755 | | return new ValueExpression(Environment.NewLine, Environment.NewLine); |
- | 3 | 756 | | }; |
- | | 757 | |
|
- | 2 | 758 | | public static FunctionHandler Concat = (primary, service, tracing, organizationConfig, parameters) => |
- | 5 | 759 | | { |
- | 5 | 760 | | var text = ""; |
+ | 3 | 583 | | value = renderFunction(new List<ValueExpression> { rowRecord, rowColumnName })?.Text; |
+ | 3 | 584 | | } |
+ | | 585 | | else |
+ | 46 | 586 | | { |
+ | 46 | 587 | | value = PropertyStringifier.Stringify(columnName, record, service, config); |
+ | 46 | 588 | | } |
+ | | 589 | |
|
+ | 51 | 590 | | if (column.ContainsKey("style")) |
+ | 8 | 591 | | { |
+ | 8 | 592 | | if (!column.ContainsKey("mergeStyle") || (bool)column["mergeStyle"]) |
+ | 4 | 593 | | { |
+ | 4 | 594 | | stringBuilder.AppendLine($"<td style=\"{lineStyle}{column["style"]}\">{value}</td>"); |
+ | 4 | 595 | | } |
+ | | 596 | | else |
+ | 4 | 597 | | { |
+ | 4 | 598 | | stringBuilder.AppendLine($"<td style=\"{column["style"]}\">{value}</td>"); |
+ | 4 | 599 | | } |
+ | 8 | 600 | | } |
+ | | 601 | | else |
+ | 43 | 602 | | { |
+ | 43 | 603 | | stringBuilder.AppendLine($"<td style=\"{lineStyle}\">{value}</td>"); |
+ | 43 | 604 | | } |
+ | 51 | 605 | | } |
+ | | 606 | |
|
+ | 29 | 607 | | if (addRecordUrl) |
+ | 6 | 608 | | { |
+ | 6 | 609 | | stringBuilder.AppendLine($"<td style=\"{lineStyle}\">{GetRecordUrl(primary, service, tracing, or |
+ | 6 | 610 | | } |
+ | | 611 | |
|
+ | 29 | 612 | | stringBuilder.AppendLine("</tr>"); |
+ | 29 | 613 | | } |
+ | 17 | 614 | | } |
+ | | 615 | |
|
+ | 17 | 616 | | stringBuilder.AppendLine("</table>"); |
+ | 17 | 617 | | var table = stringBuilder.ToString(); |
+ | | 618 | |
|
+ | 17 | 619 | | return new ValueExpression(table, table); |
+ | 17 | 620 | | }; |
+ | | 621 | |
|
+ | 2 | 622 | | public static FunctionHandler Fetch = (primary, service, tracing, organizationConfig, parameters) => |
+ | 24 | 623 | | { |
+ | 24 | 624 | | if (parameters.Count < 1) |
+ | 2 | 625 | | { |
+ | 2 | 626 | | throw new InvalidPluginExecutionException("Fetch needs at least one parameter: Fetch XML."); |
+ | 2 | 627 | | } |
+ | 2 | 628 | |
|
+ | 24 | 629 | | var fetch = parameters[0].Value as string; |
+ | 2 | 630 | |
|
+ | 24 | 631 | | if (string.IsNullOrEmpty(fetch)) |
+ | 2 | 632 | | { |
+ | 2 | 633 | | throw new InvalidPluginExecutionException("First parameter of Fetch function needs to be a fetchXml stri |
+ | 2 | 634 | | } |
+ | 2 | 635 | |
|
+ | 24 | 636 | | var references = new List<object> { primary.Id }; |
+ | 2 | 637 | |
|
+ | 24 | 638 | | if (parameters.Count > 1) |
+ | 5 | 639 | | { |
+ | 5 | 640 | | if (!(parameters[1].Value is List<ValueExpression>)) |
+ | 2 | 641 | | { |
+ | 2 | 642 | | throw new InvalidPluginExecutionException("Fetch parameters must be an array expression"); |
+ | 2 | 643 | | } |
+ | 2 | 644 | |
|
+ | 5 | 645 | | var @params = parameters[1].Value as List<ValueExpression>; |
+ | 2 | 646 | |
|
+ | 5 | 647 | | if (@params is IEnumerable) |
+ | 5 | 648 | | { |
+ | 17 | 649 | | foreach (var item in @params) |
+ | 5 | 650 | | { |
+ | 5 | 651 | | var reference = item.Value as EntityReference; |
+ | 5 | 652 | | if (reference != null) |
+ | 4 | 653 | | { |
+ | 4 | 654 | | references.Add(reference.Id); |
+ | 4 | 655 | | continue; |
+ | 2 | 656 | | } |
+ | 2 | 657 | |
|
+ | 3 | 658 | | var entity = item.Value as Entity; |
+ | 3 | 659 | | if (entity != null) |
+ | 2 | 660 | | { |
+ | 2 | 661 | | references.Add(entity.Id); |
+ | 2 | 662 | | continue; |
+ | 2 | 663 | | } |
+ | 2 | 664 | |
|
+ | 3 | 665 | | var optionSet = item.Value as OptionSetValue; |
+ | 3 | 666 | | if (optionSet != null) |
+ | 2 | 667 | | { |
+ | 2 | 668 | | references.Add(optionSet.Value); |
+ | 2 | 669 | | continue; |
+ | 2 | 670 | | } |
+ | 2 | 671 | |
|
+ | 3 | 672 | | references.Add(item.Value); |
+ | 3 | 673 | | } |
+ | 5 | 674 | | } |
+ | 5 | 675 | | } |
+ | 2 | 676 | |
|
+ | 24 | 677 | | var records = new List<object>(); |
+ | 2 | 678 | |
|
+ | 24 | 679 | | var query = fetch; |
+ | 2 | 680 | |
|
+ | 24 | 681 | | if (primary != null) |
+ | 24 | 682 | | { |
+ | 24 | 683 | | query = query.Replace("{0}", references[0].ToString()); |
+ | 24 | 684 | | } |
+ | 2 | 685 | |
|
+ | 24 | 686 | | tracing.Trace("Replacing references"); |
+ | 2 | 687 | |
|
+ | 24 | 688 | | var referenceRegex = new Regex("{([0-9]+)}", RegexOptions.Compiled | RegexOptions.CultureInvariant); |
+ | 24 | 689 | | query = referenceRegex.Replace(query, match => |
+ | 27 | 690 | | { |
+ | 27 | 691 | | var capture = match.Groups[1].Value; |
+ | 27 | 692 | | var referenceNumber = int.Parse(capture); |
+ | 24 | 693 | |
|
+ | 27 | 694 | | if (referenceNumber >= references.Count) |
+ | 24 | 695 | | { |
+ | 24 | 696 | | throw new InvalidPluginExecutionException($"You tried using reference {referenceNumber} in fetch, bu |
+ | 24 | 697 | | } |
+ | 24 | 698 | |
|
+ | 27 | 699 | | return references[referenceNumber]?.ToString(); |
+ | 27 | 700 | | }); |
+ | 2 | 701 | |
|
+ | 24 | 702 | | tracing.Trace("References replaced"); |
+ | 24 | 703 | | tracing.Trace($"Executing fetch: {query}"); |
+ | 24 | 704 | | records.AddRange(service.RetrieveMultiple(new FetchExpression(query)).Entities); |
+ | 2 | 705 | |
|
+ | 54 | 706 | | return new ValueExpression(string.Empty, records.Select(r => new ValueExpression(string.Empty, r)).ToList()) |
+ | 24 | 707 | | }; |
+ | | 708 | |
|
+ | 2 | 709 | | public static FunctionHandler GetValue = (primary, service, tracing, organizationConfig, parameters) => |
+ | 108 | 710 | | { |
+ | 108 | 711 | | if (primary == null) |
+ | 2 | 712 | | { |
+ | 2 | 713 | | return new ValueExpression(null); |
+ | 2 | 714 | | } |
+ | 2 | 715 | |
|
+ | 108 | 716 | | var field = parameters.FirstOrDefault()?.Text; |
+ | 2 | 717 | |
|
+ | 108 | 718 | | if (string.IsNullOrEmpty(field)) |
+ | 2 | 719 | | { |
+ | 2 | 720 | | throw new InvalidPluginExecutionException("First parameter of Value function needs to be the field name |
+ | 2 | 721 | | } |
+ | 2 | 722 | |
|
+ | 108 | 723 | | var target = primary; |
+ | 108 | 724 | | var config = GetConfig(parameters); |
+ | 2 | 725 | |
|
+ | 108 | 726 | | if (config.Contains("explicitTarget")) |
+ | 10 | 727 | | { |
+ | 10 | 728 | | if (config.IsSet("explicitTarget")) |
+ | 9 | 729 | | { |
+ | 9 | 730 | | var explicitTarget = config.GetValue<Entity>("explicitTarget", "explicitTarget must be an entity!"); |
+ | 2 | 731 | |
|
+ | 9 | 732 | | target = explicitTarget; |
+ | 9 | 733 | | } |
+ | 2 | 734 | | else |
+ | 3 | 735 | | { |
+ | 3 | 736 | | return new ValueExpression(string.Empty, string.Empty); |
+ | 2 | 737 | | } |
+ | 9 | 738 | | } |
+ | 2 | 739 | |
|
+ | 107 | 740 | | if (field == null) |
+ | 2 | 741 | | { |
+ | 2 | 742 | | throw new InvalidPluginExecutionException("Value requires a field target string as input"); |
+ | 2 | 743 | | } |
+ | 2 | 744 | |
|
+ | 107 | 745 | | return DataRetriever.ResolveTokenValue(field, target, service, config); |
+ | 108 | 746 | | }; |
+ | | 747 | |
|
+ | 2 | 748 | | public static FunctionHandler Join = (primary, service, tracing, organizationConfig, parameters) => |
+ | 9 | 749 | | { |
+ | 9 | 750 | | if (parameters.Count < 2) |
+ | 2 | 751 | | { |
+ | 2 | 752 | | throw new InvalidPluginExecutionException("Join function needs at lease two parameters: Separator and an |
+ | 2 | 753 | | } |
+ | 2 | 754 | |
|
+ | 9 | 755 | | var separator = parameters.FirstOrDefault()?.Text; |
+ | 2 | 756 | |
|
+ | 9 | 757 | | if (string.IsNullOrEmpty(separator)) |
+ | 2 | 758 | | { |
+ | 2 | 759 | | throw new InvalidPluginExecutionException("First parameter of Join function needs to be the separator st |
+ | 2 | 760 | | } |
| 2 | 761 | |
|
- | 31 | 762 | | foreach (var parameter in parameters) |
- | 12 | 763 | | { |
- | 12 | 764 | | text += parameter.Text; |
- | 12 | 765 | | } |
- | 2 | 766 | |
|
- | 5 | 767 | | return new ValueExpression(text, text); |
- | 5 | 768 | | }; |
- | | 769 | |
|
- | | 770 | | private static T CheckedCast<T>(object input, string errorMessage, bool failOnError = true) |
- | 147 | 771 | | { |
- | 147 | 772 | | var value = input; |
- | | 773 | |
|
- | 147 | 774 | | if (input is Money) |
- | 0 | 775 | | { |
- | 0 | 776 | | value = (input as Money).Value; |
- | 0 | 777 | | } |
- | | 778 | |
|
- | 147 | 779 | | if (input is OptionSetValue) |
- | 0 | 780 | | { |
- | 0 | 781 | | value = (input as OptionSetValue).Value; |
- | 0 | 782 | | } |
- | | 783 | |
|
- | 147 | 784 | | if (!(value is T)) |
- | 6 | 785 | | { |
- | 6 | 786 | | if (failOnError) |
- | 1 | 787 | | { |
- | 1 | 788 | | throw new InvalidPluginExecutionException(errorMessage); |
- | | 789 | | } |
+ | 9 | 762 | | var values = parameters[1].Value as List<ValueExpression>; |
+ | 2 | 763 | |
|
+ | 9 | 764 | | if (!(values is IEnumerable)) |
+ | 2 | 765 | | { |
+ | 2 | 766 | | throw new InvalidPluginExecutionException("The values parameter needs to be an enumerable, please wrap t |
+ | 2 | 767 | | } |
+ | 2 | 768 | |
|
+ | 9 | 769 | | var config = GetConfig(parameters); |
+ | 9 | 770 | | var removeEmptyEntries = false; |
+ | 2 | 771 | |
|
+ | 9 | 772 | | if (parameters.Count > 2 && parameters[2].Value is bool) |
+ | 5 | 773 | | { |
+ | 5 | 774 | | removeEmptyEntries = (bool) parameters[2].Value; |
+ | 5 | 775 | | } |
+ | 2 | 776 | |
|
+ | 9 | 777 | | var valuesToConcatenate = values |
+ | 33 | 778 | | .Where(v => !removeEmptyEntries || !string.IsNullOrEmpty(v.Text)) |
+ | 31 | 779 | | .Select(v => v.Text); |
+ | 2 | 780 | |
|
+ | 9 | 781 | | var joined = string.Join(separator, valuesToConcatenate); |
+ | 2 | 782 | |
|
+ | 9 | 783 | | return new ValueExpression(joined, joined); |
+ | 9 | 784 | | }; |
+ | | 785 | |
|
+ | 2 | 786 | | public static FunctionHandler NewLine = (primary, service, tracing, organizationConfig, parameters) => |
+ | 3 | 787 | | { |
+ | 3 | 788 | | return new ValueExpression(Environment.NewLine, Environment.NewLine); |
+ | 3 | 789 | | }; |
| | 790 | |
|
- | 5 | 791 | | return default(T); |
- | | 792 | | } |
- | | 793 | |
|
- | 141 | 794 | | return (T)value; |
- | 146 | 795 | | } |
- | | 796 | |
|
- | 2 | 797 | | public static FunctionHandler Substring = (primary, service, tracing, organizationConfig, parameters) => |
- | 11 | 798 | | { |
- | 11 | 799 | | if (parameters.Count < 2) |
- | 3 | 800 | | { |
- | 3 | 801 | | throw new InvalidPluginExecutionException("Substring expects at least two parameters: text, start index |
- | 2 | 802 | | } |
- | 2 | 803 | |
|
- | 10 | 804 | | var text = parameters[0].Text; |
- | 10 | 805 | | var startIndex = CheckedCast<int>(parameters[1].Value, "Start index parameter must be an int!"); |
- | 9 | 806 | | var length = -1; |
- | 2 | 807 | |
|
- | 9 | 808 | | if (parameters.Count > 2) |
- | 8 | 809 | | { |
- | 8 | 810 | | length = CheckedCast<int>(parameters[2].Value, "Length parameter must be an int!"); |
- | 8 | 811 | | } |
- | 2 | 812 | |
|
- | 9 | 813 | | var subString = length > -1 ? text.Substring(startIndex, length) : text.Substring(startIndex); |
- | 9 | 814 | | return new ValueExpression(subString, subString); |
- | 9 | 815 | | }; |
+ | 2 | 791 | | public static FunctionHandler Concat = (primary, service, tracing, organizationConfig, parameters) => |
+ | 5 | 792 | | { |
+ | 5 | 793 | | var text = ""; |
+ | 2 | 794 | |
|
+ | 31 | 795 | | foreach (var parameter in parameters) |
+ | 12 | 796 | | { |
+ | 12 | 797 | | text += parameter.Text; |
+ | 12 | 798 | | } |
+ | 2 | 799 | |
|
+ | 5 | 800 | | return new ValueExpression(text, text); |
+ | 5 | 801 | | }; |
+ | | 802 | |
|
+ | | 803 | | private static T CheckedCast<T>(object input, string errorMessage, bool failOnError = true) |
+ | 156 | 804 | | { |
+ | 156 | 805 | | var value = input; |
+ | | 806 | |
|
+ | 156 | 807 | | if (input is Money) |
+ | 0 | 808 | | { |
+ | 0 | 809 | | value = (input as Money).Value; |
+ | 0 | 810 | | } |
+ | | 811 | |
|
+ | 156 | 812 | | if (input is OptionSetValue) |
+ | 0 | 813 | | { |
+ | 0 | 814 | | value = (input as OptionSetValue).Value; |
+ | 0 | 815 | | } |
| | 816 | |
|
- | 2 | 817 | | public static FunctionHandler IndexOf = (primary, service, tracing, organizationConfig, parameters) => |
- | 4 | 818 | | { |
- | 4 | 819 | | if (parameters.Count < 2) |
- | 3 | 820 | | { |
- | 3 | 821 | | throw new InvalidPluginExecutionException("Substring expects at least two parameters: searchText and sea |
- | 2 | 822 | | } |
- | 2 | 823 | |
|
- | 3 | 824 | | var text = parameters[0].Text; |
- | 3 | 825 | | var searchExpression = parameters[1].Text; |
- | 2 | 826 | |
|
- | 3 | 827 | | var index = text.IndexOf(searchExpression); |
- | 3 | 828 | | return new ValueExpression($"{index}", index); |
- | 3 | 829 | | }; |
- | | 830 | |
|
- | 2 | 831 | | public static FunctionHandler Replace = (primary, service, tracing, organizationConfig, parameters) => |
- | 4 | 832 | | { |
- | 4 | 833 | | if (parameters.Count < 3) |
- | 3 | 834 | | { |
- | 3 | 835 | | throw new InvalidPluginExecutionException("Replace expects three parameters: text input, regex pattern, |
- | 2 | 836 | | } |
- | 2 | 837 | |
|
- | 3 | 838 | | var input = parameters[0].Text; |
- | 3 | 839 | | var pattern = parameters[1].Text; |
- | 3 | 840 | | var replacement = parameters[2].Text; |
- | 2 | 841 | |
|
- | 3 | 842 | | var replaced = Regex.Replace(input, pattern, replacement); |
- | 2 | 843 | |
|
- | 3 | 844 | | return new ValueExpression(replaced, replaced); |
- | 3 | 845 | | }; |
- | | 846 | |
|
- | 2 | 847 | | public static FunctionHandler Array = (primary, service, tracing, organizationConfig, parameters) => |
- | 34 | 848 | | { |
- | 95 | 849 | | return new ValueExpression(string.Join(", ", parameters.Select(p => p.Text)), parameters); |
- | 34 | 850 | | }; |
- | | 851 | |
|
- | 2 | 852 | | public static FunctionHandler DateTimeNow = (primary, service, tracing, organizationConfig, parameters) => |
- | 3 | 853 | | { |
- | 3 | 854 | | var date = DateTime.Now; |
- | 3 | 855 | | return new ValueExpression(date.ToString("o", CultureInfo.InvariantCulture), date); |
- | 3 | 856 | | }; |
- | | 857 | |
|
- | 2 | 858 | | public static FunctionHandler DateTimeUtcNow = (primary, service, tracing, organizationConfig, parameters) => |
- | 5 | 859 | | { |
- | 5 | 860 | | var date = DateTime.UtcNow; |
- | 5 | 861 | | return new ValueExpression(date.ToString("o", CultureInfo.InvariantCulture), date); |
- | 5 | 862 | | }; |
- | | 863 | |
|
- | 2 | 864 | | public static FunctionHandler Static = (primary, service, tracing, organizationConfig, parameters) => |
- | 8 | 865 | | { |
- | 8 | 866 | | if (parameters.Count < 1) |
- | 2 | 867 | | { |
- | 2 | 868 | | throw new InvalidOperationException("You have to pass a static value"); |
- | 2 | 869 | | } |
- | 2 | 870 | |
|
- | 8 | 871 | | var parameter = parameters[0]; |
- | 8 | 872 | | return new ValueExpression(parameter.Text, parameter.Value); |
- | 8 | 873 | | }; |
- | | 874 | |
|
- | 2 | 875 | | public static FunctionHandler DateToString = (primary, service, tracing, organizationConfig, parameters) => |
- | 5 | 876 | | { |
- | 5 | 877 | | if (parameters.Count < 1) |
- | 2 | 878 | | { |
- | 2 | 879 | | throw new Exception("No date to stringify"); |
- | 2 | 880 | | } |
- | 2 | 881 | |
|
- | 5 | 882 | | var date = CheckedCast<DateTime>(parameters[0].Value, "You need to pass a date"); |
- | 5 | 883 | | var config = GetConfig(parameters); |
- | 5 | 884 | | var format = config.GetValue<string>("format", "format must be a string!"); |
- | 2 | 885 | |
|
- | 5 | 886 | | if (!string.IsNullOrEmpty(format)) |
- | 5 | 887 | | { |
- | 5 | 888 | | return new ValueExpression(date.ToString(format), date.ToString(format)); |
- | 2 | 889 | | } |
- | 2 | 890 | |
|
- | 2 | 891 | | return new ValueExpression(date.ToString(CultureInfo.InvariantCulture), date.ToString(CultureInfo.InvariantC |
- | 5 | 892 | | }; |
- | | 893 | |
|
- | 2 | 894 | | public static FunctionHandler Format = (primary, service, tracing, organizationConfig, parameters) => |
- | 8 | 895 | | { |
- | 8 | 896 | | if (parameters.Count < 2) |
- | 3 | 897 | | { |
- | 3 | 898 | | throw new InvalidPluginExecutionException("Format needs a value to format and a config for defining furt |
- | 2 | 899 | | } |
- | 2 | 900 | |
|
- | 7 | 901 | | var value = parameters[0].Value; |
- | 7 | 902 | | var config = GetConfig(parameters); |
- | 7 | 903 | | var format = config.GetValue<string>("format", "format must be a string!"); |
- | 2 | 904 | |
|
- | 7 | 905 | | var knownTypes = new Dictionary<Type, Func<object, ValueExpression>> |
- | 7 | 906 | | { |
- | 12 | 907 | | { typeof(Money), (obj) => { var val = obj as Money; var formatted = string.Format(CultureInfo.InvariantC |
- | 7 | 908 | | }; |
- | 2 | 909 | |
|
- | 7 | 910 | | if(knownTypes.ContainsKey(value.GetType())) |
- | 3 | 911 | | { |
- | 3 | 912 | | return knownTypes[value.GetType()](value); |
- | 2 | 913 | | } |
- | 2 | 914 | | else |
- | 6 | 915 | | { |
- | 6 | 916 | | var formatted = string.Format(CultureInfo.InvariantCulture, format, value); |
- | 6 | 917 | | return new ValueExpression(formatted, formatted); |
- | 2 | 918 | | } |
- | 7 | 919 | | }; |
- | | 920 | |
|
- | | 921 | | private static Entity FetchSnippetByUniqueName(string uniqueName, IOrganizationService service) |
- | 6 | 922 | | { |
- | 6 | 923 | | var fetch = $@"<fetch no-lock=""true""> |
- | 6 | 924 | | <entity name=""oss_xtlsnippet""> |
- | 6 | 925 | | <attribute name=""oss_xtlexpression"" /> |
- | 6 | 926 | | <attribute name=""oss_containsplaintext"" /> |
- | 6 | 927 | | <filter operator=""and""> |
- | 6 | 928 | | <condition attribute=""oss_uniquename"" operator=""eq"" value=""{uniqueName}"" /> |
- | 6 | 929 | | </filter> |
- | 6 | 930 | | </entity> |
- | 6 | 931 | | </fetch>"; |
- | | 932 | |
|
- | 6 | 933 | | var snippet = service.RetrieveMultiple(new FetchExpression(fetch)) |
- | 6 | 934 | | .Entities |
- | 6 | 935 | | .FirstOrDefault(); |
- | | 936 | |
|
- | 6 | 937 | | return snippet; |
- | 6 | 938 | | } |
- | | 939 | |
|
- | | 940 | | private static Entity FetchSnippet(string name, string filter, Entity primary, OrganizationConfig organizationCo |
- | 6 | 941 | | { |
- | 6 | 942 | | var uniqueNameSnippet = FetchSnippetByUniqueName(name, service); |
- | | 943 | |
|
- | 6 | 944 | | if (uniqueNameSnippet != null) |
- | 1 | 945 | | { |
- | 1 | 946 | | tracing.Trace("Found snippet by unique name"); |
- | 1 | 947 | | return uniqueNameSnippet; |
- | | 948 | | } |
- | | 949 | |
|
- | 5 | 950 | | if (!string.IsNullOrEmpty(filter)) |
- | 3 | 951 | | { |
- | 3 | 952 | | tracing.Trace("Processing tokens in custom snippet filter"); |
- | 3 | 953 | | } |
- | | 954 | |
|
- | 5 | 955 | | var fetch = $@"<fetch no-lock=""true""> |
- | 5 | 956 | | <entity name=""oss_xtlsnippet""> |
- | 5 | 957 | | <attribute name=""oss_xtlexpression"" /> |
- | 5 | 958 | | <attribute name=""oss_containsplaintext"" /> |
- | 5 | 959 | | <filter operator=""and""> |
- | 5 | 960 | | <condition attribute=""oss_name"" operator=""eq"" value=""{name}"" /> |
- | 5 | 961 | | { (!string.IsNullOrEmpty(filter) ? TokenMatcher.ProcessTokens(filter, primary, organizationConfi |
- | 5 | 962 | | </filter> |
- | 5 | 963 | | </entity> |
- | 5 | 964 | | </fetch>"; |
- | | 965 | |
|
- | 5 | 966 | | if (!string.IsNullOrEmpty(filter)) |
- | 3 | 967 | | { |
- | 3 | 968 | | tracing.Trace("Done processing tokens in custom snippet filter"); |
- | 3 | 969 | | } |
- | | 970 | |
|
- | 5 | 971 | | var snippet = service.RetrieveMultiple(new FetchExpression(fetch)) |
- | 5 | 972 | | .Entities |
- | 5 | 973 | | .FirstOrDefault(); |
- | | 974 | |
|
- | 5 | 975 | | return snippet; |
- | 6 | 976 | | } |
- | | 977 | |
|
- | 2 | 978 | | public static FunctionHandler Snippet = (primary, service, tracing, organizationConfig, parameters) => |
- | 9 | 979 | | { |
- | 9 | 980 | | if (parameters.Count < 1) |
- | 3 | 981 | | { |
- | 3 | 982 | | throw new InvalidPluginExecutionException("Snippet needs at least a name as first parameter and optional |
- | 2 | 983 | | } |
- | 2 | 984 | |
|
- | 8 | 985 | | var name = CheckedCast<string>(parameters[0].Value, "Name must be a string!"); |
- | 8 | 986 | | var config = GetConfig(parameters); |
- | 2 | 987 | |
|
- | 8 | 988 | | var filter = config?.GetValue<string>("filter", "filter must be a string containing your fetchXml filter, wh |
- | 2 | 989 | |
|
- | 8 | 990 | | var snippet = FetchSnippet(name, filter, primary, organizationConfig, service, tracing); |
- | 2 | 991 | |
|
- | 8 | 992 | | if (snippet == null) |
- | 2 | 993 | | { |
- | 2 | 994 | | tracing.Trace("Failed to find a snippet matching the input"); |
- | 2 | 995 | | return new ValueExpression(string.Empty, null); |
- | 2 | 996 | | } |
- | 2 | 997 | |
|
- | 8 | 998 | | var containsPlainText = snippet.GetAttributeValue<bool>("oss_containsplaintext"); |
- | 8 | 999 | | var value = snippet.GetAttributeValue<string>("oss_xtlexpression"); |
- | 2 | 1000 | |
|
- | 2 | 1001 | | // Wrap it in ${{ ... }} block |
- | 8 | 1002 | | var processedValue = containsPlainText ? value : $"${{{{ {value} }}}}"; |
- | 2 | 1003 | |
|
- | 8 | 1004 | | tracing.Trace("Processing snippet tokens"); |
- | 2 | 1005 | |
|
- | 8 | 1006 | | var result = TokenMatcher.ProcessTokens(processedValue, primary, organizationConfig, service, tracing); |
- | 2 | 1007 | |
|
- | 8 | 1008 | | tracing.Trace("Done processing snippet tokens"); |
- | 2 | 1009 | |
|
- | 8 | 1010 | | return new ValueExpression(result, result); |
- | 8 | 1011 | | }; |
- | | 1012 | |
|
- | 2 | 1013 | | public static FunctionHandler ConvertDateTime = (primary, service, tracing, organizationConfig, parameters) => |
- | 7 | 1014 | | { |
- | 7 | 1015 | | if (parameters.Count < 2) |
- | 2 | 1016 | | { |
- | 2 | 1017 | | throw new InvalidPluginExecutionException("Convert DateTime needs a DateTime and a config for defining f |
- | 2 | 1018 | | } |
- | 2 | 1019 | |
|
- | 7 | 1020 | | var date = CheckedCast<DateTime>(parameters[0].Value, "You need to pass a date"); |
- | 7 | 1021 | | var config = GetConfig(parameters); |
- | 2 | 1022 | |
|
- | 7 | 1023 | | var timeZoneId = config.GetValue<string>("timeZoneId", "timeZoneId must be a string"); |
- | 7 | 1024 | | var userId = config.GetValue<EntityReference>("userId", "userId must be an EntityReference"); |
- | 2 | 1025 | |
|
- | 7 | 1026 | | if (userId == null && string.IsNullOrEmpty(timeZoneId)) |
- | 2 | 1027 | | { |
- | 2 | 1028 | | throw new InvalidPluginExecutionException("You need to either set a userId for converting to a user's co |
- | 2 | 1029 | | } |
- | 2 | 1030 | |
|
- | 7 | 1031 | | if (userId != null) |
- | 5 | 1032 | | { |
- | 5 | 1033 | | var userSettings = service.Retrieve("usersettings", userId.Id, new ColumnSet("timezonecode")); |
- | 5 | 1034 | | var timeZoneCode = userSettings.GetAttributeValue<int>("timezonecode"); |
- | 2 | 1035 | |
|
- | 5 | 1036 | | timeZoneId = service.Query("timezonedefinition") |
- | 5 | 1037 | | .IncludeColumns("standardname") |
- | 8 | 1038 | | .Where(e => e |
- | 11 | 1039 | | .Attribute(a => a |
- | 11 | 1040 | | .Named("timezonecode") |
- | 11 | 1041 | | .Is(ConditionOperator.Equal) |
- | 11 | 1042 | | .To(timeZoneCode) |
- | 8 | 1043 | | ) |
- | 5 | 1044 | | ) |
- | 5 | 1045 | | .Retrieve() |
- | 5 | 1046 | | .FirstOrDefault() |
- | 5 | 1047 | | ?.GetAttributeValue<string>("standardname"); |
- | 5 | 1048 | | } |
- | 2 | 1049 | |
|
- | 7 | 1050 | | if (string.IsNullOrEmpty(timeZoneId)) |
- | 2 | 1051 | | { |
- | 2 | 1052 | | throw new InvalidPluginExecutionException("Failed to retrieve timeZoneId, can't convert datetime"); |
- | 2 | 1053 | | } |
- | 2 | 1054 | |
|
- | 7 | 1055 | | var timeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId); |
- | 7 | 1056 | | var localTime = TimeZoneInfo.ConvertTime(date, timeZone); |
- | 7 | 1057 | | var text = localTime.ToString(config.GetValue<string>("format", "format must be a string", "g"), CultureInfo |
- | 2 | 1058 | |
|
- | 7 | 1059 | | return new ValueExpression(text, localTime); |
- | 7 | 1060 | | }; |
- | | 1061 | | } |
- | | 1062 | | } |
- | | 1063 | | #pragma warning restore S1104 // Fields should not have public accessibility |
+ | 156 | 817 | | if (!(value is T)) |
+ | 6 | 818 | | { |
+ | 6 | 819 | | if (failOnError) |
+ | 1 | 820 | | { |
+ | 1 | 821 | | throw new InvalidPluginExecutionException(errorMessage); |
+ | | 822 | | } |
+ | | 823 | |
|
+ | 5 | 824 | | return default(T); |
+ | | 825 | | } |
+ | | 826 | |
|
+ | 150 | 827 | | return (T)value; |
+ | 155 | 828 | | } |
+ | | 829 | |
|
+ | 2 | 830 | | public static FunctionHandler Substring = (primary, service, tracing, organizationConfig, parameters) => |
+ | 11 | 831 | | { |
+ | 11 | 832 | | if (parameters.Count < 2) |
+ | 3 | 833 | | { |
+ | 3 | 834 | | throw new InvalidPluginExecutionException("Substring expects at least two parameters: text, start index |
+ | 2 | 835 | | } |
+ | 2 | 836 | |
|
+ | 10 | 837 | | var text = parameters[0].Text; |
+ | 10 | 838 | | var startIndex = CheckedCast<int>(parameters[1].Value, "Start index parameter must be an int!"); |
+ | 9 | 839 | | var length = -1; |
+ | 2 | 840 | |
|
+ | 9 | 841 | | if (parameters.Count > 2) |
+ | 8 | 842 | | { |
+ | 8 | 843 | | length = CheckedCast<int>(parameters[2].Value, "Length parameter must be an int!"); |
+ | 8 | 844 | | } |
+ | 2 | 845 | |
|
+ | 9 | 846 | | var subString = length > -1 ? text.Substring(startIndex, length) : text.Substring(startIndex); |
+ | 9 | 847 | | return new ValueExpression(subString, subString); |
+ | 9 | 848 | | }; |
+ | | 849 | |
|
+ | 2 | 850 | | public static FunctionHandler IndexOf = (primary, service, tracing, organizationConfig, parameters) => |
+ | 4 | 851 | | { |
+ | 4 | 852 | | if (parameters.Count < 2) |
+ | 3 | 853 | | { |
+ | 3 | 854 | | throw new InvalidPluginExecutionException("IndexOf needs a source string and a string to search for"); |
+ | 2 | 855 | | } |
+ | 2 | 856 | |
|
+ | 3 | 857 | | var value = CheckedCast<string>(parameters[0].Value, "Source must be a string"); |
+ | 3 | 858 | | var searchText = CheckedCast<string>(parameters[1].Value, "Search text must be a string"); |
+ | 2 | 859 | |
|
+ | 3 | 860 | | var config = GetConfig(parameters); |
+ | 3 | 861 | | var ignoreCase = config.GetValue<bool>("ignoreCase", "ignoreCase must be a boolean!"); |
+ | 2 | 862 | |
|
+ | 3 | 863 | | var index = value.IndexOf(searchText, ignoreCase ? StringComparison.InvariantCultureIgnoreCase : StringCompa |
+ | 2 | 864 | |
|
+ | 3 | 865 | | return new ValueExpression(index.ToString(), index); |
+ | 3 | 866 | | }; |
+ | | 867 | |
|
+ | 2 | 868 | | public static FunctionHandler Replace = (primary, service, tracing, organizationConfig, parameters) => |
+ | 4 | 869 | | { |
+ | 4 | 870 | | if (parameters.Count < 3) |
+ | 3 | 871 | | { |
+ | 3 | 872 | | throw new InvalidPluginExecutionException("Replace expects three parameters: text input, regex pattern, |
+ | 2 | 873 | | } |
+ | 2 | 874 | |
|
+ | 3 | 875 | | var input = parameters[0].Text; |
+ | 3 | 876 | | var pattern = parameters[1].Text; |
+ | 3 | 877 | | var replacement = parameters[2].Text; |
+ | 2 | 878 | |
|
+ | 3 | 879 | | var replaced = Regex.Replace(input, pattern, replacement); |
+ | 2 | 880 | |
|
+ | 3 | 881 | | return new ValueExpression(replaced, replaced); |
+ | 3 | 882 | | }; |
+ | | 883 | |
|
+ | 2 | 884 | | public static FunctionHandler Array = (primary, service, tracing, organizationConfig, parameters) => |
+ | 34 | 885 | | { |
+ | 95 | 886 | | return new ValueExpression(string.Join(", ", parameters.Select(p => p.Text)), parameters); |
+ | 34 | 887 | | }; |
+ | | 888 | |
|
+ | 2 | 889 | | public static FunctionHandler DateTimeNow = (primary, service, tracing, organizationConfig, parameters) => |
+ | 3 | 890 | | { |
+ | 3 | 891 | | var date = DateTime.Now; |
+ | 3 | 892 | | return new ValueExpression(date.ToString("o", CultureInfo.InvariantCulture), date); |
+ | 3 | 893 | | }; |
+ | | 894 | |
|
+ | 2 | 895 | | public static FunctionHandler DateTimeUtcNow = (primary, service, tracing, organizationConfig, parameters) => |
+ | 5 | 896 | | { |
+ | 5 | 897 | | var date = DateTime.UtcNow; |
+ | 5 | 898 | | return new ValueExpression(date.ToString("o", CultureInfo.InvariantCulture), date); |
+ | 5 | 899 | | }; |
+ | | 900 | |
|
+ | 2 | 901 | | public static FunctionHandler Static = (primary, service, tracing, organizationConfig, parameters) => |
+ | 8 | 902 | | { |
+ | 8 | 903 | | if (parameters.Count < 1) |
+ | 2 | 904 | | { |
+ | 2 | 905 | | throw new InvalidOperationException("You have to pass a static value"); |
+ | 2 | 906 | | } |
+ | 2 | 907 | |
|
+ | 8 | 908 | | var parameter = parameters[0]; |
+ | 8 | 909 | | return new ValueExpression(parameter.Text, parameter.Value); |
+ | 8 | 910 | | }; |
+ | | 911 | |
|
+ | 2 | 912 | | public static FunctionHandler DateToString = (primary, service, tracing, organizationConfig, parameters) => |
+ | 5 | 913 | | { |
+ | 5 | 914 | | if (parameters.Count < 1) |
+ | 2 | 915 | | { |
+ | 2 | 916 | | throw new Exception("No date to stringify"); |
+ | 2 | 917 | | } |
+ | 2 | 918 | |
|
+ | 5 | 919 | | var date = CheckedCast<DateTime>(parameters[0].Value, "You need to pass a date"); |
+ | 5 | 920 | | var config = GetConfig(parameters); |
+ | 5 | 921 | | var format = config.GetValue<string>("format", "format must be a string!"); |
+ | 2 | 922 | |
|
+ | 5 | 923 | | if (!string.IsNullOrEmpty(format)) |
+ | 5 | 924 | | { |
+ | 5 | 925 | | return new ValueExpression(date.ToString(format), date.ToString(format)); |
+ | 2 | 926 | | } |
+ | 2 | 927 | |
|
+ | 2 | 928 | | return new ValueExpression(date.ToString(CultureInfo.InvariantCulture), date.ToString(CultureInfo.InvariantC |
+ | 5 | 929 | | }; |
+ | | 930 | |
|
+ | 2 | 931 | | public static FunctionHandler Format = (primary, service, tracing, organizationConfig, parameters) => |
+ | 8 | 932 | | { |
+ | 8 | 933 | | if (parameters.Count < 2) |
+ | 3 | 934 | | { |
+ | 3 | 935 | | throw new InvalidPluginExecutionException("Format needs a value to format and a config for defining furt |
+ | 2 | 936 | | } |
+ | 2 | 937 | |
|
+ | 7 | 938 | | var value = parameters[0].Value; |
+ | 7 | 939 | | var config = GetConfig(parameters); |
+ | 7 | 940 | | var format = config.GetValue<string>("format", "format must be a string!"); |
+ | 2 | 941 | |
|
+ | 7 | 942 | | var knownTypes = new Dictionary<Type, Func<object, ValueExpression>> |
+ | 7 | 943 | | { |
+ | 12 | 944 | | { typeof(Money), (obj) => { var val = obj as Money; var formatted = string.Format(CultureInfo.InvariantC |
+ | 7 | 945 | | }; |
+ | 2 | 946 | |
|
+ | 7 | 947 | | if(knownTypes.ContainsKey(value.GetType())) |
+ | 3 | 948 | | { |
+ | 3 | 949 | | return knownTypes[value.GetType()](value); |
+ | 2 | 950 | | } |
+ | 2 | 951 | | else |
+ | 6 | 952 | | { |
+ | 6 | 953 | | var formatted = string.Format(CultureInfo.InvariantCulture, format, value); |
+ | 6 | 954 | | return new ValueExpression(formatted, formatted); |
+ | 2 | 955 | | } |
+ | 7 | 956 | | }; |
+ | | 957 | |
|
+ | | 958 | | private static Entity FetchSnippetByUniqueName(string uniqueName, IOrganizationService service) |
+ | 6 | 959 | | { |
+ | 6 | 960 | | var fetch = $@"<fetch no-lock=""true""> |
+ | 6 | 961 | | <entity name=""oss_xtlsnippet""> |
+ | 6 | 962 | | <attribute name=""oss_xtlexpression"" /> |
+ | 6 | 963 | | <attribute name=""oss_containsplaintext"" /> |
+ | 6 | 964 | | <filter operator=""and""> |
+ | 6 | 965 | | <condition attribute=""oss_uniquename"" operator=""eq"" value=""{uniqueName}"" /> |
+ | 6 | 966 | | </filter> |
+ | 6 | 967 | | </entity> |
+ | 6 | 968 | | </fetch>"; |
+ | | 969 | |
|
+ | 6 | 970 | | var snippet = service.RetrieveMultiple(new FetchExpression(fetch)) |
+ | 6 | 971 | | .Entities |
+ | 6 | 972 | | .FirstOrDefault(); |
+ | | 973 | |
|
+ | 6 | 974 | | return snippet; |
+ | 6 | 975 | | } |
+ | | 976 | |
|
+ | | 977 | | private static Entity FetchSnippet(string name, string filter, Entity primary, OrganizationConfig organizationCo |
+ | 6 | 978 | | { |
+ | 6 | 979 | | var uniqueNameSnippet = FetchSnippetByUniqueName(name, service); |
+ | | 980 | |
|
+ | 6 | 981 | | if (uniqueNameSnippet != null) |
+ | 1 | 982 | | { |
+ | 1 | 983 | | tracing.Trace("Found snippet by unique name"); |
+ | 1 | 984 | | return uniqueNameSnippet; |
+ | | 985 | | } |
+ | | 986 | |
|
+ | 5 | 987 | | if (!string.IsNullOrEmpty(filter)) |
+ | 3 | 988 | | { |
+ | 3 | 989 | | tracing.Trace("Processing tokens in custom snippet filter"); |
+ | 3 | 990 | | } |
+ | | 991 | |
|
+ | 5 | 992 | | var fetch = $@"<fetch no-lock=""true""> |
+ | 5 | 993 | | <entity name=""oss_xtlsnippet""> |
+ | 5 | 994 | | <attribute name=""oss_xtlexpression"" /> |
+ | 5 | 995 | | <attribute name=""oss_containsplaintext"" /> |
+ | 5 | 996 | | <filter operator=""and""> |
+ | 5 | 997 | | <condition attribute=""oss_name"" operator=""eq"" value=""{name}"" /> |
+ | 5 | 998 | | { (!string.IsNullOrEmpty(filter) ? TokenMatcher.ProcessTokens(filter, primary, organizationConfi |
+ | 5 | 999 | | </filter> |
+ | 5 | 1000 | | </entity> |
+ | 5 | 1001 | | </fetch>"; |
+ | | 1002 | |
|
+ | 5 | 1003 | | if (!string.IsNullOrEmpty(filter)) |
+ | 3 | 1004 | | { |
+ | 3 | 1005 | | tracing.Trace("Done processing tokens in custom snippet filter"); |
+ | 3 | 1006 | | } |
+ | | 1007 | |
|
+ | 5 | 1008 | | var snippet = service.RetrieveMultiple(new FetchExpression(fetch)) |
+ | 5 | 1009 | | .Entities |
+ | 5 | 1010 | | .FirstOrDefault(); |
+ | | 1011 | |
|
+ | 5 | 1012 | | return snippet; |
+ | 6 | 1013 | | } |
+ | | 1014 | |
|
+ | 2 | 1015 | | public static FunctionHandler Snippet = (primary, service, tracing, organizationConfig, parameters) => |
+ | 9 | 1016 | | { |
+ | 9 | 1017 | | if (parameters.Count < 1) |
+ | 3 | 1018 | | { |
+ | 3 | 1019 | | throw new InvalidPluginExecutionException("Snippet needs at least a name as first parameter and optional |
+ | 2 | 1020 | | } |
+ | 2 | 1021 | |
|
+ | 8 | 1022 | | var name = CheckedCast<string>(parameters[0].Value, "Name must be a string!"); |
+ | 8 | 1023 | | var config = GetConfig(parameters); |
+ | 2 | 1024 | |
|
+ | 8 | 1025 | | var filter = config?.GetValue<string>("filter", "filter must be a string containing your fetchXml filter, wh |
+ | 2 | 1026 | |
|
+ | 8 | 1027 | | var snippet = FetchSnippet(name, filter, primary, organizationConfig, service, tracing); |
+ | 2 | 1028 | |
|
+ | 8 | 1029 | | if (snippet == null) |
+ | 2 | 1030 | | { |
+ | 2 | 1031 | | tracing.Trace("Failed to find a snippet matching the input"); |
+ | 2 | 1032 | | return new ValueExpression(string.Empty, null); |
+ | 2 | 1033 | | } |
+ | 2 | 1034 | |
|
+ | 8 | 1035 | | var containsPlainText = snippet.GetAttributeValue<bool>("oss_containsplaintext"); |
+ | 8 | 1036 | | var value = snippet.GetAttributeValue<string>("oss_xtlexpression"); |
+ | 2 | 1037 | |
|
+ | 2 | 1038 | | // Wrap it in ${{ ... }} block |
+ | 8 | 1039 | | var processedValue = containsPlainText ? value : $"${{{{ {value} }}}}"; |
+ | 2 | 1040 | |
|
+ | 8 | 1041 | | tracing.Trace("Processing snippet tokens"); |
+ | 2 | 1042 | |
|
+ | 8 | 1043 | | var result = TokenMatcher.ProcessTokens(processedValue, primary, organizationConfig, service, tracing); |
+ | 2 | 1044 | |
|
+ | 8 | 1045 | | tracing.Trace("Done processing snippet tokens"); |
+ | 2 | 1046 | |
|
+ | 8 | 1047 | | return new ValueExpression(result, result); |
+ | 8 | 1048 | | }; |
+ | | 1049 | |
|
+ | 2 | 1050 | | public static FunctionHandler ConvertDateTime = (primary, service, tracing, organizationConfig, parameters) => |
+ | 7 | 1051 | | { |
+ | 7 | 1052 | | if (parameters.Count < 2) |
+ | 2 | 1053 | | { |
+ | 2 | 1054 | | throw new InvalidPluginExecutionException("Convert DateTime needs a DateTime and a config for defining f |
+ | 2 | 1055 | | } |
+ | 2 | 1056 | |
|
+ | 7 | 1057 | | var date = CheckedCast<DateTime>(parameters[0].Value, "You need to pass a date"); |
+ | 7 | 1058 | | var config = GetConfig(parameters); |
+ | 2 | 1059 | |
|
+ | 7 | 1060 | | var timeZoneId = config.GetValue<string>("timeZoneId", "timeZoneId must be a string"); |
+ | 7 | 1061 | | var userId = config.GetValue<EntityReference>("userId", "userId must be an EntityReference"); |
+ | 2 | 1062 | |
|
+ | 7 | 1063 | | if (userId == null && string.IsNullOrEmpty(timeZoneId)) |
+ | 2 | 1064 | | { |
+ | 2 | 1065 | | throw new InvalidPluginExecutionException("You need to either set a userId for converting to a user's co |
+ | 2 | 1066 | | } |
+ | 2 | 1067 | |
|
+ | 7 | 1068 | | if (userId != null) |
+ | 5 | 1069 | | { |
+ | 5 | 1070 | | var userSettings = service.Retrieve("usersettings", userId.Id, new ColumnSet("timezonecode")); |
+ | 5 | 1071 | | var timeZoneCode = userSettings.GetAttributeValue<int>("timezonecode"); |
+ | 2 | 1072 | |
|
+ | 5 | 1073 | | timeZoneId = service.Query("timezonedefinition") |
+ | 5 | 1074 | | .IncludeColumns("standardname") |
+ | 8 | 1075 | | .Where(e => e |
+ | 11 | 1076 | | .Attribute(a => a |
+ | 11 | 1077 | | .Named("timezonecode") |
+ | 11 | 1078 | | .Is(ConditionOperator.Equal) |
+ | 11 | 1079 | | .To(timeZoneCode) |
+ | 8 | 1080 | | ) |
+ | 5 | 1081 | | ) |
+ | 5 | 1082 | | .Retrieve() |
+ | 5 | 1083 | | .FirstOrDefault() |
+ | 5 | 1084 | | ?.GetAttributeValue<string>("standardname"); |
+ | 5 | 1085 | | } |
+ | 2 | 1086 | |
|
+ | 7 | 1087 | | if (string.IsNullOrEmpty(timeZoneId)) |
+ | 2 | 1088 | | { |
+ | 2 | 1089 | | throw new InvalidPluginExecutionException("Failed to retrieve timeZoneId, can't convert datetime"); |
+ | 2 | 1090 | | } |
+ | 2 | 1091 | |
|
+ | 7 | 1092 | | var timeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId); |
+ | 7 | 1093 | | var localTime = TimeZoneInfo.ConvertTime(date, timeZone); |
+ | 7 | 1094 | | var text = localTime.ToString(config.GetValue<string>("format", "format must be a string", "g"), CultureInfo |
+ | 2 | 1095 | |
|
+ | 7 | 1096 | | return new ValueExpression(text, localTime); |
+ | 7 | 1097 | | }; |
+ | | 1098 | |
|
+ | 2 | 1099 | | public static FunctionHandler RetrieveAudit = (primary, service, tracing, organizationConfig, parameters) => |
+ | 3 | 1100 | | { |
+ | 3 | 1101 | | var firstParam = parameters.FirstOrDefault()?.Value; |
+ | 3 | 1102 | | var reference = (firstParam as Entity)?.ToEntityReference() ?? firstParam as EntityReference; |
+ | 3 | 1103 | | var config = GetConfig(parameters); |
+ | 2 | 1104 | |
|
+ | 3 | 1105 | | if (firstParam != null && reference == null) |
+ | 2 | 1106 | | { |
+ | 2 | 1107 | | throw new InvalidPluginExecutionException("RetrieveAudit: First Parameter must be an Entity or EntityRef |
+ | 2 | 1108 | | } |
+ | 2 | 1109 | |
|
+ | 3 | 1110 | | if (reference == null) |
+ | 2 | 1111 | | { |
+ | 2 | 1112 | | return new ValueExpression(string.Empty, null); |
+ | 2 | 1113 | | } |
+ | 2 | 1114 | |
|
+ | 3 | 1115 | | var field = CheckedCast<string>(parameters[1]?.Value, "RetrieveAudit: fieldName must be a string"); |
+ | 2 | 1116 | |
|
+ | 3 | 1117 | | var request = new RetrieveRecordChangeHistoryRequest |
+ | 3 | 1118 | | { |
+ | 3 | 1119 | | Target = reference |
+ | 3 | 1120 | | }; |
+ | 3 | 1121 | | var audit = service.Execute(request) as RetrieveRecordChangeHistoryResponse; |
+ | 2 | 1122 | |
|
+ | 3 | 1123 | | var auditValue = audit.AuditDetailCollection.AuditDetails.Select(d => |
+ | 4 | 1124 | | { |
+ | 4 | 1125 | | var detail = d as AttributeAuditDetail; |
+ | 3 | 1126 | |
|
+ | 4 | 1127 | | if (detail == null) |
+ | 3 | 1128 | | { |
+ | 3 | 1129 | | return null; |
+ | 3 | 1130 | | } |
+ | 3 | 1131 | |
|
+ | 4 | 1132 | | var oldValue = detail.OldValue.GetAttributeValue<object>(field); |
+ | 3 | 1133 | |
|
+ | 4 | 1134 | | return Tuple.Create(PropertyStringifier.Stringify(field, detail.OldValue, service, config), oldValue); |
+ | 4 | 1135 | | }) |
+ | 4 | 1136 | | .FirstOrDefault(t => t != null); |
+ | 2 | 1137 | |
|
+ | 3 | 1138 | | return new ValueExpression(auditValue?.Item1 ?? string.Empty, auditValue?.Item2); |
+ | 3 | 1139 | | }; |
+ | | 1140 | |
|
+ | 2 | 1141 | | public static FunctionHandler GetRecordId = (primary, service, tracing, organizationConfig, parameters) => |
+ | 9 | 1142 | | { |
+ | 9 | 1143 | | var firstParam = parameters.FirstOrDefault()?.Value; |
+ | 9 | 1144 | | var reference = (firstParam as Entity)?.ToEntityReference() ?? firstParam as EntityReference; |
+ | 9 | 1145 | | var config = GetConfig(parameters); |
+ | 2 | 1146 | |
|
+ | 9 | 1147 | | if (firstParam != null && reference == null) |
+ | 2 | 1148 | | { |
+ | 2 | 1149 | | throw new InvalidPluginExecutionException("RecordId: First Parameter must be an Entity or EntityReferenc |
+ | 2 | 1150 | | } |
+ | 2 | 1151 | |
|
+ | 9 | 1152 | | if (reference == null) |
+ | 2 | 1153 | | { |
+ | 2 | 1154 | | return new ValueExpression(string.Empty, null); |
+ | 2 | 1155 | | } |
+ | 2 | 1156 | |
|
+ | 9 | 1157 | | var textValue = reference.Id.ToString(config.GetValue<string>("format", "format must be a string", "D")); |
+ | 2 | 1158 | |
|
+ | 9 | 1159 | | return new ValueExpression(textValue, reference.Id); |
+ | 9 | 1160 | | }; |
+ | | 1161 | |
|
+ | 2 | 1162 | | public static FunctionHandler GetRecordLogicalName = (primary, service, tracing, organizationConfig, parameters) |
+ | 4 | 1163 | | { |
+ | 4 | 1164 | | var firstParam = parameters.FirstOrDefault()?.Value; |
+ | 4 | 1165 | | var reference = (firstParam as Entity)?.ToEntityReference() ?? firstParam as EntityReference; |
+ | 2 | 1166 | |
|
+ | 4 | 1167 | | if (firstParam != null && reference == null) |
+ | 2 | 1168 | | { |
+ | 2 | 1169 | | throw new InvalidPluginExecutionException("RecordLogicalName: First Parameter must be an Entity or Entit |
+ | 2 | 1170 | | } |
+ | 2 | 1171 | |
|
+ | 4 | 1172 | | if (reference == null) |
+ | 2 | 1173 | | { |
+ | 2 | 1174 | | return new ValueExpression(string.Empty, null); |
+ | 2 | 1175 | | } |
+ | 2 | 1176 | |
|
+ | 4 | 1177 | | return new ValueExpression(reference.LogicalName, reference.LogicalName); |
+ | 4 | 1178 | | }; |
+ | | 1179 | | } |
+ | | 1180 | | } |
+ | | 1181 | | #pragma warning restore S1104 // Fields should not have public accessibility |
-