├── FlowToVisio ├── Resources │ ├── Options.png │ ├── CDSbtton.jpg │ ├── Cancel-16.png │ ├── VisioTemplate.vsdx │ ├── powerautomate.jpg │ ├── Dataverse.32x32.png │ ├── VisioTemplate - Copy.zip │ ├── powerautomate (Custom).jpg │ ├── VisioTemplate - Copy │ │ ├── docProps │ │ │ ├── thumbnail.emf │ │ │ ├── core.xml │ │ │ ├── custom.xml │ │ │ └── app.xml │ │ ├── visio │ │ │ ├── media │ │ │ │ ├── image1.bmp │ │ │ │ ├── image2.png │ │ │ │ ├── image3.png │ │ │ │ ├── image4.png │ │ │ │ ├── image5.png │ │ │ │ ├── image6.png │ │ │ │ ├── image7.png │ │ │ │ ├── image8.bmp │ │ │ │ ├── image9.png │ │ │ │ ├── image10.png │ │ │ │ ├── image11.png │ │ │ │ ├── image12.png │ │ │ │ ├── image13.png │ │ │ │ ├── image14.bmp │ │ │ │ ├── image15.bmp │ │ │ │ ├── image16.bmp │ │ │ │ ├── image17.bmp │ │ │ │ ├── image18.bmp │ │ │ │ └── image19.bmp │ │ │ ├── pages │ │ │ │ ├── _rels │ │ │ │ │ ├── pages.xml.rels │ │ │ │ │ └── page1.xml.rels │ │ │ │ └── pages.xml │ │ │ ├── masters │ │ │ │ ├── _rels │ │ │ │ │ ├── master10.xml.rels │ │ │ │ │ ├── master11.xml.rels │ │ │ │ │ ├── master12.xml.rels │ │ │ │ │ ├── master13.xml.rels │ │ │ │ │ ├── master14.xml.rels │ │ │ │ │ ├── master16.xml.rels │ │ │ │ │ ├── master17.xml.rels │ │ │ │ │ ├── master18.xml.rels │ │ │ │ │ ├── master20.xml.rels │ │ │ │ │ ├── master21.xml.rels │ │ │ │ │ ├── master22.xml.rels │ │ │ │ │ ├── master23.xml.rels │ │ │ │ │ ├── master24.xml.rels │ │ │ │ │ ├── master25.xml.rels │ │ │ │ │ ├── master26.xml.rels │ │ │ │ │ ├── master28.xml.rels │ │ │ │ │ ├── master3.xml.rels │ │ │ │ │ ├── master6.xml.rels │ │ │ │ │ ├── master7.xml.rels │ │ │ │ │ ├── master9.xml.rels │ │ │ │ │ ├── master27.xml.rels │ │ │ │ │ └── masters.xml.rels │ │ │ │ ├── master1.xml │ │ │ │ ├── master5.xml │ │ │ │ └── master8.xml │ │ │ ├── _rels │ │ │ │ └── document.xml.rels │ │ │ └── windows.xml │ │ ├── _rels │ │ │ └── .rels │ │ └── [Content_Types].xml │ ├── CDS.svg │ └── powerautomate.svg ├── Classes │ ├── Solution.cs │ ├── APIConns.cs │ ├── Display.cs │ ├── LogicAppConn.cs │ ├── FlowConn.cs │ ├── FlowRun.cs │ └── FlowDefinition.cs ├── Visio │ ├── Comment.cs │ ├── ConditionAction.cs │ ├── Connection.cs │ ├── CaseAction.cs │ ├── ShapeXml.DataOps.cs │ ├── BaseShape.cs │ ├── VisioGen.cs │ ├── ShapeXML.Conditions.cs │ ├── ShapeXml.Template.cs │ ├── Action.cs │ └── VisionGen.Base.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── packages.config ├── app.config ├── FlowRuns │ ├── FlowRuns.cs │ └── FlowRuns.Designer.cs ├── FlowToVIsioPlugin.cs └── FlowToVisioControl.cs ├── images ├── Screenshot 2024-04-27 150033.png ├── Screenshot 2024-04-27 150357.png └── Screenshot 2024-04-27 151451.png ├── Limitations.md ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── AddActions.md ├── FlowToVisio.sln ├── FlowToVisio.nuspec ├── README.md └── .gitignore /FlowToVisio/Resources/Options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/Options.png -------------------------------------------------------------------------------- /FlowToVisio/Resources/CDSbtton.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/CDSbtton.jpg -------------------------------------------------------------------------------- /FlowToVisio/Resources/Cancel-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/Cancel-16.png -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate.vsdx -------------------------------------------------------------------------------- /FlowToVisio/Resources/powerautomate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/powerautomate.jpg -------------------------------------------------------------------------------- /images/Screenshot 2024-04-27 150033.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/images/Screenshot 2024-04-27 150033.png -------------------------------------------------------------------------------- /images/Screenshot 2024-04-27 150357.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/images/Screenshot 2024-04-27 150357.png -------------------------------------------------------------------------------- /images/Screenshot 2024-04-27 151451.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/images/Screenshot 2024-04-27 151451.png -------------------------------------------------------------------------------- /FlowToVisio/Resources/Dataverse.32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/Dataverse.32x32.png -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy.zip -------------------------------------------------------------------------------- /FlowToVisio/Resources/powerautomate (Custom).jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/powerautomate (Custom).jpg -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/docProps/thumbnail.emf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/docProps/thumbnail.emf -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image1.bmp -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image2.png -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image3.png -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image4.png -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image5.png -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image6.png -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image7.png -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image8.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image8.bmp -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image9.png -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image10.png -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image11.png -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image12.png -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image13.png -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image14.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image14.bmp -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image15.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image15.bmp -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image16.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image16.bmp -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image17.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image17.bmp -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image18.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image18.bmp -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image19.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkeD365/FlowToVisio/HEAD/FlowToVisio/Resources/VisioTemplate - Copy/visio/media/image19.bmp -------------------------------------------------------------------------------- /FlowToVisio/Classes/Solution.cs: -------------------------------------------------------------------------------- 1 | namespace LinkeD365.FlowToVisio 2 | { 3 | public class Solution 4 | { 5 | public string Name { get; set; } 6 | public string Publisher { get; set; } 7 | public string Id { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/pages/_rels/pages.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master10.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master11.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master12.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master13.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master14.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master16.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master17.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master18.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master20.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master21.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master22.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master23.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master24.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master25.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master26.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master28.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master3.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master6.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master7.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master9.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Classes/APIConns.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace LinkeD365.FlowToVisio 4 | { 5 | public class APIConns 6 | { 7 | public List LogicAppConns = new List(); 8 | public List FlowConns = new List(); 9 | 10 | public Display Display = new Display(); 11 | } 12 | } -------------------------------------------------------------------------------- /FlowToVisio/Classes/Display.cs: -------------------------------------------------------------------------------- 1 | namespace LinkeD365.FlowToVisio 2 | { 3 | public class Display 4 | { 5 | public bool ShowConCurrency { get; set; } 6 | public bool ShowSecure { get; set; } 7 | public bool ShowTrackedProps { get; set; } 8 | public bool ShowTriggers { get; set; } 9 | public bool ShowTrackingID { get; set; } 10 | public bool ShowComments { get; set; } = true; 11 | } 12 | } -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/master27.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/_rels/document.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Limitations.md: -------------------------------------------------------------------------------- 1 | # Limitations 2 | 3 | ### Unknown Actions 4 | 5 | The tool is only as good as the actions it knows about. As Microsoft introduce new actions, or alter the older ones, it is difficult to keep up. Please let me know if there are things missing by creating an [issue](https://github.com/LinkeD365/FlowToVisio/issues).  6 | 7 | If the code does not know about a particular action, it will be a plain action with no colour or logo 8 | 9 | ### Overlapping 10 | 11 | When the Visio diagram is created, there will be some overlapping lines or even actions, as the layout method is simple. Anyone who can offer a solution, let me know 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: LinkeD365 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behaviour: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behaviour** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[ENH]" 5 | labels: enhancement 6 | assignees: LinkeD365 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/docProps/core.xml: -------------------------------------------------------------------------------- 1 | 2 | Carl Cookson2020-09-02T09:58:38Z2020-09-02T09:58:38Z2021-05-09T13:38:56Zen-GB -------------------------------------------------------------------------------- /FlowToVisio/Resources/CDS.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/_rels/.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Visio/Comment.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xrm.Sdk; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | 5 | namespace LinkeD365.FlowToVisio 6 | { 7 | public class Comment 8 | { 9 | public Comment(Entity entity) 10 | { 11 | AnchorId = entity["anchor"].ToString(); 12 | CommentString = JObject.Parse(entity["body"].ToString())["ops"][0]["insert"].ToString(); 13 | Commenter = ((EntityReference)entity["createdby"]).Name; 14 | Kind = ((OptionSetValue)entity["kind"]).Value; 15 | Created = (DateTime)entity["createdon"]; 16 | } 17 | 18 | public string AnchorId { get; set; } 19 | 20 | public int Kind { get; set; } 21 | 22 | public string Commenter { get; set; } 23 | 24 | public string CommentString { get; set; } 25 | 26 | public DateTime Created { get; set; } 27 | } 28 | } -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/docProps/custom.xml: -------------------------------------------------------------------------------- 1 | 2 | 10746723571075459774true2021-05-09T13:38:52Z -------------------------------------------------------------------------------- /FlowToVisio/Classes/LogicAppConn.cs: -------------------------------------------------------------------------------- 1 | namespace LinkeD365.FlowToVisio 2 | { 3 | public class LogicAppConn 4 | { 5 | public int Id = 0; 6 | public string Name = string.Empty; 7 | public string SubscriptionId = string.Empty; 8 | public string TenantId = string.Empty; 9 | public string AppId = string.Empty; 10 | public string ReturnURL = string.Empty; 11 | public bool UseDev; 12 | 13 | public override string ToString() 14 | { 15 | return Name; 16 | } 17 | 18 | public override bool Equals(object obj) 19 | { 20 | if ((obj == null) || !this.GetType().Equals(obj.GetType())) 21 | { 22 | return false; 23 | } 24 | else 25 | { 26 | LogicAppConn p = (LogicAppConn)obj; 27 | return (Id == p.Id); 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /FlowToVisio/Classes/FlowConn.cs: -------------------------------------------------------------------------------- 1 | namespace LinkeD365.FlowToVisio 2 | { 3 | public class FlowConn 4 | { 5 | public int Id = 0; 6 | public string Name; 7 | public string AppId;// = string.Empty; 8 | public string TenantId = string.Empty; 9 | public string ReturnURL = string.Empty; 10 | public string Environment = string.Empty; 11 | public bool UseDev; 12 | 13 | public override string ToString() 14 | { 15 | return Name; 16 | } 17 | } 18 | 19 | /// 20 | /// Legacy version of settings 21 | /// 22 | public class FlowConnection 23 | { 24 | public string AppId;// = string.Empty; 25 | public string TenantId = string.Empty; 26 | public string ReturnURL = string.Empty; 27 | public string Environment = string.Empty; 28 | public bool UseDev; 29 | public string SubscriptionId = string.Empty; 30 | 31 | public string LATenantId = string.Empty; 32 | public string LAAppId = string.Empty; 33 | public string LAReturnURL = string.Empty; 34 | public bool LAUseDev; 35 | } 36 | } -------------------------------------------------------------------------------- /AddActions.md: -------------------------------------------------------------------------------- 1 | ## Adding Actions to Actions.json 2 | 3 | Actions.json is hosted within this repository and holds configuration for the display of actions. 4 | 5 | To be more dynamic in loading them and as new actions get created, add actions to this file. 6 | 7 | ![](/images/Screenshot%202024-04-27%20150033.png) 8 | 9 | Each action has a title, what will be used in the header of the Action in Visio 10 | 11 | Type: Leave as OpenApiConnection, this is the standard 12 | 13 | visioShape: Links back to the list of shapes in the Visio with apporpriate images 14 | 15 | ![](/images/Screenshot%202024-04-27%20150357.png) 16 | 17 | connectionName: This is taken from the connectionName property in the peek code 18 | 19 | operationId: This is taken from the operationId property via peek code 20 | 21 | Display: This is a list of the main properties for your action, those that you know about, usually those that are not hidden under advanced. 22 | 23 | Each display has a name and where to get the value from. Each node in the JSON is seperated by a |. This can be linked back to the peek code. 24 | 25 | For those values which are not essential to be displayed, you can use the value option 26 | 27 | ![](/images/Screenshot%202024-04-27%20151451.png) 28 | 29 | This will repeat for all parameters defined with a prefix (and node stepping with |) of "item/" in this case. -------------------------------------------------------------------------------- /FlowToVisio/Visio/ConditionAction.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace LinkeD365.FlowToVisio 6 | { 7 | public class ConditionAction : Action 8 | { 9 | public ConditionAction(JProperty property, Action parent, int current, int children, string type) : base(property, parent, current, children, type) 10 | { 11 | } 12 | 13 | protected void AddChildActions(IEnumerable childActions, Action parent, int finalNo) 14 | { 15 | int childCount = childActions.Count(); 16 | int curCount = 0; 17 | foreach (var actionProperty in childActions) 18 | { 19 | var childAction = Utils.AddAction(actionProperty, parent, ++curCount, childCount); 20 | FinalActions[finalNo] = childAction.EndAction; 21 | 22 | if (actionProperty.Parent != null && actionProperty.Parent.Children().Any(el => el.Value["runAfter"].HasValues && ((JProperty)el.Value["runAfter"].First()).Name == childAction.Name)) 23 | AddChildActions(actionProperty.Parent.Children().Where(el => el.Value["runAfter"].HasValues && ((JProperty)el.Value["runAfter"].First()).Name == childAction.Name), childAction, finalNo); 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/pages/pages.xml: -------------------------------------------------------------------------------- 1 | 2 |
-------------------------------------------------------------------------------- /FlowToVisio.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30320.27 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowToVisio", "FlowToVisio\FlowToVisio.csproj", "{07FA101F-0839-4B46-8136-8D43F57A82FD}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Soluton Items", "Soluton Items", "{4CB38560-C11D-4759-B817-BC19DA7127CC}" 9 | ProjectSection(SolutionItems) = preProject 10 | actions.json = actions.json 11 | FlowToVisio.nuspec = FlowToVisio.nuspec 12 | EndProjectSection 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {07FA101F-0839-4B46-8136-8D43F57A82FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {07FA101F-0839-4B46-8136-8D43F57A82FD}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {07FA101F-0839-4B46-8136-8D43F57A82FD}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {07FA101F-0839-4B46-8136-8D43F57A82FD}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {4FC9DA99-2C4E-4ECC-B132-DD3153B33338} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /FlowToVisio/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following set of attributes. 5 | // Change these attribute values to modify the information associated with an assembly. 6 | [assembly: AssemblyTitle("Flow To Visio XrmToolBox tool")] 7 | [assembly: AssemblyDescription("Allows creation of Visio diagrams to document your Power Automate Flows")] 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("LinkeD365")] 10 | [assembly: AssemblyProduct("FlowToVisio")] 11 | [assembly: AssemblyCopyright("Copyright © 2020")] 12 | [assembly: AssemblyTrademark("")] 13 | [assembly: AssemblyCulture("")] 14 | 15 | // Setting ComVisible to false makes the types in this assembly not visible to COM components. If 16 | // you need to access a type in this assembly from COM, set the ComVisible attribute to true on that type. 17 | [assembly: ComVisible(false)] 18 | 19 | // The following GUID is for the ID of the typelib if this project is exposed to COM 20 | [assembly: Guid("07fa101f-0839-4b46-8136-8d43f57a82fd")] 21 | 22 | // Version information for an assembly consists of the following four values: 23 | // 24 | // Major Version Minor Version Build Number Revision 25 | // 26 | // You can specify all the values or you can default the Build and Revision Numbers by using the '*' 27 | // as shown below: [assembly: AssemblyVersion("1.0.*")] 28 | [assembly: AssemblyVersion("1.0.0.0")] 29 | [assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /FlowToVisio/Visio/Connection.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace LinkeD365.FlowToVisio 6 | { 7 | public class Connection 8 | { 9 | public string Name { get; set; } 10 | public string Api { get; set; } 11 | 12 | public Connection(string name, string api) 13 | { 14 | Name = name; 15 | Api = api; 16 | } 17 | 18 | private static List aPIConnections; 19 | 20 | public static List APIConnections => aPIConnections; 21 | 22 | public static void ClearAPIs() 23 | { 24 | aPIConnections = null; 25 | } 26 | 27 | internal static void SetAPIs(JObject root) 28 | { 29 | aPIConnections = new List(); 30 | if (root["properties"]?["connectionReferences"] != null) 31 | foreach (var item in root["properties"]["connectionReferences"].Children()) 32 | if (item.Value["api"] != null) aPIConnections.Add(new Connection(item.Name, ((JProperty)item.Value["api"].Children().First()).Value.ToString())); 33 | else aPIConnections.Add(new Connection(item.Name, item.Value["connectionName"].ToString())); 34 | else if (root["properties"]?["parameters"]?["$connections"]?["value"] != null) // For Logic App connections 35 | foreach (var item in root["properties"]["parameters"]["$connections"]["value"].Children()) 36 | aPIConnections.Add(new Connection(item.Name, item.Value["id"].ToString().Substring(item.Value["id"].ToString().LastIndexOf("/") + 1))); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/docProps/app.xml: -------------------------------------------------------------------------------- 1 | 2 | Microsoft VisiofalsePages1Masters28Page-1Dynamic connectorAction.1032Init Variable.1033conditioncase.1048Teams.1063CDS.1065ScopeCompose.1072ExcelFlowOutlookPowerAppSharePointTimeWeatherAI BuilderOneDriveHTTP2WordEmailFormsForms.1106Cust VoicePushOfficeCDSOffice.1119falsefalsefalse16.0000 -------------------------------------------------------------------------------- /FlowToVisio/Visio/CaseAction.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using System.Linq; 3 | using System.Xml.Linq; 4 | 5 | namespace LinkeD365.FlowToVisio 6 | { 7 | public class CaseAction : Action 8 | { 9 | public CaseAction(Action parent, int current, int children, string Name) : base(parent) 10 | { 11 | Shape = new XElement(GetTemplateShape("case")); 12 | this.current = current; 13 | this.children = children; 14 | Shape.SetAttributeValue("NameU", Name); 15 | CalcPosition(); 16 | 17 | Line line = new Line(); 18 | line.Connect(Parent, this, current, children); 19 | } 20 | 21 | public CaseAction(JProperty property, Action parent, int current, int children) : base(property, parent, current, children, "case") 22 | { 23 | } 24 | 25 | public CaseAction(ConditionAction conditionAction, string type) : base() 26 | { 27 | Shape = new XElement(GetTemplateShape("case")); 28 | current = 1; 29 | children = 1; 30 | Shape.SetAttributeValue("NameU", conditionAction.PropertyName + ".End"); 31 | Props.Add(XElement.Parse(" ")); 32 | PinY = conditionAction.FinalActions.Min(el => el.PinY) - offsetY; //Double.Parse(Parent.Shape.Elements().Where(el => el.Attribute("N").Value == "PinY").First().Attribute("V").Value) - offsetY); 33 | PinX = conditionAction.PinX;// Double.Parse(Parent.Shape.Elements().Where(el => el.Attribute("N").Value == "PinX").First().Attribute("V").Value) - CalcX; 34 | SetPosition(); 35 | 36 | foreach (var parentAction in conditionAction.FinalActions) 37 | { 38 | Line line = new Line(); 39 | line.Connect(parentAction, this, current, children); 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/windows.xml: -------------------------------------------------------------------------------- 1 | 2 | 101119658473410.5101119658473410.5101102 -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/master1.xml: -------------------------------------------------------------------------------- 1 | 2 |
-------------------------------------------------------------------------------- /FlowToVisio/Resources/powerautomate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /FlowToVisio/Classes/FlowRun.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Net.Http; 5 | using System.Windows.Forms; 6 | 7 | namespace LinkeD365.FlowToVisio 8 | { 9 | public class FlowRun 10 | { 11 | public string Id; 12 | 13 | [DisplayName(" ")] 14 | public bool Selected { get; set; } 15 | 16 | public DateTime Start { get; set; } 17 | public DateTime? End { get; set; } 18 | 19 | [Browsable(false)] 20 | public TimeSpan? DurationTS 21 | { 22 | get { return (End == null) ? null : End - Start; } 23 | } 24 | 25 | public string Duration 26 | { 27 | get 28 | { 29 | if (DurationTS == null) { return string.Empty; } 30 | TimeSpan durationTS = DurationTS.Value; 31 | if (durationTS.TotalDays >= 1) return durationTS.ToString(@"d\d hh.mm"); 32 | if (durationTS.TotalHours >= 1) return durationTS.ToString(@"hh\:mm"); 33 | if (durationTS.TotalMinutes >= 1) return durationTS.ToString(@"mm\:ss"); 34 | if (durationTS.TotalSeconds >= 1) return durationTS.ToString(@"ss\.fff") + " s"; 35 | return durationTS.ToString(@"fff") + " ms"; 36 | } 37 | } 38 | 39 | public string Status { get; set; } 40 | 41 | [Browsable(false)] 42 | public HttpResponseMessage Message { get; internal set; } 43 | } 44 | 45 | internal class FlowRunComparer : IComparer 46 | { 47 | private string fieldName = string.Empty; 48 | private SortOrder sortOrder = SortOrder.None; 49 | 50 | public FlowRunComparer(string fieldName, SortOrder sortingOrder) 51 | { 52 | this.fieldName = fieldName; 53 | sortOrder = sortingOrder; 54 | } 55 | 56 | public int Compare(FlowRun flowRun1, FlowRun flowRun2) 57 | { 58 | switch (fieldName) 59 | { 60 | case "Selected": 61 | default: 62 | return sortOrder == SortOrder.Ascending ? flowRun1.Selected.CompareTo(flowRun2.Selected) : flowRun2.Selected.CompareTo(flowRun1.Selected); 63 | 64 | case "Start": 65 | return sortOrder == SortOrder.Ascending ? flowRun1.Start.CompareTo(flowRun2.Start) : flowRun2.Start.CompareTo(flowRun1.Start); 66 | 67 | case "End": 68 | return (int)(sortOrder == SortOrder.Ascending ? flowRun1.End.HasValue ? flowRun1.End?.CompareTo(flowRun2.End) : 0 : flowRun2.End?.CompareTo(flowRun1.End)); 69 | 70 | case "Duration": 71 | case "DurationTS": 72 | return (int)(sortOrder == SortOrder.Ascending ? flowRun1.DurationTS?.CompareTo(flowRun2.DurationTS) : flowRun2.DurationTS?.CompareTo(flowRun1.DurationTS)); 73 | 74 | case "Status": 75 | return sortOrder == SortOrder.Ascending ? flowRun1.Status.CompareTo(flowRun2.Status) : flowRun2.Status.CompareTo(flowRun1.Status); 76 | } 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /FlowToVisio/Classes/FlowDefinition.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel; 3 | using System.Windows.Forms; 4 | 5 | namespace LinkeD365.FlowToVisio 6 | { 7 | public class FlowDefinition 8 | { 9 | public bool Solution; 10 | public string Id; 11 | public string Definition; 12 | public string Name { get; set; } 13 | public string Description { get; set; } 14 | public bool Managed { get; set; } 15 | public string OwnerType { get; set; } 16 | public bool LogicApp { get; internal set; } 17 | [Browsable(false)] 18 | public string UniqueId { get; set; } 19 | 20 | public List Comments = new List(); 21 | } 22 | 23 | internal class FlowDefComparer : IComparer 24 | { 25 | private string memberName = string.Empty; // specifies the member name to be sorted 26 | private SortOrder sortOrder = SortOrder.None; // Specifies the SortOrder. 27 | 28 | public FlowDefComparer(string strMemberName, SortOrder sortingOrder) 29 | { 30 | memberName = strMemberName; 31 | sortOrder = sortingOrder; 32 | } 33 | 34 | public int Compare(FlowDefinition flow1, FlowDefinition flow2) 35 | { 36 | int returnValue = 1; 37 | switch (memberName) 38 | { 39 | case "Name": 40 | if (sortOrder == SortOrder.Ascending) 41 | { 42 | returnValue = flow1.Name.CompareTo(flow2.Name); 43 | } 44 | else 45 | { 46 | returnValue = flow2.Name.CompareTo(flow1.Name); 47 | } 48 | 49 | break; 50 | 51 | case "Description": 52 | if (sortOrder == SortOrder.Ascending) 53 | { 54 | returnValue = flow1.Description.CompareTo(flow2.Description); 55 | } 56 | else 57 | { 58 | returnValue = flow2.Description.CompareTo(flow1.Description); 59 | } 60 | 61 | break; 62 | 63 | case "Managed": 64 | if (sortOrder == SortOrder.Ascending) 65 | { 66 | returnValue = flow1.Managed.CompareTo(flow2.Managed); 67 | } 68 | else 69 | { 70 | returnValue = flow2.Managed.CompareTo(flow1.Managed); 71 | } 72 | break; 73 | 74 | default: 75 | if (sortOrder == SortOrder.Ascending) 76 | { 77 | returnValue = flow1.Name.CompareTo(flow2.Name); 78 | } 79 | else 80 | { 81 | returnValue = flow2.Name.CompareTo(flow1.Name); 82 | } 83 | break; 84 | } 85 | return returnValue; 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/_rels/masters.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/pages/_rels/page1.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | LinkeD365.FlowToVisio 5 | Flow to Visio 6 | $version$ 7 | LinkeD365 8 | LinkeD365 9 | false 10 | https://github.com/LinkeD365/FlowToVisio 11 | https://linked365.blog/wp-content/uploads/2020/03/smallIcon.png 12 | A XrmToolBox tool to create Visio diagram of your Power Automate Flows that are within solutions, not in solutions and also Logic Apps 13 | 14 | Tweaked Teams displays and added fix for switches 15 | #43 Fixed 16 | PR from @filcole 17 | 18 | Updated to latest version of XrmToolBox 19 | 20 | Added functionality to view flow Runs then cancel one or more runs 21 | 22 | Updated tool version 23 | added Actions for dataverse 24 | #36 Added solution filter 25 | 26 | #35 Added toggle to display comments 27 | 28 | #33, #34 flows with [] in the name are corrupted (Thanks @RobCieloCosta, @banderson-eco) 29 | 30 | #31 Added Options drop down to allow display of advanced settings 31 | Added more detail to Http triggers and actions 32 | 33 | #31, #32 Reduced bottom border for all shapes (Thanks @rappen, @mrhyde1st) 34 | #30 Added append to string icon and value (DO IT YOUR SELF NEXT TIME @MCJ) 35 | #29 Added SQL actions, which led to a lot of work to get parameters showing (Bloody MCJ) 36 | #28 Added open file option (Thanks @MCJ) 37 | #27 Resolved issue with Dataverse shape (thanks @Geoff-RSM) 38 | #26 Resolved issue where no connectors on logic apps caused error (thanks @BTSSCarol) 39 | #15 Allowed multi select of flows to create a large file (thanks @MichaelLaMontagne) 40 | 41 | #25 Added Help and improved user interface for connecting to Power Automate and Logic Apps 42 | 43 | #23 Fixed Filter/Search 44 | #22 Fixed Sort 45 | #20 Also returns flows that have been shared with me (Thanks Gilles) 👍 46 | #24 Allowed multi configurations for Power Automate and Logic Apps (Thanks Fred) 🙌 47 | Changed retrun from Flow and Logic apps to check if there are more to get 48 | 49 | #17 Added Office 365 actions 50 | 51 | #16 Added Logic App config, very beta 🤞 52 | 53 | #14 Removed File button 54 | #13 Fixed bug in conditions missing steps 55 | #8 Implemented INoConnectionRequired to tidy up and removed initial call to get flows from Dataverse 😜 56 | 57 | #11 Fixed display of _ in titles 58 | #10 Modified Title rendering to allow for quotes and double quotes (Thanks Amey) 59 | 60 | #9 Added Dev Azure APP info 61 | Added additional logging around Flow API access 62 | Added Append Array Increment Variables 🎁 63 | Added Office Users✨ 64 | 65 | #7 Resolved issue with repeat was a class 66 | Added PowerApp, more CDS, Forms, Customer Voice 67 | 10.7 68 | #3 Fixed readonly grid 69 | #5 Hope you are happy Jonas 70 | #6 hopefully 🤞🏽 71 | Added Onedrive and Word Templates 72 | 73 | Copyright 2020 74 | XrmToolBox Plugins Flow Visio 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/master5.xml: -------------------------------------------------------------------------------- 1 | 2 |
Case 3 |
-------------------------------------------------------------------------------- /FlowToVisio/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /FlowToVisio/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/[Content_Types].xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FlowToVisio/Visio/ShapeXml.DataOps.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | namespace LinkeD365.FlowToVisio 6 | { 7 | public class ComposeAction : Action 8 | { 9 | public ComposeAction(JProperty property, Action parent, int current, int children) : base(property, parent, current, children, "Compose") 10 | { 11 | AddName(); 12 | AddType(); 13 | AddText("Value: " + Property.Value["inputs"]); 14 | } 15 | } 16 | 17 | public class TableAction : Action 18 | { 19 | public TableAction(JProperty property, Action parent, int current, int children) : base(property, parent, current, children, "Compose") 20 | { 21 | AddName(); 22 | AddType(); 23 | 24 | var sb = new StringBuilder("From: " + Property.Value["inputs"]["from"]).AppendLine(); 25 | sb.AppendLine("Format: " + Property.Value["inputs"]["format"]); 26 | 27 | if (Property.Value["inputs"]["columns"] != null && Property.Value["inputs"]["columns"].HasValues) 28 | { 29 | sb.AppendLine("Columns:"); 30 | foreach (var props in Property.Value["inputs"]["columns"].First().Children()) 31 | { 32 | sb.AppendLine(props.Name + ": " + props.Value); 33 | } 34 | } 35 | 36 | AddText(sb.ToString()); 37 | } 38 | } 39 | 40 | public class FilterAction : Action 41 | { 42 | public FilterAction(JProperty property, Action parent, int current, int children) : base(property, parent, current, children, "Compose") 43 | { 44 | AddName(); 45 | AddType("Filter Array"); 46 | 47 | var sb = new StringBuilder("From: " + Property.Value["inputs"]["from"]).AppendLine(); 48 | sb.AppendLine("Where: " + Property.Value["inputs"]["where"]); 49 | 50 | AddText(sb.ToString()); 51 | } 52 | } 53 | 54 | public class JoinAction : Action 55 | { 56 | public JoinAction(JProperty property, Action parent, int current, int children) : base(property, parent, current, children, "Compose") 57 | { 58 | AddName(); 59 | AddType("Join"); 60 | 61 | var sb = new StringBuilder("From: " + Property.Value["inputs"]["from"]).AppendLine(); 62 | sb.AppendLine("Join With: " + Property.Value["inputs"]["joinWith"]); 63 | 64 | AddText(sb.ToString()); 65 | } 66 | } 67 | 68 | public class ParseAction : Action 69 | { 70 | public ParseAction(JProperty property, Action parent, int current, int children) : base(property, parent, current, children, "Compose") 71 | { 72 | AddName(); 73 | AddType("Parse JSON"); 74 | 75 | var sb = new StringBuilder("Content: " + Property.Value["inputs"]["content"]).AppendLine(); 76 | sb.AppendLine("Schema: " + Property.Value["inputs"]["schema"]); 77 | 78 | AddText(sb.ToString()); 79 | } 80 | } 81 | 82 | public class SelectAction : Action 83 | { 84 | public SelectAction(JProperty property, Action parent, int current, int children) : base(property, parent, current, children, "Compose") 85 | { 86 | AddName(); 87 | AddType("Select"); 88 | 89 | var sb = new StringBuilder("From: " + Property.Value["inputs"]["from"]).AppendLine(); 90 | 91 | if (Property.Value["inputs"]["select"] != null && Property.Value["inputs"]["select"].HasValues) 92 | { 93 | sb.AppendLine("Map:"); 94 | foreach (var props in Property.Value["inputs"]["select"].Children()) 95 | { 96 | sb.AppendLine(props.Name + ": " + props.Value); 97 | } 98 | } 99 | 100 | AddText(sb.ToString()); 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlowToVisio 2 | 3 | A tool to create Visio representations of your Cloud Flows. 4 | 5 | Tool available via [XrmToolBox](https://www.xrmtoolbox.com/plugins/LinkeD365.FlowToVisio/) 6 | 7 | [Limitations](Limitations.md) 8 | 9 | ## Solution Flows 10 | 11 | Open Tool 12 | 13 | For flows that are part of solutions, connect to your Dataverse environment using standard XrmToolBox [connections](https://www.xrmtoolbox.com/documentation/for-users/manage-connections/). Select the Connect to Dataverse button. 14 | 15 | ![](https://user-images.githubusercontent.com/43988771/106387685-c2f43b80-63d2-11eb-80e8-bcf3a25a9111.png) 16 | 17 | A list of Flows will be displayed. Use the Search bar to find the flow you want. Select Create Visio after selecting. A file dialogue will prompt you where to save your file. 18 | 19 | Once complete, a prompt will tell you if you are successful, displaying the number of actions the tool generated. 20 | 21 | ![](https://user-images.githubusercontent.com/43988771/106387742-0ea6e500-63d3-11eb-9f77-55475121e6ce.png) 22 | 23 | Find the Visio and open it up. 24 | 25 | ![](https://user-images.githubusercontent.com/43988771/106387764-28482c80-63d3-11eb-9af3-92eda5d70867.png) 26 | 27 | ## Connecting to Non-Solutioned Flows 28 | 29 | Flows will only appear in the main list if they are part of a solution. Solutions are only available with a Dataverse configuration. If you want to document flows that appear under My Flows, use the Connect to Flow API button. The dialogue allows you to add more than one connection, depending on your environments. Select the appropriate one, when you have created one, by using the select field.  30 | 31 | Add a new connection by selecting the + button. The Label field is just that, a name for the connection to distinguish it. The other 3 parts need to be populated with information created or retrieved in Azure. 32 | 33 | ![](https://user-images.githubusercontent.com/43988771/123132474-b25a6580-d446-11eb-9aee-1b0f1f0b3eba.png) 34 | 35 | Firstly, you will need to register an Azure application, more details are available [here](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app).  36 | 37 | In Azure, register an application, copy the App Id and Tenant Id into the corresponding boxes in the window available when you select Connect to Flow API 38 | 39 | ![](https://user-images.githubusercontent.com/43988771/106388080-ba9d0000-63d4-11eb-86d6-74df19e18279.png) 40 | 41 | Environment Id will be displayed when you navigate to the Flow in My Flows 42 | 43 | ![](https://user-images.githubusercontent.com/43988771/106388113-e0c2a000-63d4-11eb-8012-6892494ed7d8.png) 44 | 45 | > ## **The Return URL needs to be specified as a Mobile and Desktop application** 46 | 47 | ![](https://user-images.githubusercontent.com/43988771/106388141-0bacf400-63d5-11eb-99e9-22efb5c4bdea.png) 48 | 49 | ### Document Logic Apps (BETA) 50 | 51 | To connect to the Azure Logic Apps, there is a new button available in the toolbar. But first, you need to amend or add an app registration to allow access to Logic Apps. 52 | 53 | Register or amend an App registration. API Permissions required are Azure Service Management/user impersonation 54 | 55 | ![](https://user-images.githubusercontent.com/43988771/107113837-75a41e00-6859-11eb-954c-aecbdc253857.png) 56 | 57 | In the application, select the Connect to Logic Apps button 58 | 59 | ![](https://user-images.githubusercontent.com/43988771/107113888-c9af0280-6859-11eb-9bae-327743f74da2.png) 60 | 61 | Populate the API connection pop up with the appropriate settings.  62 | 63 | Firstly, the Subscription Id can be found on your Logic App Overview, the purple box here 64 | 65 | ![](https://user-images.githubusercontent.com/43988771/107113966-4d68ef00-685a-11eb-850e-fbbfe0e9740d.png) 66 | 67 | Next, the Application Id and Tenant Id both are available on the App Registration page, the red and yellow items here 68 | 69 | ![](https://user-images.githubusercontent.com/43988771/107114017-a5075a80-685a-11eb-9723-b4686c7e11c7.png) 70 | 71 | > ## **The Return URL needs to be specified as a Mobile and Desktop application** 72 | -------------------------------------------------------------------------------- /FlowToVisio/Visio/BaseShape.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using System.Linq; 3 | using System.Xml.Linq; 4 | 5 | namespace LinkeD365.FlowToVisio 6 | { 7 | public abstract class BaseShape 8 | { 9 | public JProperty Property { get; private set; } 10 | //protected XDocument xmlPage; 11 | 12 | protected XElement shapes; 13 | 14 | private XElement line; 15 | public double PinX { get; protected set; } 16 | public double PinY { get; protected set; } 17 | public int Id { get; protected set; } 18 | 19 | public XElement GetTemplateShape(string name) 20 | { 21 | var selectedElements = 22 | from el in Shapes.Elements() 23 | where el.Attribute("NameU")?.Value == name 24 | select el; 25 | return selectedElements.DefaultIfEmpty(null).FirstOrDefault(); 26 | } 27 | 28 | public XElement Line 29 | { 30 | get 31 | { 32 | if (line == null) 33 | { 34 | var selectedElements = 35 | from el in Shapes.Elements() 36 | where el.Attribute("ID").Value == "1301" 37 | select el; 38 | line = selectedElements.DefaultIfEmpty(null).FirstOrDefault(); 39 | } 40 | 41 | return line; 42 | } 43 | } 44 | 45 | public XElement Shapes 46 | { 47 | get 48 | { 49 | if (shapes == null) 50 | { 51 | var elements = 52 | from element in Utils.XMLPage.Descendants() 53 | where element.Name.LocalName == "Shapes" 54 | select element; 55 | // Return the selected elements to the calling code. 56 | shapes = elements.FirstOrDefault(); 57 | } 58 | 59 | return shapes; 60 | } 61 | } 62 | 63 | public string Name 64 | { 65 | get { if (Property == null) return "Line." + Id; else return Property.Name; } 66 | } 67 | 68 | public BaseShape(JProperty property) 69 | { 70 | Property = property; 71 | } 72 | 73 | public BaseShape() 74 | { 75 | } 76 | 77 | private XElement shape; 78 | 79 | public XElement Shape 80 | { 81 | get => shape; 82 | set 83 | { 84 | shape = value; 85 | Shapes.Add(shape); 86 | SetId(); 87 | } 88 | } 89 | 90 | public string PropertyName 91 | { 92 | get 93 | { 94 | if (Property == null) return string.Empty; 95 | string returnstring = Property.Name.Replace("__", "LiNkEd365").Replace("_", " ").Replace("LiNkEd365", "_").Replace("'", "'"); 96 | //var regext = new Regex("/(_{2,})|_/g, '$1'"); 97 | // string returnstring = regext.Replace(Property.Name, " ").Replace("'", "'"); 98 | return returnstring; 99 | } 100 | } 101 | 102 | private void SetId() 103 | { 104 | if (Shape.Attribute("ID") == null) return; 105 | Id = Shapes.Descendants().Where(el => el.Attribute("ID") != null).Max(x => int.Parse(x.Attribute("ID").Value)) + 1; 106 | 107 | Shape.SetAttributeValue("ID", Id); 108 | 109 | foreach (var stencil in Shape.Descendants().Where(el => el.Attribute("ID") != null)) 110 | stencil.SetAttributeValue("ID", 111 | Shapes.Descendants().Where(el => el.Attribute("ID") != null).Max(x => int.Parse(x.Attribute("ID").Value)) + 1); 112 | //if (Shape.Elements().Any(el => el.Name.LocalName == "Shapes")) 113 | //{ 114 | // foreach(var stencilShape in Shape.Elements().Where(el => el.Name.LocalName == Shapes).) 115 | //} 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /FlowToVisio/Visio/VisioGen.cs: -------------------------------------------------------------------------------- 1 | using LinkeD365.FlowToVisio.Properties; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.IO.Packaging; 7 | using System.Linq; 8 | using System.Windows; 9 | using XrmToolBox.Extensibility; 10 | 11 | namespace LinkeD365.FlowToVisio 12 | { 13 | public partial class FlowToVisioControl : PluginControlBase 14 | { 15 | //private JObject flowObject; 16 | 17 | #region xmlVisio bits 18 | 19 | private Package package; 20 | 21 | private PackagePart document; 22 | 23 | private PackagePart pages; 24 | private PackagePart page; 25 | // private XDocument xmlPage; 26 | 27 | #endregion xmlVisio bits 28 | 29 | public void GenerateVisio(string fileName, FlowDefinition flow, int flowCount, bool logicApp = false) 30 | { 31 | CreateVisio(fileName); 32 | JObject flowObject = JObject.Parse(flow.Definition); 33 | //CreateConnections(); 34 | Utils.Root = flowObject; 35 | Connection.SetAPIs(flowObject); 36 | var triggerProperty = flowObject["properties"]["definition"]["triggers"].First() as JProperty; 37 | 38 | var triggerShape = Utils.AddAction(triggerProperty, null, 0, 1); 39 | Utils.AddComment(triggerShape); 40 | if (flowObject["properties"]["definition"]["actions"].Children().Where(a => !a.Value["runAfter"].HasValues).Any()) 41 | Utils.AddActions(flowObject["properties"]["definition"]["actions"].Children().Where(a => !a.Value["runAfter"].HasValues), triggerShape); 42 | 43 | foreach (var shapeName in Utils.VisioTemplates) 44 | triggerShape.GetTemplateShape(shapeName).Remove(); 45 | 46 | //SaveXDocumentToPart(page, Utils.XMLPage); 47 | CreateNewPage(package, pages, Utils.XMLPage, //new Uri( Uri.EscapeUriString($"/visio/pages/{flow.Name.Replace(' ','_')}.xml"),UriKind.Relative), 48 | new Uri(Uri.EscapeUriString($"/visio/pages/flowPage{flowCount}.xml"), UriKind.Relative), page.ContentType, "http://schemas.microsoft.com/visio/2010/relationships/page", flow.Name); 49 | Utils.Ai.WriteEvent(logicApp ? "Logic App Actions" : "Flow Actions", Utils.actionCount); 50 | Utils.totalVisio += 1; 51 | Utils.totalActions += Utils.actionCount; 52 | Utils.actionCount = 0; 53 | 54 | return; 55 | } 56 | 57 | public void CompleteVisio(string fileName) 58 | { 59 | RemoveTemplate(); 60 | RecalcDocument(package); 61 | package.Close(); 62 | if (MessageBox.Show($@"{Utils.totalVisio} Visio{(Utils.totalVisio > 1 ? "s" : "")} generated with {Utils.totalActions} actions.{Environment.NewLine}Do you want to open the file?", "Visio Created Succesfully", 63 | MessageBoxButton.YesNo, MessageBoxImage.Information) == MessageBoxResult.Yes) 64 | { 65 | Process.Start(fileName); 66 | } 67 | package = null; 68 | Utils.totalActions = 0; 69 | Utils.totalVisio = 0; 70 | } 71 | 72 | public void CreateVisio(string fileName) 73 | { 74 | // create a copy of the resource template 75 | if (package == null) 76 | { 77 | File.WriteAllBytes(fileName, Resources.VisioTemplate); 78 | // var template = Package.Open(new MemoryStream(Properties.Resources.VisioTemplate), 79 | // FileMode.Open); template. 80 | 81 | #region get to the xml of the page 82 | 83 | package = Package.Open(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite); 84 | } 85 | document = GetPackagePart(package, "http://schemas.microsoft.com/visio/2010/relationships/document"); 86 | 87 | pages = GetPackagePart(package, document, "http://schemas.microsoft.com/visio/2010/relationships/pages"); 88 | page = GetPackagePart(package, pages, "http://schemas.microsoft.com/visio/2010/relationships/page"); 89 | ; 90 | Utils.XMLPage = GetXMLFromPart(page); 91 | 92 | #endregion get to the xml of the page 93 | 94 | return; 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /FlowToVisio/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace LinkeD365.FlowToVisio.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | public class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | public static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LinkeD365.FlowToVisio.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | public static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized resource of type System.Drawing.Bitmap. 65 | /// 66 | public static System.Drawing.Bitmap Cancel_16 { 67 | get { 68 | object obj = ResourceManager.GetObject("Cancel-16", resourceCulture); 69 | return ((System.Drawing.Bitmap)(obj)); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized resource of type System.Byte[]. 75 | /// 76 | public static byte[] CDS { 77 | get { 78 | object obj = ResourceManager.GetObject("CDS", resourceCulture); 79 | return ((byte[])(obj)); 80 | } 81 | } 82 | 83 | /// 84 | /// Looks up a localized resource of type System.Drawing.Bitmap. 85 | /// 86 | public static System.Drawing.Bitmap CDSbtton { 87 | get { 88 | object obj = ResourceManager.GetObject("CDSbtton", resourceCulture); 89 | return ((System.Drawing.Bitmap)(obj)); 90 | } 91 | } 92 | 93 | /// 94 | /// Looks up a localized resource of type System.Drawing.Bitmap. 95 | /// 96 | public static System.Drawing.Bitmap Dataverse_32x32 { 97 | get { 98 | object obj = ResourceManager.GetObject("Dataverse.32x32", resourceCulture); 99 | return ((System.Drawing.Bitmap)(obj)); 100 | } 101 | } 102 | 103 | /// 104 | /// Looks up a localized resource of type System.Drawing.Bitmap. 105 | /// 106 | public static System.Drawing.Bitmap Options { 107 | get { 108 | object obj = ResourceManager.GetObject("Options", resourceCulture); 109 | return ((System.Drawing.Bitmap)(obj)); 110 | } 111 | } 112 | 113 | /// 114 | /// Looks up a localized resource of type System.Drawing.Bitmap. 115 | /// 116 | public static System.Drawing.Bitmap powerautomate { 117 | get { 118 | object obj = ResourceManager.GetObject("powerautomate", resourceCulture); 119 | return ((System.Drawing.Bitmap)(obj)); 120 | } 121 | } 122 | 123 | /// 124 | /// Looks up a localized resource of type System.Drawing.Bitmap. 125 | /// 126 | public static System.Drawing.Bitmap powerautomate__Custom_ { 127 | get { 128 | object obj = ResourceManager.GetObject("powerautomate__Custom_", resourceCulture); 129 | return ((System.Drawing.Bitmap)(obj)); 130 | } 131 | } 132 | 133 | /// 134 | /// Looks up a localized resource of type System.Byte[]. 135 | /// 136 | public static byte[] VisioTemplate { 137 | get { 138 | object obj = ResourceManager.GetObject("VisioTemplate", resourceCulture); 139 | return ((byte[])(obj)); 140 | } 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /FlowToVisio/FlowRuns/FlowRuns.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Net.Http; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Windows.Forms; 11 | using XrmToolBox.Extensibility; 12 | 13 | namespace LinkeD365.FlowToVisio 14 | { 15 | public partial class FlowRunForm : Form 16 | { 17 | public List FlowRuns { get; set; } 18 | private FlowDefinition flowDefinition; 19 | private FlowConn flowConn; 20 | private HttpClient _client; 21 | private FlowToVisioControl parent; 22 | 23 | public FlowRunForm(List runs, FlowDefinition flow, FlowConn flowConn, HttpClient client, FlowToVisioControl flowToVisioControl) 24 | { 25 | InitializeComponent(); 26 | FlowRuns = runs; 27 | flowDefinition = flow; 28 | this.flowConn = flowConn; 29 | _client = client; 30 | parent = flowToVisioControl; 31 | dgvFlowRuns.DataSource = FlowRuns; 32 | SetupFilter(); 33 | SetupColumns(); 34 | SortFlowGrid("Start", SortOrder.Descending); 35 | Text = "Runs for " + flowDefinition.Name; 36 | lblCancel.Visible = false; 37 | Utils.Ai.WriteEvent("Flow Runs Displayed", runs.Count); 38 | } 39 | 40 | private void SetupFilter() 41 | { 42 | ddlFilter.Items.Clear(); 43 | ddlFilter.Items.Add("Filter By"); 44 | ddlFilter.Items.AddRange(FlowRuns.Select(fr => fr.Status).Distinct().ToArray()); 45 | ddlFilter.SelectedIndex = 0; 46 | } 47 | 48 | private void SetupColumns() 49 | { 50 | dgvFlowRuns.Columns["Selected"].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells; 51 | } 52 | 53 | private void SortFlowGrid(string name, SortOrder sortOrder) 54 | { 55 | List sortingFlowRuns = (List)dgvFlowRuns.DataSource; 56 | sortingFlowRuns.Sort(new FlowRunComparer(name, sortOrder)); 57 | dgvFlowRuns.DataSource = null; 58 | dgvFlowRuns.DataSource = sortingFlowRuns; 59 | SetupColumns(); 60 | dgvFlowRuns.Columns[name].HeaderCell.SortGlyphDirection = sortOrder; 61 | } 62 | 63 | private void dgvFlowRuns_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e) 64 | { 65 | if (dgvFlowRuns.Columns[e.ColumnIndex].SortMode == DataGridViewColumnSortMode.NotSortable) return; 66 | SortOrder sortOrder = GetSortOrder(e.ColumnIndex); 67 | 68 | SortFlowGrid(dgvFlowRuns.Columns[e.ColumnIndex].Name, sortOrder); 69 | } 70 | 71 | private SortOrder GetSortOrder(int columnIndex) 72 | { 73 | if (dgvFlowRuns.Columns[columnIndex].HeaderCell.SortGlyphDirection == SortOrder.None || 74 | dgvFlowRuns.Columns[columnIndex].HeaderCell.SortGlyphDirection == SortOrder.Descending) 75 | { 76 | dgvFlowRuns.Columns[columnIndex].HeaderCell.SortGlyphDirection = SortOrder.Ascending; 77 | return SortOrder.Ascending; 78 | } 79 | else 80 | { 81 | dgvFlowRuns.Columns[columnIndex].HeaderCell.SortGlyphDirection = SortOrder.Descending; 82 | return SortOrder.Descending; 83 | } 84 | } 85 | 86 | private void btnStopFlows_Click(object sender, EventArgs e) 87 | { 88 | List flowRuns = ((List)dgvFlowRuns.DataSource).Where(fr => fr.Selected && fr.Status == "Running").ToList(); 89 | if (flowRuns.Any()) 90 | { 91 | CancelAllFlows(flowRuns); 92 | } 93 | else MessageBox.Show("Please select one or more running flows before cancelling", "Select a Flow", MessageBoxButtons.OK, MessageBoxIcon.Information); 94 | } 95 | 96 | private FlowRun CancelFlow(FlowRun flowRun) 97 | { 98 | string url = $"https://api.flow.microsoft.com/providers/Microsoft.ProcessSimple/environments/{flowConn.Environment}/flows/{flowDefinition.UniqueId}/runs/{flowRun.Id}/cancel?api-version=2016-11-01"; 99 | flowRun.Message = _client.PostAsync(url, null).Result; 100 | return flowRun; 101 | } 102 | 103 | private List CancelFlows(List flowRuns, BackgroundWorker w) 104 | { 105 | return flowRuns.Select(fr => CancelFlow(fr)).ToList(); 106 | } 107 | 108 | private void CancelAllFlows(List flowRuns) 109 | { 110 | lblCancel.Visible = true; 111 | parent.WorkAsync(new WorkAsyncInfo 112 | { 113 | Message = "Cancelling " + flowRuns.Count + " Flows for " + flowDefinition.Name, 114 | Work = (w, args) => args.Result = CancelFlows(flowRuns, w), 115 | PostWorkCallBack = args => 116 | { 117 | if (args.Error != null) { parent.ShowError(args.Error.Message, "Error"); } 118 | else 119 | { 120 | List returnFlows = args.Result as List; 121 | DialogResult = DialogResult.Yes; 122 | // this.Close(); 123 | } 124 | } 125 | }); 126 | 127 | Utils.Ai.WriteEvent("Flow Runs Cancelled", flowRuns.Count); 128 | } 129 | 130 | private void chkAll_CheckedChanged(object sender, EventArgs e) 131 | { 132 | List flowRuns = dgvFlowRuns.DataSource as List; 133 | flowRuns.ForEach(fr => fr.Selected = chkAll.Checked); 134 | dgvFlowRuns.DataSource = null; 135 | dgvFlowRuns.DataSource = flowRuns; 136 | SetupColumns(); 137 | } 138 | 139 | private void ddlFilter_SelectedIndexChanged(object sender, EventArgs e) 140 | { 141 | if (ddlFilter.SelectedIndex <= 0) return; 142 | //List flowRuns = dgvFlowRuns.DataSource as List; 143 | 144 | dgvFlowRuns.DataSource = null; 145 | dgvFlowRuns.DataSource = FlowRuns.Where(fr => fr.Status == ddlFilter.Text).ToList(); 146 | SetupColumns(); 147 | } 148 | 149 | private void dgvFlowRuns_CellValueChanged(object sender, DataGridViewCellEventArgs e) 150 | { 151 | if (dgvFlowRuns.IsCurrentCellDirty) 152 | { 153 | dgvFlowRuns.CommitEdit(DataGridViewDataErrorContexts.Commit); 154 | } 155 | } 156 | 157 | private void dgvFlowRuns_CurrentCellDirtyStateChanged(object sender, EventArgs e) 158 | { 159 | dgvFlowRuns.CommitEdit(DataGridViewDataErrorContexts.Commit); 160 | } 161 | } 162 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /FlowToVisio/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\Resources\CDS.svg;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 123 | 124 | 125 | ..\Resources\VisioTemplate.vsdx;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 126 | 127 | 128 | ..\Resources\Options.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 129 | 130 | 131 | ..\Resources\CDSbtton.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 132 | 133 | 134 | ..\Resources\powerautomate.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 135 | 136 | 137 | ..\Resources\powerautomate (Custom).jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 138 | 139 | 140 | ..\Resources\Dataverse.32x32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 141 | 142 | 143 | ..\Resources\Cancel-16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 144 | 145 | -------------------------------------------------------------------------------- /FlowToVisio/FlowToVIsioPlugin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.Composition; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using XrmToolBox.Extensibility; 8 | using XrmToolBox.Extensibility.Interfaces; 9 | 10 | namespace LinkeD365.FlowToVisio 11 | { 12 | // Do not forget to update version number and author (company attribute) in AssemblyInfo.cs 13 | // class To generate Base64 string for Images below, you can use https://www.base64-image.de/ 14 | [Export(typeof(IXrmToolBoxPlugin))] 15 | [ExportMetadata("Name", "Flow Visio Builder")] 16 | [ExportMetadata("Description", "Tool to document Power Automate Flows in Visio")] 17 | // Please specify the base64 content of a 32x32 pixels image 18 | [ExportMetadata("SmallImageBase64", "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAANSSURBVEhL7ZVLKK1RFMe3Z0yUEgMcA2+S1/EohIkixQApBkwk6owwoJzJSZmYmZgYyKMUUiQDeUQZeCTPyEB5hBKSN/d3zt6Xe30fn3QNbvkP9llr7f2t/97rdZyen5/Fd8JZ/X4bfggM8akkLy0tnZ+fOzv/dZunpycvL6+EhASlvwMDgoeHh5GRkfv7ex8fHy3B6empq6trfn4+q7JqYEAwOjp6c3OTl5fn4eGhTH+ALQ6wxQFl0uCjHHDBw8PDrKysF+82m00KEtizs7M5c3JyokwafEQwMDBA6A8ODpQuxMXFhZJ+Y39/H+Pg4KDSNfiIICAgICgoaG9vT+l6YNdkMvn7+ytdAwMCMpmbm6t0Ie7u7ra2toiJ0oUg+mTxiwQxMTF8PD09rXQhqqurZ2ZmVldXlS4Eu4+Pj7GxsUrXwKCKdnZ2FhcXvb2909PTPT09ldUBSgiys7Oz+Pj40NBQZdXAuNGI8vr6OpGB5qUVCB2uw8PDIyMjyZM06uJTnQy47/HxMX6lCpOvr69uc7zBZwm+jFeC3d3d+fl5Jwf8/PzS0tLc3NywHx0dDQ0NXV5ehoWFUTPSKEfI9vZ2cHBwQUEBD6K0JicnpTcmR0lJid0pwCTR2dmJmpSUNDw8HBgYGB0dTX9eXV0lJyeTzNbWVnb5jJO3t7c0MGdWVlbq6ura2towdnR0OPzZERUV5XBpx1uCnJwc5KamJuSGhgYy3NzcjEW2G/WO3N7ejlxTU4M8Ozvb19eHYLFYurq6oAQEA4uEfh/Ixpmbm+OajY2Nvb29tbW1lKkkHh8fZ93Y2EhJSamvr+etqDTH1NRUaWlpVVXV8vIyFgl9Ahno6+trVsK9trZGJpj+skwpUFaSxFO4RGVlJWpERER5efnExAQni4uL+QQj0CcgpawMGd47NjbGEGUsU6a8gxFLCbAbEhLCLEHY3NxkhJCqzMxMtuLi4ujthYUFu6P3CCgn1oqKCgh4ODIuCKiLi4u7u3thYSEWSgBKBLPZzNBtaWlBxjVdyUNf/+lkKkB/fz89mZiYWFZWlpGR0d3djZGnoPJ2Opa+lUbazWq14oJjBIpxgpFaKCoqSk1NJVY9PT12jw58e6Pph+gf4ofAEP87gRC/AD2r9paj2iJuAAAAAElFTkSuQmCC")] 19 | // Please specify the base64 content of a 80x80 pixels image 20 | [ExportMetadata("BigImageBase64", "iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAIAAAABc2X6AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAApwSURBVHhe7Zp3TFTNFsCv7RN7L8/yBHtBokZjF7soSuxCbBjUaNTELgghUWNNUGPUWGJBEzUaRWMHTVQssSCCgr0idsWGvbxf7hw3l2Xhw91L8rLs7w+YOTP37j0zZ065u/l+//6t5SXyy/88g0thZ8elsLPjUtjZcSns7LgUdnbynMK5Xjw8fPjw2LFjycnJ6enpP3/+FKkt8uXLV6hQocqVK7du3bpdu3Zubm4yYCq5qPCpU6fWrl1brVq13r17N2/eHAVQScZswZP8+vXr6dOnLFBMTEydOnWmTJlSqlQpGTaJXFH4/fv3ISEh7u7ukyZNevDgwd69e+/evStj/0aRIkXat2/v6+t7+/btxYsXDx06lLaMmQIKm0tqauqAAQOSkpJQctSoUeHh4Xfu3GHrZPjf+Pz58759+9Bz9erV3759mzt37rJly2TMDExW+NWrVwMHDnz27Bm7GhQU9Pr1axn4e6KiooYPH56WlrZmzZoVK1aI1GHMVPjHjx88Ivu5Y8eO0NDQnO9qVmDVLB8HJDg4+OTJkyJ1DDMVRs/IyMj4+PiJEyc6rq3i+vXrnAvsHM2xcJE6gGkKs73+/v48U0BAwIcPH0RqC0x9/fr10skB69at44Bg4Zs3bxaRA5iWeJw9e5bgSSjy9vYuXry4SG0RFxfHvkknBwQGBu7atatXr17R0dEicgDTFD569CjxFgfLPovIJMhG6tat+/jx40qVKr1580ak9mKawo8ePSLH4IFMTxUAqzlx4oSXl1diYqKI7MWExOPFixekRBhqs2bNeKAWLVoUKFBgxowZ9erVkxkZadq0aefOnSMiIqSfNTzb8uXLExISPn78mJKSUrp06a9fv+K9cIoyww70k2wyxGFyLOlkokmTJlOnTpVOtrx8+XLMmDHSMQkTdvj58+eod+/evVq1alEqeHh4kDOz52y1zMhIzneY2BYWFsY9CUvYUbFixb5//+7n5zd58mSZYQdKb8cZNmwYkYmUUPpZk/MdthAbG0u+RZA/fvy4iOzFNKdVpUoVtrpEiRKUgSIyj9OnT3fo0OHatWuenp4ishfTqiWCJJGjQoUKb9++JcEUqS2IXtSAOB5VLeLh+vXrl03opooeOXIkWceIESO2bdsmUrtRG+04+E9yLP4OGjToy5cvIrVFcnIyVo2GipIlS7KBMmaL7du3b9myhQp55cqVInIAM720ygFJtmbPni2irOHA44GAhohsQXhnHVnBwYMHc1hE6gBmKkwizfZirmyFKUUst+rbty/+ecmSJeRwInUMk+MwG8JWvHv3Dqc6bdo0R/aE5Jxb4Rd2794dHh4uUocxWWG4efMm+5yamnrp0iWeGM3JN2UsBxB7L168OG7cOJTEkqmrOCBmFZuQK++0MMWZM2f6+PgMGTKEEMrBpmDEG1MGZPMeD2/MkeZ5GjduTDzH/82bN69ly5aUkzLDDHJFYeC21HR79uxp06YNlV3NmjVRVW2UzMhEfh2W5sKFC6wRM0NCQqpWrSrDJpFbCiu4OYZN5Xj37t2CBQuKNGtYETc3t1atWvn6+pYtW1akppK7Cv8fkue+anEp7OzkOYVz0WnhcskK1Ws3KoSKFSv+888/aih7KPepNPlbunRpriKAy4ApoLBNSIzr169PGKxWrVr16tX/+4caNWo0atTIz89v0aJFDx8+lNkZoUIk8ahUqZIxzSDedOvWbceOHVmlTcgJv127drWsC5dTb3KrzC+6ExMTqcDhP3+onJGtW7fK1IxkqTCJDgusPljRuXNnKtIuXbqQMCkJT0YCqNIjC2xpgwYNGO3UqROp0ty5c9u1a6fmK/z9/TNXSKhEHqomoCcFMEkL1S95C5KOHTtaXYI+anJWnDt3TqZm5C8UjoqKUkMUgMZvq0ePHm3cNDYE4bBhwyxCcsb+/furyYoNGzaoIQVLZvxOlGtlQH+PV7RoUYTR0dEi0pk1a5aabJPChQtTwMjUjNijMIwdO1ak+oZYajeUrF27NkKrau7AgQNqsgL1ZECHWlIGdM6cOSMDOspeFi9eLH0d0lWExYsXL2WLJk2ayLxM2Omlu3fvLi3dC/A0qk198+zZMxoWs1dY5Yn4JGlpGsY8f/586WhaiRIlmjVrJh3d83369AkhiEgvM65evUrizV/8RWbi4+NlaibsVBh/Ji0d0v0nT57QKFKkiPrNAr5NDSmoGaWl4+7uLi1NO3To0KtXr6SjafhFq193cILS0tLGjx8vfU2jy6fg+XFaIsoxdiqM82SBpaNpeBR0Vm3MDMfWsGFD1QXO4ZIlS6SjaVQREyZMkI6msTrS0mGlXr9+TQjAaHv06DF9+nS8oFVkunXrFp9Yp04d7Oj8+fNz5syhnAwICOBgHzlyhCGZZxNl2ZnJ/gx//PjRqvqJiIiQMR0eeufOnZGRkfgwgoRM0gMyQpmkY/W+npjHakpHB22xeaNfXLt2LXKKKuXDrfD09Lx8+bJMzYSdCpMVWBleWFiYjOlQFcqAAfVrCJmhw2nEhmVYBxdIJOMQ4vxFpAs3btwo1/z+bfluibjYr1+/qVOn+vj4GK0Av5WVzvYrzHGVAR2KdRnTSU5ObtmyJZtgVcGz/AcPHpRJ+rtL8hMZ02nevLnaTGzEuKakFnyouqpnz55IMByWVUmAUKdmKnDUVqFbYafC6enpVn54wYIFMpYRnh63VKZMGZmH28if3xKHM++w8VsYLy8vkepYvmdBk7i4uKSkJNVVEMyNvhCjsJl72Om0CD9oIh0d0k9pZYQPZkOWLl0qfT3STJs27f3797RR3urEkslKS9PKly8vLR3Lj72wXkKX0S8CPkVFbAW6YdXSMWCnwgQGo8JopYJnTEwMXodNI9FRQwp0Zo50NI1QafHqVhHOWGDYLBuI24R93MHt27dF9Aer+RwBaRmwU+H79++zhNLRtFq1aqnnTkhI4PRy9vDGakhRrFgxqyNg+c7N29tbNRTGpyQWSEtHxfYrV64EBwfv37/fyjRA5TwWbBqdnQrHxsZKS4e4qsKyioHsknE/gVBM+SUdHct5o/BS2bIiNTVVNbCgx48fqzawgrhAGpwm/nJ/YyIAWI3xtzL4VEoO6RjRT7INsnFaPL0xxcEbM1kN4YGRoEBKSoqSKMgN1GQF5mB0oaGhoTKgR1clfPDggVEljr2SU5Mq0z18+LCSKBYuXKhmKqZMmSIDGfkLhTEkdF61apXx1LGKVPlyjV5Ft23bFjkeZdeuXTdu3MCdEqKNWQrLQbYoF+jwWX369FGjKElVjGEb6xPuafzWJjAwECGBatOmTfhqnBPPZjzA3M0Sw6z4C4WNcCYpj6lXiStywR8wraCgIKsorUBtMgTOuUw1wEpRPKsaA3O1XI474LxYvQDAqole5LBqjhH85cqVKzM/lYUsX/EgpxbhSun/gYcmqLK6Nl2oBR6RKo9AoupS1o5Di7mWK1dOZtiCVcY7sGnkz9S0Hh4e3bp1swpOFthzPgJfzRJjFyQwJBtgdbatcL2Id3ZcCjs7LoWdHZfCzo5LYWfHpbCz41LY2XEp7OzkMYU17X8u2MCzBPAX9AAAAABJRU5ErkJggg==")] 21 | [ExportMetadata("BackgroundColor", "Lavender")] 22 | [ExportMetadata("PrimaryFontColor", "Black")] 23 | [ExportMetadata("SecondaryFontColor", "Gray")] 24 | public class FlowToVisioPlugin : PluginBase, IPayPalPlugin 25 | 26 | { 27 | public string DonationDescription => "Flow to Visio Fans"; 28 | 29 | public string EmailAccount => "carl.cookson@gmail.com"; 30 | 31 | public override IXrmToolBoxPluginControl GetControl() 32 | { 33 | return new FlowToVisioControl(); 34 | } 35 | 36 | /// 37 | /// Constructor 38 | /// 39 | public FlowToVisioPlugin() 40 | { 41 | // If you have external assemblies that you need to load, uncomment the following to 42 | // hook into the event that will fire when an Assembly fails to resolve 43 | // AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(AssemblyResolveEventHandler); 44 | } 45 | 46 | /// 47 | /// Event fired by CLR when an assembly reference fails to load Assumes that related 48 | /// assemblies will be loaded from a subfolder named the same as the Plugin For example, a 49 | /// folder named Sample.XrmToolBox.MyPlugin 50 | /// 51 | /// 52 | /// 53 | /// 54 | private Assembly AssemblyResolveEventHandler(object sender, ResolveEventArgs args) 55 | { 56 | Assembly loadAssembly = null; 57 | Assembly currAssembly = Assembly.GetExecutingAssembly(); 58 | 59 | // base name of the assembly that failed to resolve 60 | var argName = args.Name.Substring(0, args.Name.IndexOf(",")); 61 | 62 | // check to see if the failing assembly is one that we reference. 63 | List refAssemblies = currAssembly.GetReferencedAssemblies().ToList(); 64 | var refAssembly = refAssemblies.Where(a => a.Name == argName).FirstOrDefault(); 65 | 66 | // if the current unresolved assembly is referenced by our plugin, attempt to load 67 | if (refAssembly != null) 68 | { 69 | // load from the path to this plugin assembly, not host executable 70 | string dir = Path.GetDirectoryName(currAssembly.Location).ToLower(); 71 | string folder = Path.GetFileNameWithoutExtension(currAssembly.Location); 72 | dir = Path.Combine(dir, folder); 73 | 74 | var assmbPath = Path.Combine(dir, $"{argName}.dll"); 75 | 76 | if (File.Exists(assmbPath)) 77 | loadAssembly = Assembly.LoadFrom(assmbPath); 78 | else 79 | throw new FileNotFoundException($"Unable to locate dependency: {assmbPath}"); 80 | } 81 | 82 | return loadAssembly; 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /FlowToVisio/FlowRuns/FlowRuns.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace LinkeD365.FlowToVisio 2 | { 3 | partial class FlowRunForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FlowRunForm)); 32 | this.dgvFlowRuns = new System.Windows.Forms.DataGridView(); 33 | this.tscMain = new System.Windows.Forms.ToolStripContainer(); 34 | this.chkAll = new System.Windows.Forms.CheckBox(); 35 | this.tools = new System.Windows.Forms.ToolStrip(); 36 | this.btnStopFlows = new System.Windows.Forms.ToolStripButton(); 37 | this.ddlFilter = new System.Windows.Forms.ToolStripComboBox(); 38 | this.lblCancel = new System.Windows.Forms.Label(); 39 | ((System.ComponentModel.ISupportInitialize)(this.dgvFlowRuns)).BeginInit(); 40 | this.tscMain.ContentPanel.SuspendLayout(); 41 | this.tscMain.TopToolStripPanel.SuspendLayout(); 42 | this.tscMain.SuspendLayout(); 43 | this.tools.SuspendLayout(); 44 | this.SuspendLayout(); 45 | // 46 | // dgvFlowRuns 47 | // 48 | this.dgvFlowRuns.AllowUserToAddRows = false; 49 | this.dgvFlowRuns.AllowUserToDeleteRows = false; 50 | this.dgvFlowRuns.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; 51 | this.dgvFlowRuns.Dock = System.Windows.Forms.DockStyle.Fill; 52 | this.dgvFlowRuns.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnKeystroke; 53 | this.dgvFlowRuns.Location = new System.Drawing.Point(0, 0); 54 | this.dgvFlowRuns.MultiSelect = false; 55 | this.dgvFlowRuns.Name = "dgvFlowRuns"; 56 | this.dgvFlowRuns.RowHeadersVisible = false; 57 | this.dgvFlowRuns.Size = new System.Drawing.Size(545, 425); 58 | this.dgvFlowRuns.TabIndex = 0; 59 | this.dgvFlowRuns.CellValueChanged += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgvFlowRuns_CellValueChanged); 60 | this.dgvFlowRuns.ColumnHeaderMouseClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.dgvFlowRuns_ColumnHeaderMouseClick); 61 | this.dgvFlowRuns.CurrentCellDirtyStateChanged += new System.EventHandler(this.dgvFlowRuns_CurrentCellDirtyStateChanged); 62 | // 63 | // tscMain 64 | // 65 | this.tscMain.BottomToolStripPanelVisible = false; 66 | // 67 | // tscMain.ContentPanel 68 | // 69 | this.tscMain.ContentPanel.Controls.Add(this.lblCancel); 70 | this.tscMain.ContentPanel.Controls.Add(this.chkAll); 71 | this.tscMain.ContentPanel.Controls.Add(this.dgvFlowRuns); 72 | this.tscMain.ContentPanel.Size = new System.Drawing.Size(545, 425); 73 | this.tscMain.Dock = System.Windows.Forms.DockStyle.Fill; 74 | this.tscMain.LeftToolStripPanelVisible = false; 75 | this.tscMain.Location = new System.Drawing.Point(0, 0); 76 | this.tscMain.Name = "tscMain"; 77 | this.tscMain.RightToolStripPanelVisible = false; 78 | this.tscMain.Size = new System.Drawing.Size(545, 450); 79 | this.tscMain.TabIndex = 2; 80 | this.tscMain.Text = "toolStripContainer1"; 81 | // 82 | // tscMain.TopToolStripPanel 83 | // 84 | this.tscMain.TopToolStripPanel.Controls.Add(this.tools); 85 | // 86 | // chkAll 87 | // 88 | this.chkAll.AutoSize = true; 89 | this.chkAll.Location = new System.Drawing.Point(3, 3); 90 | this.chkAll.Name = "chkAll"; 91 | this.chkAll.Size = new System.Drawing.Size(15, 14); 92 | this.chkAll.TabIndex = 1; 93 | this.chkAll.UseVisualStyleBackColor = true; 94 | this.chkAll.CheckedChanged += new System.EventHandler(this.chkAll_CheckedChanged); 95 | // 96 | // tools 97 | // 98 | this.tools.Dock = System.Windows.Forms.DockStyle.None; 99 | this.tools.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { 100 | this.btnStopFlows, 101 | this.ddlFilter}); 102 | this.tools.Location = new System.Drawing.Point(3, 0); 103 | this.tools.Name = "tools"; 104 | this.tools.Size = new System.Drawing.Size(278, 25); 105 | this.tools.TabIndex = 0; 106 | // 107 | // btnStopFlows 108 | // 109 | this.btnStopFlows.Image = global::LinkeD365.FlowToVisio.Properties.Resources.Cancel_16; 110 | this.btnStopFlows.ImageTransparentColor = System.Drawing.Color.Magenta; 111 | this.btnStopFlows.Name = "btnStopFlows"; 112 | this.btnStopFlows.Size = new System.Drawing.Size(143, 22); 113 | this.btnStopFlows.Text = "Cancel Selected Flows"; 114 | this.btnStopFlows.Click += new System.EventHandler(this.btnStopFlows_Click); 115 | // 116 | // ddlFilter 117 | // 118 | this.ddlFilter.Name = "ddlFilter"; 119 | this.ddlFilter.Size = new System.Drawing.Size(121, 25); 120 | this.ddlFilter.SelectedIndexChanged += new System.EventHandler(this.ddlFilter_SelectedIndexChanged); 121 | // 122 | // lblCancel 123 | // 124 | this.lblCancel.Dock = System.Windows.Forms.DockStyle.Fill; 125 | this.lblCancel.Font = new System.Drawing.Font("Microsoft Sans Serif", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 126 | this.lblCancel.Location = new System.Drawing.Point(0, 0); 127 | this.lblCancel.Name = "lblCancel"; 128 | this.lblCancel.Size = new System.Drawing.Size(545, 425); 129 | this.lblCancel.TabIndex = 2; 130 | this.lblCancel.Text = "Cancelling Flow Runs"; 131 | this.lblCancel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 132 | // 133 | // FlowRunForm 134 | // 135 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 136 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 137 | this.ClientSize = new System.Drawing.Size(545, 450); 138 | this.Controls.Add(this.tscMain); 139 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 140 | this.Name = "FlowRunForm"; 141 | this.Text = "FlowRuns"; 142 | ((System.ComponentModel.ISupportInitialize)(this.dgvFlowRuns)).EndInit(); 143 | this.tscMain.ContentPanel.ResumeLayout(false); 144 | this.tscMain.ContentPanel.PerformLayout(); 145 | this.tscMain.TopToolStripPanel.ResumeLayout(false); 146 | this.tscMain.TopToolStripPanel.PerformLayout(); 147 | this.tscMain.ResumeLayout(false); 148 | this.tscMain.PerformLayout(); 149 | this.tools.ResumeLayout(false); 150 | this.tools.PerformLayout(); 151 | this.ResumeLayout(false); 152 | 153 | } 154 | 155 | #endregion 156 | 157 | private System.Windows.Forms.DataGridView dgvFlowRuns; 158 | private System.Windows.Forms.ToolStripContainer tscMain; 159 | private System.Windows.Forms.ToolStrip tools; 160 | private System.Windows.Forms.ToolStripButton btnStopFlows; 161 | private System.Windows.Forms.ToolStripComboBox ddlFilter; 162 | private System.Windows.Forms.CheckBox chkAll; 163 | private System.Windows.Forms.Label lblCancel; 164 | } 165 | } -------------------------------------------------------------------------------- /FlowToVisio/Visio/ShapeXML.Conditions.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using System.Linq; 3 | using System.Text; 4 | using System.Xml.Linq; 5 | 6 | namespace LinkeD365.FlowToVisio 7 | { 8 | public class IfAction : ConditionAction 9 | { 10 | public CaseAction Yes { get; private set; } 11 | public CaseAction No { get; private set; } 12 | 13 | private void CreateYesNo() 14 | { 15 | Yes = new CaseAction(this, 1, 2, Property.Name + ".Yes"); 16 | Yes.Props.Add(XElement.Parse(" ")); 17 | Yes.AddFillColour("136, 218, 141"); 18 | 19 | FinalActions.Add(Yes); 20 | if (Property.Value["actions"] != null && Property.Value["actions"].Count() > 0) 21 | AddChildActions(Property.Value["actions"].Children().Where(a => !a.Value["runAfter"].HasValues), Yes, 0); 22 | 23 | No = new CaseAction(this, 2, 2, Property.Name + ".No"); 24 | No.Props.Add(XElement.Parse(" ")); 25 | No.AddFillColour("251, 137, 129"); 26 | FinalActions.Add(No); 27 | 28 | if (Property.Value["else"] != null && ((JObject)Property.Value["else"])["actions"] != null) 29 | AddChildActions((Property.Value["else"] as JObject)["actions"].Children().Where(el => !el.Value["runAfter"].HasValues), No, 1); 30 | 31 | EndAction = new CaseAction(this, "If"); 32 | EndAction.AddFillColour("221, 223, 224"); 33 | } 34 | 35 | public IfAction(JProperty property, Action parent, int current, int children) : base(property, parent, current, children, "condition") 36 | { 37 | Props.Add(XElement.Parse(" ")); 38 | Props.Add(XElement.Parse(" ")); 39 | var sb = new StringBuilder("Expression: "); 40 | var condition = ((JObject)property.Value["expression"]).Children().First(); 41 | sb.Append(condition.Value.First() + " " + condition.Name + " " + condition.Value.Last()); 42 | AddText(sb); 43 | CreateYesNo(); 44 | } 45 | } 46 | 47 | public class SwitchAction : ConditionAction 48 | { 49 | public SwitchAction(JProperty property, Action parent, int current, int children) : base(property, parent, current, children, "condition") 50 | { 51 | AddFillColour("234,237,239"); 52 | Props.Add(XElement.Parse(" ")); 53 | Props.Add(XElement.Parse(" ")); 54 | var sb = new StringBuilder("Expression: "); 55 | // var condition = ((JObject)property.Value["expression"]).Children().First(); 56 | sb.Append(property.Value["expression"]); 57 | AddText(sb); 58 | 59 | CreateCases(); 60 | } 61 | 62 | private void CreateCases() 63 | { 64 | int curCount = 0; 65 | int childCount = ((JObject)Property.Value["cases"]).Children().Count(); 66 | if (Property.Value["default"].HasValues) childCount++; 67 | foreach (var caseProperty in ((JObject)Property.Value["cases"]).Children()) 68 | { 69 | var caseAction = new CaseAction(caseProperty, this, ++curCount, childCount); 70 | caseAction.Props.Add(XElement.Parse(" ")); 71 | FinalActions.Add(caseAction); 72 | if (caseProperty.Value["actions"] != null && 73 | ((JObject)caseProperty.Value["actions"]).Children().Count() > 0) 74 | AddChildActions( 75 | ((JObject)caseProperty.Value["actions"]).Children() 76 | .Where(el => !el.Value["runAfter"].HasValues), 77 | caseAction, 78 | FinalActions.Count() - 1); 79 | //FinalActions[0] = caseAction.EndAction; 80 | } 81 | 82 | if (Property.Value["default"]["actions"].HasValues) 83 | { 84 | var defaultAction = new CaseAction(this, ++curCount, childCount, "Default"); 85 | defaultAction.Props.Add(XElement.Parse(" ")); 86 | FinalActions.Add(defaultAction); 87 | 88 | AddChildActions(((JObject)Property.Value["default"]["actions"]).Children().Where(el => !el.Value["runAfter"].HasValues), defaultAction, FinalActions.Count() - 1); 89 | //FinalActions[1] = defaultAction.EndAction; 90 | } 91 | 92 | EndAction = new CaseAction(this, "Switch"); 93 | EndAction.AddFillColour("234,237,239"); 94 | } 95 | } 96 | 97 | public class ForEachAction : ConditionAction 98 | { 99 | public ForEachAction(JProperty property, Action parent, int current, int children) : base(property, parent, current, children, "condition") 100 | { 101 | Props.Add(XElement.Parse(" ")); 102 | Props.Add(XElement.Parse(" ")); 103 | var sb = new StringBuilder("On: "); 104 | // var condition = ((JObject)property.Value["expression"]).Children().First(); 105 | sb.Append(property.Value["foreach"]); 106 | AddText(sb); 107 | FinalActions.Add(this); 108 | AddChildActions(((JObject)Property.Value["actions"]).Children().Where(el => !el.Value["runAfter"].HasValues), this, 0); 109 | 110 | EndAction = new CaseAction(this, "For Each"); 111 | EndAction.AddFillColour("234,237,239"); 112 | } 113 | } 114 | 115 | public class ScopeAction : ConditionAction 116 | { 117 | public ScopeAction(JProperty property, Action parent, int current, int children) : base(property, parent, current, children, "Scope") 118 | { 119 | AddName(); 120 | AddType("Scope"); 121 | 122 | FinalActions.Add(this); 123 | 124 | AddChildActions(((JObject)Property.Value["actions"]).Children().Where(el => !el.Value["runAfter"].HasValues), this, 0); 125 | 126 | EndAction = new CaseAction(this, "Scope"); 127 | EndAction.AddFillColour("238,225,217"); 128 | } 129 | } 130 | 131 | public class UntilAction : ConditionAction 132 | { 133 | public UntilAction(JProperty property, Action parent, int current, int children) : base(property, parent, current, children, "condition") 134 | { 135 | AddName(); 136 | AddType("Do Until"); 137 | 138 | var sb = new StringBuilder("Expression: " + Property.Value["expression"]).AppendLine(); 139 | if (Property.Value["limit"] != null && Property.Value["limit"].HasValues) 140 | { 141 | sb.AppendLine("Limit:"); 142 | foreach (var props in Property.Value["limit"].Children()) 143 | sb.AppendLine(props.Name + ": " + props.Value); 144 | } 145 | AddText(sb.ToString()); 146 | 147 | FinalActions.Add(this); 148 | 149 | AddChildActions(((JObject)Property.Value["actions"]).Children().Where(el => !el.Value["runAfter"].HasValues), this, 0); 150 | 151 | EndAction = new CaseAction(this, "Do Until"); 152 | EndAction.AddFillColour("234,237,239"); 153 | } 154 | } 155 | 156 | public class ChangeSetAction : ConditionAction 157 | { 158 | public ChangeSetAction(JProperty property, Action parent, int current, int children) : base(property, parent, current, children, "CDS") 159 | { 160 | AddName(); 161 | AddType("Change Set"); 162 | FinalActions.Add(this); 163 | 164 | foreach (var childProperty in ((JObject)Property.Value["actions"]).Children()) 165 | FinalActions[0] = Utils.AddAction(childProperty, FinalActions[0], 1, 1); 166 | 167 | // AddChildActions(((JObject)Property.Value["actions"]).Children(), this, 0); 168 | 169 | EndAction = new CaseAction(this, "Change Set"); 170 | EndAction.AddFillColour("234,223,234"); 171 | } 172 | } 173 | } -------------------------------------------------------------------------------- /FlowToVisio/Visio/ShapeXml.Template.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace LinkeD365.FlowToVisio 8 | { 9 | public class TemplateAction : Action 10 | { 11 | public JProperty Template { get; private set; } 12 | 13 | public TemplateAction(JProperty template, JProperty property, Action parent, int current, int children, string templateName) : base(property, parent, current, children, templateName) 14 | { 15 | Template = template; 16 | AddName(); 17 | AddType(template.Name); 18 | 19 | var sb = new StringBuilder(); 20 | if (Template.Value["display"] != null) 21 | { 22 | foreach (var display in Template.Value["display"].Children()) 23 | { 24 | var splitList = display.Value["value"].ToString().Split('|').ToList(); 25 | string value = GetPropValue(property, splitList, display.Value["options"], ((bool?)display.Value["parameter"])); 26 | if (value != string.Empty) sb.AppendLine(display.Name + " : " + value); 27 | } 28 | } 29 | 30 | if (Template.Value["repeat"] != null) 31 | { 32 | foreach (var display in Template.Value["repeat"].Children()) 33 | { 34 | var repeat = CreateRepeat(property, display["path"].ToString().Split('|').ToList(), 35 | display["filter"]?.ToString().Split('|').ToList() ?? new List(), int.Parse(display["left"]?.ToString() ?? "0"), display["class"] != null && bool.Parse(display["class"].ToString())); 36 | if (repeat != String.Empty) 37 | { 38 | if (display["title"] != null) sb.AppendLine(display["title"] + " :"); 39 | sb.AppendLine(repeat); 40 | } 41 | } 42 | } 43 | 44 | AddText(sb); 45 | } 46 | 47 | private string CreateRepeat(JToken property, List splitList, List filter, int left, bool isClass) 48 | { 49 | string name = splitList[0]; 50 | if (property.Type == JTokenType.Object) 51 | { 52 | var childObject = ((JObject)property).Children().FirstOrDefault(prop => prop.Name == name); 53 | if (childObject == null) return string.Empty; 54 | if (splitList.Count == 1) 55 | { 56 | var sb = new StringBuilder(); 57 | if (isClass) 58 | { 59 | foreach (var classProp in childObject.Value.Children()) 60 | { 61 | sb.AppendLine(classProp.Name + CreateClassString(classProp, filter, left)); 62 | } 63 | } 64 | else 65 | { 66 | foreach (var valueProp in childObject.Value.Children().Where(vo => !filter.Contains(vo.Name))) 67 | { 68 | sb.AppendLine(valueProp.Name.Substring(left, valueProp.Name.Length - left) + " : " + valueProp.Value); 69 | } 70 | } 71 | 72 | return sb.ToString(); 73 | } 74 | return CreateRepeat(childObject, splitList.GetRange(1, splitList.Count - 1), filter, left, isClass); 75 | //if (((JProperty)property).Value[name] == null) return string.Empty; 76 | } 77 | else if (property.Type == JTokenType.Property) 78 | { 79 | if (((JProperty)property).Value[name] == null) return string.Empty; 80 | if (splitList.Count == 1) 81 | { 82 | var sb = new StringBuilder(); 83 | if (((JProperty)property).Value[name] is JObject) 84 | { 85 | var valueObject = ((JProperty)property).Value[name] as JObject; 86 | foreach (var valueProp in valueObject.Children().Where(vo => !filter.Contains(vo.Name))) 87 | { 88 | sb.AppendLine(valueProp.Name.Substring(left, valueProp.Name.Length - left) + " : " + valueProp.Value); 89 | } 90 | 91 | return sb.ToString(); 92 | } 93 | 94 | if (((JProperty)property).Value[name] is JArray) 95 | { 96 | foreach (var tokenValue in ((JProperty)property).Value[name] as JArray) 97 | { 98 | if (tokenValue is JObject) 99 | { 100 | var objectValue = tokenValue as JObject; 101 | foreach (var childValue in objectValue.Children()) 102 | { 103 | sb.AppendLine(childValue.Name.Substring(left, childValue.Name.Length - left) + " : " + childValue.Value); 104 | } 105 | } 106 | else sb.AppendLine(tokenValue.ToString()); 107 | } 108 | 109 | return sb.ToString(); 110 | } 111 | 112 | return string.Empty; 113 | } 114 | 115 | return CreateRepeat(((JProperty)property).Value[name], splitList.GetRange(1, splitList.Count - 1), filter, left, isClass); 116 | } 117 | 118 | return string.Empty; 119 | } 120 | 121 | private string CreateClassString(JProperty property, List filter, int left) 122 | { 123 | var sb = new StringBuilder(property.Name + " : ").AppendLine(); 124 | 125 | foreach (var propValue in property.Children().Values().Where(vo => !filter.Contains(vo.Name))) 126 | { 127 | sb.AppendLine(propValue.Name.Substring(left, propValue.Name.Length - left) + " : " + propValue.Value); 128 | } 129 | 130 | // property.Values().Where(vo => !filter.Contains(vo.Name)) 131 | return sb.ToString(); 132 | } 133 | 134 | private string GetPropValue(JToken property, List splitList, JToken options, bool? parameter) 135 | { 136 | string name = splitList[0]; 137 | if (property.Type == JTokenType.Object) 138 | { 139 | if ((parameter ?? false) && splitList.Count == 1) return GetParameters(property, name); 140 | var childObject = ((JObject)property).Children().FirstOrDefault(prop => prop.Name == name); 141 | if (childObject == null) return string.Empty; 142 | if (splitList.Count == 1) return GetOptionValue(childObject.Value.ToString(), options); 143 | 144 | return GetPropValue(childObject, splitList.GetRange(1, splitList.Count - 1), options, parameter); 145 | } 146 | else if (property.Type == JTokenType.Property) 147 | { 148 | if ((parameter ?? false) && splitList.Count == 1) return GetParameters(property, name); //((JProperty)property).Values().Where(v => ((JProperty)v).Name.Contains(name)) 149 | if (((JProperty)property).Value[name] == null) return string.Empty; 150 | if (splitList.Count == 1) return GetOptionValue(((JProperty)property).Value[name].ToString(), options); 151 | return GetPropValue(((JProperty)property).Value[name], splitList.GetRange(1, splitList.Count - 1), options, parameter); 152 | } 153 | 154 | return string.Empty; 155 | // if (property.Value[name] == null) return string.Empty; if (splitList.Count == 0) 156 | // return property.Value[name].ToString(); 157 | 158 | //return GetPropValue((JProperty)property.Value[name], splitList.GetRange(1, splitList.Count)); 159 | } 160 | 161 | private string GetParameters(JToken property, string name) 162 | { 163 | var sb = new StringBuilder(); 164 | 165 | foreach (JProperty parameter in ((JProperty)property).Values().Where(v => ((JProperty)v).Name.Contains(name))) 166 | { 167 | sb.AppendLine(parameter.Name.Substring(name.Length) + " : " + parameter.Value.ToString()); 168 | } 169 | if (sb.ToString() != string.Empty) return Environment.NewLine + sb.ToString(); 170 | return string.Empty; 171 | } 172 | 173 | private string GetOptionValue(string value, JToken options) 174 | { 175 | if (options == null) return value; 176 | 177 | var option = options.Children().FirstOrDefault(opt => opt.Name == value); 178 | if (option == null) return value; 179 | return option.Value.ToString(); 180 | } 181 | } 182 | } -------------------------------------------------------------------------------- /FlowToVisio/Visio/Action.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Xml.Linq; 7 | 8 | namespace LinkeD365.FlowToVisio 9 | { 10 | public class Action : BaseShape 11 | { 12 | private Action endAction; 13 | public List FinalActions = new List(); 14 | private XElement sections; 15 | protected double offsetX = 3; // inches 16 | protected double offsetY = 1.1; 17 | 18 | protected double CalcX 19 | { 20 | get 21 | { 22 | if (children == 1) return 0; 23 | // if (children == 2) 24 | double width = children + 1; 25 | return (-(width / 2) + (double)current / (children + 1) * width) * offsetX; 26 | // return (Math.Ceiling((double)children / 2) - current + (children % 2 == 0 ? 1 : 27 | // 0)) * offsetX; 28 | } 29 | } 30 | 31 | protected void CalcPosition() 32 | { 33 | PinY = Parent.EndAction.PinY - 34 | offsetY; // Double.Parse(Shape.Elements().Where(el => el.Attribute("N").Value == "PinY").First().Attribute("V").Value) - offsetY); 35 | PinX = Parent.EndAction.PinX + 36 | CalcX; // Double.Parse(parent.Shape.Elements().Where(el => el.Attribute("N").Value == "PinX").First().Attribute("V").Value) - CalcX; 37 | SetPosition(); 38 | } 39 | 40 | protected void SetPosition() 41 | { 42 | Shape.Elements().First(el => el.Attribute("N").Value == "PinY").SetAttributeValue("V", PinY); 43 | Shape.Elements().First(el => el.Attribute("N").Value == "PinX").SetAttributeValue("V", PinX); 44 | } 45 | 46 | protected int current = 0; 47 | protected int children = 0; 48 | 49 | public XElement Connections 50 | { 51 | get 52 | { 53 | if (sections == null) 54 | { 55 | var elements = 56 | from element in Shape.Descendants() 57 | where element.Name.LocalName == "Section" && element.Attribute("N").Value == "Connection" 58 | select element; 59 | if (!elements.Any()) 60 | { 61 | sections = new XElement("Section"); 62 | sections.SetAttributeValue("N", "Connection"); 63 | Shape.Add(sections); 64 | } 65 | else 66 | sections = elements.FirstOrDefault(); 67 | } 68 | 69 | return sections; 70 | } 71 | } 72 | 73 | private XElement props; 74 | 75 | public XElement Props 76 | { 77 | get 78 | { 79 | if (props == null) 80 | { 81 | var elements = 82 | from element in Shape.Descendants() 83 | where element.Name.LocalName == "Section" && element.Attribute("N").Value == "Property" 84 | select element; 85 | if (!elements.Any()) 86 | { 87 | props = new XElement("Section"); 88 | props.SetAttributeValue("N", "Property"); 89 | Shape.Add(props); 90 | } 91 | else 92 | props = elements.FirstOrDefault(); 93 | } 94 | 95 | return props; 96 | } 97 | } 98 | 99 | protected void AddProp(string name, string value) 100 | { 101 | Props.Add(XElement.Parse(" ")); 102 | } 103 | 104 | protected void AddType(string value) 105 | { 106 | AddProp("ActionType", value); 107 | } 108 | 109 | protected void AddType() 110 | { 111 | AddType(Property.Value["type"].ToString()); 112 | } 113 | 114 | protected void AddName(string value) 115 | { 116 | AddProp("ActionName", value); 117 | } 118 | 119 | protected void AddName() 120 | { 121 | AddProp("ActionName", PropertyName); 122 | } 123 | 124 | public void AddText(string text) 125 | { 126 | var textElement = Shape.Descendants().Where(el => el.Name.LocalName == "Text").First(); 127 | var sb = new StringBuilder(); 128 | 129 | if (Utils.Display.ShowTrackingID && Property?.Value["correlation"]?["clientTrackingId"] != null) 130 | sb.AppendLine("Client Tracking ID: " + Property.Value["correlation"]["clientTrackingId"]).ToString(); 131 | 132 | if (Utils.Display.ShowConCurrency && Property?.Value["runtimeConfiguration"]?["concurrency"]?["runs"] != null) 133 | sb.AppendLine("ConCurrency: " + Property.Value["runtimeConfiguration"]["concurrency"]["runs"]).ToString(); 134 | if (Utils.Display.ShowSecure && Property?.Value["runtimeConfiguration"]?["secureData"]?["properties"] != null) 135 | { 136 | if (((JArray)(Property.Value["runtimeConfiguration"]["secureData"]["properties"])).Select(jt => jt.ToString()).ToList().Any(st => st == "inputs")) sb.AppendLine("Secure Inputs: true"); 137 | if (((JArray)(Property.Value["runtimeConfiguration"]["secureData"]["properties"])).Select(jt => jt.ToString()).ToList().Any(st => st == "outputs")) sb.AppendLine("Secure Outputs: true"); 138 | } 139 | if (Utils.Display.ShowTriggers && Property?.Value["conditions"] != null) 140 | { 141 | sb.AppendLine("Triggers:"); 142 | ((JArray)Property.Value["conditions"]).Children().ToList().ForEach(jt => sb.AppendLine(jt["expression"].ToString())); 143 | } 144 | 145 | if (Utils.Display.ShowTrackedProps && Property?.Value["trackedProperties"] != null) 146 | { 147 | sb.AppendLine("Tracked Properties:"); 148 | (Property.Value["trackedProperties"]).Children().ToList().ForEach(jp => sb.AppendLine(jp.Name + " : " + jp.Value.ToString())); 149 | } 150 | if (Property?.Value["description"] != null) sb.AppendLine("Comment: " + Property.Value["description"]); 151 | sb.AppendLine(text).ToString(); 152 | textElement.ReplaceWith(XElement.Parse("")); 153 | } 154 | 155 | protected void AddText(StringBuilder sb) 156 | { 157 | AddText(sb.ToString()); 158 | } 159 | 160 | public Action EndAction 161 | { 162 | get 163 | { 164 | if (endAction == null) return this; 165 | return endAction; 166 | } 167 | protected set => endAction = value; 168 | } 169 | 170 | public Action Parent; 171 | 172 | public Action(Action parent) : base() 173 | { 174 | Utils.actionCount++; 175 | Parent = parent; 176 | } 177 | 178 | public Action(JProperty property) : this(property, null, 0, 0, "Default") 179 | { 180 | AddBaseText(); 181 | } 182 | 183 | public Action(JProperty property, Action parent, int current, int children) : this(property, parent, current, children, "Default") 184 | { 185 | AddBaseText(); 186 | } 187 | 188 | public Action(JProperty property, Action parent, int current, int children, string templateName) : base(property) 189 | { 190 | Parent = parent; 191 | Shape = new XElement(GetTemplateShape(templateName)); 192 | Utils.actionCount++; 193 | 194 | this.current = current; 195 | this.children = children; 196 | Shape.SetAttributeValue("NameU", property.Name); 197 | if (parent != null) 198 | 199 | AddRunAfter(); 200 | else 201 | { 202 | // Variant to allow for international useage 203 | PinX = double.TryParse(Shape.Elements().First(el => el.Attribute("N").Value == "PinX").Attribute("V").Value, NumberStyles.Any, 204 | CultureInfo.InvariantCulture, out var tempPinX) ? tempPinX : 0.0; 205 | PinY = double.TryParse(Shape.Elements().First(el => el.Attribute("N").Value == "PinY").Attribute("V").Value, NumberStyles.Any, 206 | CultureInfo.InvariantCulture, out var tempPiny) ? tempPiny : 0.0; 207 | } 208 | 209 | // if (this is Action) AddBaseText(); 210 | } 211 | 212 | private void AddRunAfter() 213 | { 214 | if (Property.Value["runAfter"] != null && Property.Value["runAfter"].HasValues) // #2 Added check for null 215 | { 216 | var runAfterString = Property.Value["runAfter"].Children().First().Value().Value.Where(jt => jt.ToString() != "Succeeded").Aggregate(string.Empty, (accumulator, jToken) => accumulator += jToken + " | "); 217 | 218 | if (runAfterString != string.Empty) 219 | { 220 | runAfterString = runAfterString.Substring(0, runAfterString.Length - 3); 221 | var header = new CaseAction(Parent, current, children, PropertyName + runAfterString + current); 222 | header.AddName(runAfterString); 223 | header.Props.Add(XElement.Parse(" ")); 224 | header.AddFillColour("255,242,204"); 225 | Parent = header; 226 | current = 1; 227 | children = 1; 228 | } 229 | } 230 | CalcPosition(); 231 | var line = new Line(); 232 | line.Connect(Parent.EndAction, this, current, children); 233 | } 234 | 235 | public Action() : base() 236 | { 237 | Utils.actionCount++; 238 | } 239 | 240 | public Action(string templateName, double pinX, double pinY) 241 | { 242 | Shape = new XElement(GetTemplateShape(templateName)); 243 | Utils.actionCount++; 244 | Shape.SetAttributeValue("NameU", templateName + Utils.actionCount); 245 | PinX = pinX; 246 | PinY = pinY; 247 | SetPosition(); 248 | } 249 | 250 | private void AddBaseText() 251 | { 252 | Props.Add(XElement.Parse(" ")); 253 | Props.Add(XElement.Parse(" ")); 254 | // var sb = "" + PropertyName + "\n"; var 255 | // textElement = Shape.Descendants().Where(el => el.Name.LocalName == "Text").First(); 256 | var sb = new StringBuilder("Properties: "); 257 | sb.AppendLine(); 258 | if (((JObject)Property.Value).Properties().Count() > 0) 259 | foreach (var item in ((JObject)Property.Value).Properties().Where(p => p.Name != "runAfter")) 260 | sb.AppendLine(item.Name + " : " + @item.Value); 261 | AddText(sb); 262 | } 263 | 264 | public void AddFillColour(string colour) 265 | { 266 | Shape.Add(XElement.Parse("")); 267 | } 268 | } 269 | } -------------------------------------------------------------------------------- /FlowToVisio/Visio/VisionGen.Base.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.IO.Packaging; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Xml; 8 | using System.Xml.Linq; 9 | using XrmToolBox.Extensibility; 10 | 11 | namespace LinkeD365.FlowToVisio 12 | { 13 | public partial class FlowToVisioControl : PluginControlBase 14 | { 15 | private void CreateNewPage(Package filePackage, PackagePart parent, 16 | XDocument partXML, Uri packageLocation, string contentType, 17 | string relationship, string name) 18 | { 19 | // Need to check first to see whether the part exists already. 20 | 21 | if (!filePackage.PartExists(packageLocation)) 22 | { 23 | // Create a new blank package part at the specified URI of the specified content type. 24 | PackagePart newPackagePart = filePackage.CreatePart(packageLocation, 25 | contentType); 26 | // Create a stream from the package part and save the XML document to the package part. 27 | using (Stream partStream = newPackagePart.GetStream(FileMode.Create, 28 | FileAccess.ReadWrite)) 29 | { 30 | partXML.Save(partStream); 31 | } 32 | } 33 | // Add a relationship from the file package to this package part. You can also create 34 | // relationships between an existing package part and a new part. 35 | var rel = parent.CreateRelationship(packageLocation, 36 | TargetMode.Internal, 37 | relationship); 38 | 39 | var pagesXml = GetXMLFromPart(parent); 40 | var templatePage = pagesXml.Root.Elements().First(ele => ele.Attribute("NameU").Value == "Page-1"); 41 | var pageXML = new XElement(templatePage); 42 | pageXML.SetAttributeValue("Name", name); 43 | pageXML.SetAttributeValue("NameU", name); 44 | pageXML.SetAttributeValue("IsCustomNameU", "1"); 45 | pageXML.SetAttributeValue("IsCustomName", "1"); 46 | pageXML.SetAttributeValue("ID", templatePage.Parent.Elements().Count()); 47 | XNamespace ns = pagesXml.Root.GetNamespaceOfPrefix("r"); 48 | pageXML.Elements().First(el => el.Name.LocalName == "Rel").SetAttributeValue(ns + "id", rel.Id); 49 | templatePage.Parent.Add(new XElement(pageXML)); 50 | SaveXDocumentToPart(parent, pagesXml); 51 | } 52 | 53 | private void RemoveTemplate() 54 | { 55 | package.DeletePart(page.Uri); 56 | 57 | var pagesXML = GetXMLFromPart(pages); 58 | pagesXML.Root.Elements().First(ele => ele.Attribute("NameU").Value == "Page-1").Remove(); 59 | 60 | Uri pagesURI = pages.Uri; 61 | string contentType = pages.ContentType; 62 | 63 | var relationships = pages.GetRelationships().Where(rel => rel.Id != "rId1"); 64 | package.DeletePart(pages.Uri); 65 | PackagePart newPages = package.CreatePart(pagesURI, contentType); 66 | foreach (var relationship in relationships) 67 | { 68 | newPages.CreateRelationship(relationship.TargetUri, TargetMode.Internal, relationship.RelationshipType, relationship.Id); 69 | } 70 | SaveXDocumentToPart(newPages, pagesXML); 71 | } 72 | 73 | private static int CheckForRecalc(XDocument customPropsXDoc) 74 | { 75 | // Set the inital pidValue to -1, which is not an allowed value. The calling code tests 76 | // to see whether the pidValue is greater than -1. 77 | int pidValue = -1; 78 | // Get all of the property elements from the document. 79 | IEnumerable props = GetXElementsByName( 80 | customPropsXDoc, "property"); 81 | // Get the RecalcDocument property from the document if it exists already. 82 | XElement recalcProp = GetXElementByAttribute(props, 83 | "name", "RecalcDocument"); 84 | // If there is already a RecalcDocument instruction in the Custom File Properties part, 85 | // then we don't need to add another one. Otherwise, we need to create a unique pid value. 86 | if (recalcProp != null) 87 | return pidValue; 88 | else 89 | { 90 | // Get all of the pid values of the property elements and then convert the 91 | // IEnumerable object into an array. 92 | IEnumerable propIDs = 93 | from prop in props 94 | where prop.Name.LocalName == "property" 95 | select prop.Attribute("pid").Value; 96 | string[] propIDArray = propIDs.ToArray(); 97 | // Increment this id value until a unique value is found. This starts at 2, because 98 | // 0 and 1 are not valid pid values. 99 | int id = 2; 100 | while (pidValue == -1) 101 | if (propIDArray.Contains(id.ToString())) 102 | id++; 103 | else 104 | pidValue = id; 105 | } 106 | return pidValue; 107 | } 108 | 109 | private static void RecalcDocument(Package filePackage) 110 | { 111 | // Get the Custom File Properties part from the package and and then extract the XML 112 | // from it. 113 | PackagePart customPart = GetPackagePart(filePackage, 114 | "http://schemas.openxmlformats.org/officeDocument/2006/relationships/" + 115 | "custom-properties"); 116 | XDocument customPartXML = GetXMLFromPart(customPart); 117 | // Check to see whether document recalculation has already been set for this document. 118 | // If it hasn't, use the integer value returned by CheckForRecalc as the property ID. 119 | int pidValue = CheckForRecalc(customPartXML); 120 | if (pidValue > -1) 121 | { 122 | XElement customPartRoot = customPartXML.Elements().ElementAt(0); 123 | // Two XML namespaces are needed to add XML data to this document. Here, we're using 124 | // the GetNamespaceOfPrefix and GetDefaultNamespace methods to get the namespaces 125 | // that we need. You can specify the exact strings for the namespaces, but that is 126 | // not recommended. 127 | XNamespace customVTypesNS = customPartRoot.GetNamespaceOfPrefix("vt"); 128 | XNamespace customPropsSchemaNS = customPartRoot.GetDefaultNamespace(); 129 | // Construct the XML for the new property in the XDocument.Add method. This ensures 130 | // that the XNamespace objects will resolve properly, apply the correct prefix, and 131 | // will not default to an empty namespace. 132 | customPartRoot.Add( 133 | new XElement(customPropsSchemaNS + "property", 134 | new XAttribute("pid", pidValue.ToString()), 135 | new XAttribute("name", "RecalcDocument"), 136 | new XAttribute("fmtid", 137 | "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"), 138 | new XElement(customVTypesNS + "bool", "true") 139 | )); 140 | } 141 | // Save the Custom Properties package part back to the package. 142 | SaveXDocumentToPart(customPart, customPartXML); 143 | } 144 | 145 | private static void SaveXDocumentToPart(PackagePart packagePart, 146 | XDocument partXML) 147 | { 148 | // Create a new XmlWriterSettings object to define the characteristics for the XmlWriter 149 | XmlWriterSettings partWriterSettings = new XmlWriterSettings(); 150 | partWriterSettings.Encoding = Encoding.UTF8; 151 | // packagePart.GetStream().Flush(); Create a new XmlWriter and then write the XML back 152 | // to the document part. 153 | XmlWriter partWriter = XmlWriter.Create(packagePart.GetStream(), 154 | partWriterSettings); 155 | 156 | partXML.WriteTo(partWriter); 157 | // Flush and close the XmlWriter. 158 | partWriter.Flush(); 159 | partWriter.Close(); 160 | } 161 | 162 | private static PackagePart GetPackagePart(Package filePackage, 163 | string relationship) 164 | { 165 | // Use the namespace that describes the relationship to get the relationship. 166 | PackageRelationship packageRel = 167 | filePackage.GetRelationshipsByType(relationship).FirstOrDefault(); 168 | PackagePart part = null; 169 | // If the Visio file package contains this type of relationship with one of its parts, 170 | // return that part. 171 | if (packageRel != null) 172 | { 173 | // Clean up the URI using a helper class and then get the part. 174 | Uri docUri = PackUriHelper.ResolvePartUri( 175 | new Uri("/", UriKind.Relative), packageRel.TargetUri); 176 | part = filePackage.GetPart(docUri); 177 | } 178 | return part; 179 | } 180 | 181 | private static PackagePart GetPackagePart(Package filePackage, 182 | PackagePart sourcePart, string relationship) 183 | { 184 | // This gets only the first PackagePart that shares the relationship with the 185 | // PackagePart passed in as an argument. You can modify the code here to return a 186 | // different PackageRelationship from the collection. 187 | PackageRelationship packageRel = 188 | sourcePart.GetRelationshipsByType(relationship).FirstOrDefault(); 189 | PackagePart relatedPart = null; 190 | PackagePart newPart = null; 191 | if (packageRel != null) 192 | { 193 | // Use the PackUriHelper class to determine the URI of PackagePart that has the 194 | // specified relationship to the PackagePart passed in as an argument. 195 | Uri partUri = PackUriHelper.ResolvePartUri( 196 | sourcePart.Uri, packageRel.TargetUri); 197 | relatedPart = filePackage.GetPart(partUri); 198 | } 199 | return relatedPart; 200 | } 201 | 202 | private static XDocument GetXMLFromPart(PackagePart packagePart) 203 | { 204 | XDocument partXml = null; 205 | // Open the packagePart as a stream and then open the stream in an XDocument object. 206 | Stream partStream = packagePart.GetStream(); 207 | partXml = XDocument.Load(partStream, LoadOptions.PreserveWhitespace); 208 | return partXml; 209 | } 210 | 211 | private static IEnumerable GetXElementsByName( 212 | XDocument packagePart, string elementType) 213 | { 214 | // Construct a LINQ query that selects elements by their element type. 215 | IEnumerable elements = 216 | from element in packagePart.Descendants() 217 | where element.Name.LocalName == elementType 218 | select element; 219 | // Return the selected elements to the calling code. 220 | return elements.DefaultIfEmpty(null); 221 | } 222 | 223 | private static XElement GetXElementByAttribute(IEnumerable elements, 224 | string attributeName, string attributeValue) 225 | { 226 | // Construct a LINQ query that selects elements from a group of elements by the value of 227 | // a specific attribute. 228 | IEnumerable selectedElements = 229 | from el in elements 230 | where el.Attribute(attributeName).Value == attributeValue 231 | select el; 232 | // If there aren't any elements of the specified type with the specified attribute value 233 | // in the document, return null to the calling code. 234 | return selectedElements.DefaultIfEmpty(null).FirstOrDefault(); 235 | } 236 | } 237 | } -------------------------------------------------------------------------------- /FlowToVisio/Resources/VisioTemplate - Copy/visio/masters/master8.xml: -------------------------------------------------------------------------------- 1 | 2 |
adf 3 |
test type 4 |
-------------------------------------------------------------------------------- /FlowToVisio/FlowToVisioControl.cs: -------------------------------------------------------------------------------- 1 | using LinkeD365.FlowToVisio.Properties; 2 | using McTools.Xrm.Connection; 3 | using Microsoft.Xrm.Sdk; 4 | using Microsoft.Xrm.Sdk.Query; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Net.Http; 9 | using System.Windows.Forms; 10 | using XrmToolBox.Extensibility; 11 | using XrmToolBox.Extensibility.Interfaces; 12 | 13 | namespace LinkeD365.FlowToVisio 14 | { 15 | public partial class FlowToVisioControl : PluginControlBase, IGitHubPlugin, INoConnectionRequired, IPayPalPlugin 16 | { 17 | private bool overrideSave; 18 | 19 | public string RepositoryName => "FlowToVisio"; 20 | public string UserName => "LinkeD365"; 21 | 22 | public string DonationDescription => "Flow to Visio Fans"; 23 | 24 | public string EmailAccount => "carl.cookson@gmail.com"; 25 | 26 | public FlowToVisioControl() 27 | { 28 | InitializeComponent(); 29 | } 30 | 31 | private void FlowToVisioControl_Load(object sender, EventArgs e) 32 | { 33 | try 34 | { 35 | if (SettingsManager.Instance.TryLoad(GetType(), out FlowConnection flowConnection)) 36 | { 37 | LogWarning("Old settings file found, converting"); 38 | aPIConnections = new APIConns(); 39 | if (!string.IsNullOrEmpty(flowConnection.TenantId)) 40 | { 41 | aPIConnections.FlowConns 42 | .Add( 43 | new FlowConn 44 | { 45 | AppId = flowConnection.AppId, 46 | TenantId = flowConnection.TenantId, 47 | Environment = flowConnection.Environment, 48 | ReturnURL = flowConnection.ReturnURL, 49 | UseDev = flowConnection.UseDev, 50 | Name = "Flow Connection" 51 | }); 52 | } 53 | if (!string.IsNullOrEmpty(flowConnection.SubscriptionId)) 54 | { 55 | aPIConnections.LogicAppConns 56 | .Add( 57 | new LogicAppConn 58 | { 59 | AppId = flowConnection.LAAppId, 60 | TenantId = flowConnection.LATenantId, 61 | ReturnURL = flowConnection.LAReturnURL, 62 | SubscriptionId = flowConnection.SubscriptionId, 63 | UseDev = flowConnection.UseDev, 64 | Name = "LA Connection" 65 | }); 66 | } 67 | 68 | return; 69 | } 70 | } 71 | catch (Exception) 72 | { 73 | } 74 | if (!SettingsManager.Instance.TryLoad(GetType(), out aPIConnections)) 75 | { 76 | aPIConnections = new APIConns(); 77 | 78 | LogWarning("Settings not found => a new settings file has been created!"); 79 | } 80 | } 81 | 82 | private void tsbClose_Click(object sender, EventArgs e) 83 | { 84 | CloseTool(); 85 | } 86 | 87 | /// 88 | /// This event occurs when the connection has been 89 | /// updated in XrmToolBox 90 | /// 91 | public override void UpdateConnection(IOrganizationService newService, ConnectionDetail detail, string actionName, object parameter) 92 | { 93 | base.UpdateConnection(newService, detail, actionName, parameter); 94 | 95 | if (flowConn != null && detail != null) 96 | { 97 | // mySettings.LastUsedOrganizationWebappUrl 98 | // = detail.WebApplicationUrl; 99 | LogInfo("Connection has changed to: {0}", detail.WebApplicationUrl); 100 | } 101 | 102 | LoadFlows(); 103 | LoadSolutions(); 104 | } 105 | 106 | private void btnCreateVisio_Click(object sender, EventArgs e) 107 | { 108 | var selectedFlows = grdFlows.SelectedRows; 109 | 110 | if (selectedFlows.Count == 0) return; 111 | 112 | Utils.Display = aPIConnections.Display; 113 | if (selectedFlows.Count == 1) 114 | { 115 | var selectFlow = ((FlowDefinition)grdFlows.SelectedRows[0].DataBoundItem); 116 | 117 | saveDialog.FileName = selectFlow.Name + ".vsdx"; 118 | if (saveDialog.ShowDialog() != DialogResult.OK) 119 | { 120 | return; 121 | } 122 | 123 | if (selectFlow.Solution) 124 | { 125 | PopulateComment(selectFlow); 126 | // flowObject 127 | // = JObject.Parse(selectFlow.Definition); 128 | GenerateVisio(saveDialog.FileName, selectFlow, 1); 129 | } 130 | else 131 | { 132 | LoadFlow(selectFlow, saveDialog.FileName, 1); 133 | } 134 | } 135 | else 136 | { 137 | if (!splitTop.Panel2Collapsed && ddlSolutions.SelectedIndex > 0) 138 | { 139 | saveDialog.FileName = ddlSolutions.Text + ".vsdx"; 140 | } 141 | else saveDialog.FileName = "My Flows.vsdx"; 142 | if (saveDialog.ShowDialog() != DialogResult.OK) 143 | { 144 | return; 145 | } 146 | int flowCount = 0; 147 | foreach (DataGridViewRow selectedRow in selectedFlows) 148 | { 149 | flowCount++; 150 | var selFlow = (FlowDefinition)selectedRow.DataBoundItem; 151 | if (selFlow.Solution) 152 | { 153 | PopulateComment(selFlow); 154 | GenerateVisio(saveDialog.FileName, selFlow, flowCount, false); 155 | } 156 | else 157 | { 158 | LoadFlow(selFlow, saveDialog.FileName, flowCount); 159 | } 160 | } 161 | } 162 | CompleteVisio(saveDialog.FileName); 163 | } 164 | 165 | public List Sort(List input, string property) 166 | { 167 | var type = typeof(T); 168 | var sortProperty = type.GetProperty(property); 169 | return input.OrderBy(p => sortProperty.GetValue(p, null)).ToList(); 170 | } 171 | 172 | private SortOrder GetSortOrder(int columnIndex) 173 | { 174 | if (grdFlows.Columns[columnIndex].HeaderCell.SortGlyphDirection == SortOrder.None || 175 | grdFlows.Columns[columnIndex].HeaderCell.SortGlyphDirection == SortOrder.Descending) 176 | { 177 | grdFlows.Columns[columnIndex].HeaderCell.SortGlyphDirection = SortOrder.Ascending; 178 | return SortOrder.Ascending; 179 | } 180 | else 181 | { 182 | grdFlows.Columns[columnIndex].HeaderCell.SortGlyphDirection = SortOrder.Descending; 183 | return SortOrder.Descending; 184 | } 185 | } 186 | 187 | private void grdFlows_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e) 188 | { 189 | if (grdFlows.Columns[e.ColumnIndex].SortMode == DataGridViewColumnSortMode.NotSortable) return; 190 | SortOrder sortOrder = GetSortOrder(e.ColumnIndex); 191 | 192 | SortGrid(grdFlows.Columns[e.ColumnIndex].Name, sortOrder); 193 | // string strColumnName 194 | // = grdFlows.Columns[e.ColumnIndex].Name; 195 | } 196 | 197 | private void SortGrid(string name, SortOrder sortOrder) 198 | { 199 | List sortingFlows = (List)grdFlows.DataSource; 200 | sortingFlows.Sort(new FlowDefComparer(name, sortOrder)); 201 | grdFlows.DataSource = null; 202 | grdFlows.DataSource = sortingFlows; 203 | InitGrid(); 204 | grdFlows.Columns[name].HeaderCell.SortGlyphDirection = sortOrder; 205 | } 206 | 207 | private void textSearch_TextChanged(object sender, EventArgs e) 208 | { 209 | //gridFlows.DataSource = null; 210 | if (!string.IsNullOrEmpty(textSearch.Text)) 211 | { 212 | grdFlows.DataSource = flows.Where(flw => flw.Name.ToLower().Contains(textSearch.Text.ToLower())).ToList();//.Entities.Where(ent => ent.Attributes["name"].ToString().ToLower().Contains(textSearch.Text)); 213 | } 214 | else 215 | { 216 | grdFlows.DataSource = flows; 217 | } 218 | } 219 | 220 | private void toolStripButton1_Click(object sender, EventArgs e) 221 | { 222 | //GetClient(); 223 | LoadUnSolutionedFlows(); 224 | } 225 | 226 | private void PopulateComment(FlowDefinition selectFlow) 227 | { 228 | if (!selectFlow.Solution) return; 229 | if (!aPIConnections.Display.ShowComments) return; 230 | 231 | var fetchXml = $@" 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | "; 247 | var fe = new FetchExpression(fetchXml); 248 | var comments = Service.RetrieveMultiple(fe); 249 | 250 | Utils.Comments = comments.Entities.Select(com => new Comment(com)).OrderBy(cmt => cmt.Kind).ToList(); 251 | } 252 | 253 | private HttpClient _client; 254 | 255 | private void btnConnectCDS_Click(object sender, EventArgs e) 256 | { 257 | ExecuteMethod(LoadFlows); 258 | ExecuteMethod(LoadSolutions); 259 | } 260 | 261 | private void InitGrid() 262 | { 263 | grdFlows.AutoResizeColumns(); 264 | grdFlows.Columns["Name"].SortMode = DataGridViewColumnSortMode.Automatic; 265 | grdFlows.Columns["Description"].AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader; 266 | 267 | grdFlows.Columns["Managed"].SortMode = DataGridViewColumnSortMode.Automatic; 268 | 269 | if (btnConnectLogicApps.Visible) 270 | { 271 | if (grdFlows.Columns["history"] != null) 272 | { 273 | var histCol = grdFlows.Columns["history"]; 274 | histCol.DisplayIndex = grdFlows.ColumnCount - 1; 275 | } 276 | else 277 | { 278 | DataGridViewImageColumn history = new DataGridViewImageColumn(); 279 | history.Width = 20; 280 | history.HeaderText = "History"; 281 | history.Name = "history"; 282 | history.AutoSizeMode = DataGridViewAutoSizeColumnMode.None; 283 | history.Image = Resources.powerautomate__Custom_; 284 | history.ImageLayout = DataGridViewImageCellLayout.Normal; 285 | grdFlows.Columns.Add(history); 286 | } 287 | } 288 | } 289 | 290 | private void btnConnectLogicApps_Click(object sender, EventArgs e) 291 | { 292 | LoadLogicApps(); 293 | } 294 | 295 | private void chkShowConCurrency_CheckedChanged(object sender, EventArgs e) 296 | { 297 | aPIConnections.Display.ShowConCurrency = chkShowConCurrency.Checked; 298 | } 299 | 300 | private void chkShowConCurrency_Click(object sender, EventArgs e) 301 | { 302 | } 303 | 304 | private void chkShowTrackedProps_CheckedChanged(object sender, EventArgs e) 305 | { 306 | aPIConnections.Display.ShowTrackedProps = chkShowTrackedProps.Checked; 307 | } 308 | 309 | private void chkShowTriggerConditions_CheckedChanged(object sender, EventArgs e) 310 | { 311 | aPIConnections.Display.ShowTriggers = chkShowTriggerConditions.Checked; 312 | } 313 | 314 | private void chkShowSecure_CheckedChanged(object sender, EventArgs e) 315 | { 316 | aPIConnections.Display.ShowSecure = chkShowSecure.Checked; 317 | } 318 | 319 | private void chkShowCustomTracking_CheckedChanged(object sender, EventArgs e) 320 | { 321 | aPIConnections.Display.ShowTrackingID = chkShowCustomTracking.Checked; 322 | } 323 | 324 | private void chkShowComments_CheckedChanged(object sender, EventArgs e) 325 | { 326 | aPIConnections.Display.ShowComments = chkShowComments.Checked; 327 | } 328 | 329 | private void ddlSolutions_SelectedIndexChanged(object sender, EventArgs e) 330 | { 331 | ExecuteMethod(GetFlowsForSolution); 332 | } 333 | 334 | private void grdFlows_CellClick(object sender, DataGridViewCellEventArgs e) 335 | { 336 | if (grdFlows.Columns[e.ColumnIndex].Name == "history" && e.RowIndex >= 0) 337 | { 338 | FlowDefinition flow = grdFlows.Rows[e.RowIndex].DataBoundItem as FlowDefinition; 339 | GetAllFlowRuns(flow); 340 | } 341 | } 342 | 343 | private void grdFlows_CellMouseEnter(object sender, DataGridViewCellEventArgs e) 344 | { 345 | grdFlows.Cursor = e.ColumnIndex < 0 ? Cursors.Default : (grdFlows.Columns[e.ColumnIndex].Name == "history") ? Cursors.Hand : Cursors.Default; 346 | } 347 | } 348 | } --------------------------------------------------------------------------------