├── 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 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------
/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 | 
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 | 
22 |
23 | Find the Visio and open it up.
24 |
25 | 
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 | 
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 | 
40 |
41 | Environment Id will be displayed when you navigate to the Flow in My Flows
42 |
43 | 
44 |
45 | > ## **The Return URL needs to be specified as a Mobile and Desktop application**
46 |
47 | 
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 | 
56 |
57 | In the application, select the Connect to Logic Apps button
58 |
59 | 
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 | 
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 | 
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 | }
--------------------------------------------------------------------------------