// Used on reading from source. Get full list of rules for this control. public Dictionary <string, string> GetDefaultRules() { // Add themes first. var defaults = new Dictionary <string, string>(); var variantDefaults = new Dictionary <string, string>(); if (_template != null) { // Default values from the variants take precedence over the base template var hasVariantDefaults = _variantName != null && _template.VariantDefaultValues.TryGetValue(_variantName, out variantDefaults); if (hasVariantDefaults) { defaults.AddRange(variantDefaults); } defaults.AddRange(_template.InputDefaults.Where(kvp => !ControlTemplateParser.IsLocalizationKey(kvp.Value) && !(hasVariantDefaults && variantDefaults.ContainsKey(kvp.Key)))); } defaults.AddRange(_theme.GetStyle(_styleName).Where(kvp => !ControlTemplateParser.IsLocalizationKey(kvp.Value))); if (_inResponsiveContext) { defaults.AddRange(DynamicProperties.GetDefaultValues(_templateName, this)); } return(defaults); }
private static void LoadTemplateFiles(ErrorContainer errors, CanvasDocument app, string packagesPath, out Dictionary <string, ControlTemplate> loadedTemplates) { loadedTemplates = new Dictionary <string, ControlTemplate>(); var templateList = new List <TemplatesJson.TemplateJson>(); foreach (var file in new DirectoryReader(packagesPath).EnumerateFiles(string.Empty, "*.xml", searchSubdirectories: false)) { var xmlContents = file.GetContents(); if (!ControlTemplateParser.TryParseTemplate(new TemplateStore(), xmlContents, app._properties.DocumentAppType, loadedTemplates, out var parsedTemplate, out var templateName)) { errors.GenericError($"Unable to parse template file {file._relativeName}"); throw new DocumentException(); } // Some control templates specify a name with an initial capital letter (e.g. rating control) // However, the server doesn't always use that. If the template name doesn't match the one we wrote // as the file name, adjust the template name to lowercase if (!file._relativeName.StartsWith(templateName)) { templateName = templateName.ToLower(); } templateList.Add(new TemplatesJson.TemplateJson() { Name = templateName, Template = xmlContents, Version = parsedTemplate.Version }); } // Also add Screen and App templates (not xml, constructed in code on the server) GlobalTemplates.AddCodeOnlyTemplates(new TemplateStore(), loadedTemplates, app._properties.DocumentAppType); app._templates = new TemplatesJson() { UsedTemplates = templateList.ToArray() }; }
public void TestGalleryNestedTemplateParse() { var galleryTemplatePath = Path.Combine(Environment.CurrentDirectory, "Templates", "gallery_2.10.0.xml"); Assert.IsTrue(File.Exists(galleryTemplatePath)); using var galleryTemplateStream = File.OpenRead(galleryTemplatePath); using var galleryTemplateReader = new StreamReader(galleryTemplateStream); var galleryTemplateContents = galleryTemplateReader.ReadToEnd(); var parsedTemplates = new Dictionary <string, ControlTemplate>(); var templateStore = new TemplateStore(); Assert.IsTrue(ControlTemplateParser.TryParseTemplate(templateStore, galleryTemplateContents, AppType.DesktopOrTablet, parsedTemplates, out var topTemplate, out var name)); Assert.AreEqual(2, parsedTemplates.Count); Assert.AreEqual("gallery", name); Assert.AreEqual("http://microsoft.com/appmagic/gallery", topTemplate.Id); Assert.IsTrue(templateStore.TryGetTemplate("gallery", out _)); Assert.IsTrue(parsedTemplates.TryGetValue("galleryTemplate", out var innerTemplate)); Assert.AreEqual("http://microsoft.com/appmagic/galleryTemplate", innerTemplate.Id); Assert.AreEqual("RGBA(0, 0, 0, 0)", innerTemplate.InputDefaults["TemplateFill"]); Assert.IsTrue(templateStore.TryGetTemplate("galleryTemplate", out _)); }
internal void ApplyAfterMsAppLoadTransforms(ErrorContainer errors) { // Update volatile documentproperties _entropy.SetProperties(_properties); // Shard templates, parse for default values var templateDefaults = new Dictionary <string, ControlTemplate>(); foreach (var template in _templates.UsedTemplates) { if (!ControlTemplateParser.TryParseTemplate(_templateStore, template.Template, _properties.DocumentAppType, templateDefaults, out _, out _)) { errors.GenericError($"Unable to parse template file {template.Name}"); throw new DocumentException(); } } // Also add Screen and App templates (not xml, constructed in code on the server) GlobalTemplates.AddCodeOnlyTemplates(_templateStore, templateDefaults, _properties.DocumentAppType); // PCF templates if (_pcfControls.Count == 0) { foreach (var kvp in _templateStore.Contents) { if (kvp.Value.IsPcfControl && kvp.Value.DynamicControlDefinitionJson != null) { _pcfControls.Add(kvp.Key, PcfControl.GetPowerAppsControlFromJson(kvp.Value)); kvp.Value.DynamicControlDefinitionJson = null; } } } var componentInstanceTransform = new ComponentInstanceTransform(errors); var componentDefTransform = new ComponentDefinitionTransform(errors, _templateStore, componentInstanceTransform); // Transform component definitions and populate template set of component instances that need updates foreach (var ctrl in _components) { AddComponentDefaults(ctrl.Value, templateDefaults); componentDefTransform.AfterRead(ctrl.Value); } var transformer = new SourceTransformer(this, errors, templateDefaults, new Theme(_themes), componentInstanceTransform, _editorStateStore, _templateStore, _entropy); foreach (var ctrl in _screens.Concat(_components)) { transformer.ApplyAfterRead(ctrl.Value); } StabilizeAssetFilePaths(errors); // Persist the original order of resource entries in Resources.json in the entropy. this.PersistOrderingOfResourcesJsonEntries(); }
internal void ApplyBeforeMsAppWriteTransforms(ErrorContainer errors) { // Update volatile documentproperties _entropy.GetProperties(_properties); // Shard templates, parse for default values var templateDefaults = new Dictionary <string, ControlTemplate>(); foreach (var template in _templates.UsedTemplates) { if (!ControlTemplateParser.TryParseTemplate(_templateStore, template.Template, _properties.DocumentAppType, templateDefaults, out _, out _)) { errors.GenericError($"Unable to parse template file {template.Name}"); throw new DocumentException(); } } // Also add Screen and App templates (not xml, constructed in code on the server) GlobalTemplates.AddCodeOnlyTemplates(_templateStore, templateDefaults, _properties.DocumentAppType); // Generate DynamicControlDefinitionJson for power apps controls foreach (var kvp in _pcfControls) { if (_templateStore.TryGetTemplate(kvp.Key, out var template)) { template.DynamicControlDefinitionJson = PcfControl.GenerateDynamicControlDefinition(kvp.Value); } else { // Validation for accidental deletion of ocf control templates. errors.ValidationError($"Could not find Pcf Control Template with name: {kvp.Key} in pkgs/PcfControlTemplates directory. " + $"If it was intentionally deleted, please delete the entry from ControlTemplates.json along with its references from source files."); } } var componentInstanceTransform = new ComponentInstanceTransform(errors); var componentDefTransform = new ComponentDefinitionTransform(errors, _templateStore, componentInstanceTransform); // Transform component definitions and populate template set of component instances that need updates foreach (var ctrl in _components) { componentDefTransform.BeforeWrite(ctrl.Value); AddComponentDefaults(ctrl.Value, templateDefaults); } var transformer = new SourceTransformer(this, errors, templateDefaults, new Theme(_themes), componentInstanceTransform, _editorStateStore, _templateStore, _entropy); foreach (var ctrl in _screens.Concat(_components)) { transformer.ApplyBeforeWrite(ctrl.Value); } RestoreAssetFilePaths(); }
// To Load the fluidGrid template defaults private Dictionary <string, ControlTemplate> getTemplateStore() { var parsedTemplates = new Dictionary <string, ControlTemplate>(); var fluidGridTemplatePath = Path.Combine(Environment.CurrentDirectory, "Templates", "fluidGrid_2.2.0.xml"); Assert.IsTrue(File.Exists(fluidGridTemplatePath)); using var fluidGridTemplateStream = File.OpenRead(fluidGridTemplatePath); using var fluidGridTemplateReader = new StreamReader(fluidGridTemplateStream); var templateStore = new TemplateStore(); var fluidGridTemplateContents = fluidGridTemplateReader.ReadToEnd(); ControlTemplateParser.TryParseTemplate(templateStore, fluidGridTemplateContents, AppType.DesktopOrTablet, parsedTemplates, out var topTemplate, out var name); return(parsedTemplates); }
public Theme(ThemesJson themeJson) { Contract.Assert(themeJson != null); foreach (var theme in themeJson.CustomThemes) { Dictionary <string, string> palleteRules = new Dictionary <string, string>(); foreach (var item in theme.palette) { palleteRules[item.name] = item.value; } foreach (var style in theme.styles) { var styleName = style.name; foreach (var prop in style.propertyValuesMap) { var propName = prop.property; var ruleValue = prop.value; // TODO - share with logic in D:\dev\pa2\PowerApps-Client\src\Cloud\DocumentServer.Core\Document\Document\Theme\ControlStyle.cs // Resolve %%, from palette. { var match = Regex.Match(ruleValue, "%Palette.([^%]+)%"); if (match.Success) { var group = match.Groups[1]; string resourceValue; // Template may refer to a missing rule. if (palleteRules.TryGetValue(group.ToString(), out resourceValue)) { ruleValue = ruleValue.Replace(match.Value, resourceValue); } } } ruleValue = ControlTemplateParser.UnescapeReservedName(ruleValue); _styles.GetOrCreate(styleName).Add(propName, ruleValue); } } } }
internal void ApplyBeforeMsAppWriteTransforms(ErrorContainer errors) { // Update volatile documentproperties _entropy.GetProperties(_properties); // Shard templates, parse for default values var templateDefaults = new Dictionary <string, ControlTemplate>(); foreach (var template in _templates.UsedTemplates) { if (!ControlTemplateParser.TryParseTemplate(_templateStore, template.Template, _properties.DocumentAppType, templateDefaults, out _, out _)) { errors.GenericError($"Unable to parse template file {template.Name}"); throw new DocumentException(); } } // Also add Screen and App templates (not xml, constructed in code on the server) GlobalTemplates.AddCodeOnlyTemplates(_templateStore, templateDefaults, _properties.DocumentAppType); var componentInstanceTransform = new ComponentInstanceTransform(errors); var componentDefTransform = new ComponentDefinitionTransform(errors, _templateStore, componentInstanceTransform); // Transform component definitions and populate template set of component instances that need updates foreach (var ctrl in _components) { componentDefTransform.BeforeWrite(ctrl.Value); AddComponentDefaults(ctrl.Value, templateDefaults); } var transformer = new SourceTransformer(errors, templateDefaults, new Theme(_themes), componentInstanceTransform, _editorStateStore, _templateStore, _entropy); foreach (var ctrl in _screens.Concat(_components)) { transformer.ApplyBeforeWrite(ctrl.Value); } RestoreAssetFilePaths(); }
// Used on writing to source to omit default rules. public bool TryGetDefaultRule(string propertyName, out string defaultScript) { // Themes (styles) are higher precedence then Template XML. var template = _template; if (_theme.TryLookup(_styleName, propertyName, out defaultScript)) { if (ControlTemplateParser.IsLocalizationKey(defaultScript)) { return(false); } return(true); } // Check template variant first, then template base if (template != null && ((_variantName != null && template.VariantDefaultValues.TryGetValue(_variantName, out var defaults) && defaults.TryGetValue(propertyName, out defaultScript)) || template.InputDefaults.TryGetValue(propertyName, out defaultScript))) { if (ControlTemplateParser.IsLocalizationKey(defaultScript)) { return(false); } // Found in template. return(true); } if (_inResponsiveContext && DynamicProperties.TryGetDefaultValue(propertyName, _templateName, this, out defaultScript)) { return(true); } defaultScript = null; return(false); }
// Write out to a directory (this shards it) public static void SaveAsSource(CanvasDocument app, string directory2, ErrorContainer errors) { var dir = new DirectoryWriter(directory2); dir.DeleteAllSubdirs(); // Shard templates, parse for default values var templateDefaults = new Dictionary <string, ControlTemplate>(); foreach (var template in app._templates.UsedTemplates) { var filename = $"{template.Name}_{template.Version}.xml"; dir.WriteAllXML(PackagesDir, filename, template.Template); if (!ControlTemplateParser.TryParseTemplate(app._templateStore, template.Template, app._properties.DocumentAppType, templateDefaults, out _, out _)) { throw new NotSupportedException($"Unable to parse template file {template.Name}"); } } // Also add Screen and App templates (not xml, constructed in code on the server) GlobalTemplates.AddCodeOnlyTemplates(app._templateStore, templateDefaults, app._properties.DocumentAppType); var importedComponents = app.GetImportedComponents(); foreach (var control in app._screens) { string controlName = control.Key; var isTest = controlName == AppTestControlName; var subDir = isTest ? TestDir : CodeDir; WriteTopParent(dir, app, control.Key, control.Value, subDir); } foreach (var control in app._components) { string controlName = control.Key; app._templateStore.TryGetTemplate(controlName, out var templateState); bool isImported = importedComponents.Contains(templateState.TemplateOriginalName); var subDir = (isImported) ? ComponentPackageDir : ComponentCodeDir; WriteTopParent(dir, app, control.Key, control.Value, subDir); } // Write out control templates at top level, skipping component templates which are written alongside components var nonComponentControlTemplates = app._templateStore.Contents.Where(kvp => !(kvp.Value.IsComponentTemplate ?? false)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); dir.WriteAllJson("", "ControlTemplates.json", nonComponentControlTemplates); if (app._checksum != null) { app._checksum.ClientBuildDetails = _buildVerJson; dir.WriteAllJson(EntropyDir, FileKind.Checksum, app._checksum); } if (app._appCheckerResultJson != null) { dir.WriteAllJson(EntropyDir, FileKind.AppCheckerResult, app._appCheckerResultJson); } foreach (var file in app._assetFiles.Values) { dir.WriteAllBytes(AssetsDir, file.Name, file.RawBytes); } if (app._logoFile != null) { dir.WriteAllBytes(AssetsDir, app._logoFile.Name, app._logoFile.RawBytes); } if (app._themes != null) { dir.WriteAllJson(CodeDir, "Themes.json", app._themes); } if (app._resourcesJson != null) { dir.WriteAllJson(AssetsDir, "Resources.json", app._resourcesJson); } WriteDataSources(dir, app, errors); // Loose files. foreach (FileEntry file in app._unknownFiles.Values) { // Standardize the .json files so they're determinsitc and comparable if (file.Name.EndsWith(".json", StringComparison.OrdinalIgnoreCase)) { ReadOnlyMemory <byte> span = file.RawBytes; var je = JsonDocument.Parse(span).RootElement; var jsonStr = JsonNormalizer.Normalize(je); dir.WriteAllText(OtherDir, file.Name, jsonStr); } else { dir.WriteAllBytes(OtherDir, file.Name, file.RawBytes); } } var manifest = new CanvasManifestJson { FormatVersion = CurrentSourceVersion, Properties = app._properties, Header = app._header, PublishInfo = app._publishInfo, ScreenOrder = app._screenOrder }; dir.WriteAllJson("", FileKind.CanvasManifest, manifest); if (app._connections != null) { dir.WriteAllJson(ConnectionDir, FileKind.Connections, app._connections); } if (app._libraryReferences != null) { dir.WriteAllJson("", FileKind.ComponentReferences, app._libraryReferences); } dir.WriteAllJson(EntropyDir, FileKind.Entropy, app._entropy); }