diff --git a/content/en/docs/apidocs-mxsdk/apidocs/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/_index.md index 80899c05b91..0df2e49a7a6 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/_index.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/_index.md @@ -9,6 +9,7 @@ aliases: - /apidocs/ - /apidocs-mxsdk/apidocs/authentication/ - /apidocs/index.html + - /apidocs-mxsdk/apidocs/runtime-api/ #If moving or renaming this doc file, implement a temporary redirect and let the respective team know they should update the URL in the product. See Mapping to Products for more details. --- diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/_index.md new file mode 100644 index 00000000000..d661fa7647b --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/_index.md @@ -0,0 +1,11 @@ +--- +title: "APIs for Studio Pro 10" +url: /apidocs-mxsdk/apidocs/apis-for-studio-pro-10/ +no_list: false +description_list: true +description: "Provides the documentation of APIs for Studio Pro 10, including Extensibility API and Mendix Runtime API." +weight: 5 +linktitle: "Studio Pro 10" +--- + +Mendix offers the following APIs for Studio Pro 10: diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/_index.md similarity index 95% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/_index.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/_index.md index 5150d1e36c0..7820a590789 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/_index.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/_index.md @@ -1,6 +1,6 @@ --- title: "Extensibility API" -url: /apidocs-mxsdk/apidocs/extensibility-api/ +url: /apidocs-mxsdk/apidocs/extensibility-api-10/ description: "The Extensibility API allows you to extend Studio Pro by adding custom functionality." weight: 57 no_list: false diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/_index.md new file mode 100644 index 00000000000..883ba26b43d --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/_index.md @@ -0,0 +1,65 @@ +--- +title: "Extensibility API for C# Developers" +linktitle: "C# Extensibility API" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-10/ +description: "The C# extensibility API allows your custom Studio Pro extensions developed in C# to interact with some internal services of Studio Pro." +weight: 10 +no_list: false +description_list: true +aliases: + - /apidocs-mxsdk/apidocs/extensibility-api/ +--- + +{{% alert color="warning" %}} +This feature is in beta. For more information, see [Beta Releases](/releasenotes/beta-features/). +{{% /alert %}} + +{{% alert color="info" %}} +For information on new releases of the Extensibility API, see [Extensibility: C# API Release Notes](/releasenotes/studio-pro/csharp-extensibility-api/). +{{% /alert %}} + +## Introduction + +Extensions can be written in C#, described here, or using a web API which is documented separately in [Extensibility API for Web Developers](/apidocs-mxsdk/apidocs/web-extensibility-api-10/). + +If you need to add your own custom UI to Studio Pro, you can achieve this using web technology. Your web-based UI will be rendered in Studio Pro using a hosted web view, the API provides communication functionality between your web UI and the C# extension logic. + +## Prerequisites + +* You need at least a basic understanding of the Mendix platform. +* You need some understanding of the Mendix Model. +* You need to have some C# development experience. Extensions are developed using [C#](https://docs.microsoft.com/en-us/dotnet/), and compiled into a `.dll` assembly file. + +## Getting Started + +For detailed explanation on how to get started with extensions, check out [Get Started with the Extensibility API](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/get-started/). + +You can also check out our examples and [API reference documentation](https://github.com/mendix/ExtensionAPI-Samples). + +## How-tos + +Here is a list of how-tos for you to begin with: + +* [How to Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-menu-extension/) +* [How to Create a Dockable Pane Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-dockable-pane-extension/) +* [How to Create a Context Menu Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-context-menu/) +* [How to Create a Web View Hosted Inside a Modal Dialog Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-modal-web-view/) +* [How to Create Microflows for Calculations Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-microflows-for-calculations/) + +## Advanced APIs + +APIs for the Mendix platform's advanced users: + +* [Use the Untyped Model Access API Using C#](/apidocs-mxsdk/apidocs/untyped-model-access-api/) + +## Learn More + +You can dive into the following topics in depth: + +* [What are extension points](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/extension-points/) +* [What are the Extensibility API services](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/services/) +* [How to Interact with the Model API Using C#](/apidocs-mxsdk/apidocs/interact-with-model-api/) +* [How to host web content via a web view wrapper](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/web-views/) +* [How to Build a Todo Example Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/build-todo-example-extension/) + +## Documentation in This Category diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/_index.md new file mode 100644 index 00000000000..5fcf9f96543 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/_index.md @@ -0,0 +1,12 @@ +--- +title: "C# Extensibility API How-tos" +linktitle: "How-tos" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-10/how-tos/ +weight: 3 +no_list: false +description_list: true +--- + +## Introduction + +The following how-tos teach you how to use the Extensibility API for C# Developers in different use cases. \ No newline at end of file diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/add-menu.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/add-menu.md new file mode 100644 index 00000000000..5d39afc4c57 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/add-menu.md @@ -0,0 +1,66 @@ +--- +title: "Add Menus and Submenus to Studio Pro Using C#" +linktitle: "Structured Menus" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-10/add-menu/ +weight: 15 +--- + +## Introduction + +This how-to describes how you can add a menu that contains submenus, some of which also contain submenus of their own. Before you start this how-to, it is recommended to [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-menu-extension/) first. + +You can download the example in this how-to in [this GitHub repository](https://github.com/mendix/ExtensionAPI-Samples). + +## Creating Menu Extension Class + +1. Open the project that you previously created when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-menu-extension/). +2. Add a new class to the project and name it `MyMenuExtension.cs`. +3. Replace the code in the file with the following code: + + ```csharp + using Mendix.StudioPro.ExtensionsAPI.UI.Menu; + using Mendix.StudioPro.ExtensionsAPI.UI.Services; + using System.ComponentModel.Composition; + + namespace MyCompany.MyProject.MendixExtension; + + [Export(typeof(MenuExtension))] + public class MyMenuExtension : MenuExtension + { + readonly IMessageBoxService messageBoxService; + + [ImportingConstructor] + public MyMenuExtension(IMessageBoxService messageBoxService) + { + this.messageBoxService = messageBoxService; + } + + public override IEnumerable GetMenus() + { + var ristretto = new MenuViewModel("Ristretto", () => messageBoxService.ShowInformation("Ristretto")); + var regularExpresso = new MenuViewModel("Regular Espresso", () => messageBoxService.ShowInformation("Regular Espresso")); + var espresso = new MenuViewModel("Espresso", [regularExpresso, ristretto]); + var blackCoffee = new MenuViewModel("Black Coffee", () => messageBoxService.ShowInformation("Black Coffee")); + var decaf = new MenuViewModel("Decaf", () => messageBoxService.ShowInformation("Decaf")) { Separator = MenuSeparator.After }; + var coffee = new MenuViewModel("Coffee", [blackCoffee, decaf, espresso]); + + var tea = new MenuViewModel("Tea", () => messageBoxService.ShowInformation("Tea")); + + var hot = new MenuViewModel("Hot", [coffee, tea]); + + var soda = new MenuViewModel("Soda", () => messageBoxService.ShowInformation("Soda")); + var cold = new MenuViewModel("Cold", [soda]); + + var beverages = new MenuViewModel("Beverages", [hot, cold]); + yield return beverages; + } + } + ``` + +The code above creates a single menu, `Beverages`, which contains the submenus `Hot` and `Cold`, both of which contain some submenus. Note that when you are creating this menu structure, you only return the main parent menu (in this example, the `Beverages` menu) from the `GetMenus` method. You should only return the topmost parents in your list, so only the ones that do not have a parent should be returned. In the example above, there is only one. + +If an app contains one or more extensions, a top-level menu named `Extensions` will appear in the main menu bar of Studio Pro. Menus that are created from `MenuExtension` implementations will be grouped by their extension entry point name (in this example, `MyCompany`), and then placed under their own dedicated submenu under the main `Extensions` top-level menu. For example, the `MyMenuExtension` above will be placed as follows: **Extensions** > **MyCompany** > **MyMenuExtension**. + +A menu can only be a parent (namely, a menu that contains submenus) or have an action. You cannot create a menu with an action which also contains submenus. + +You can add a `MenuSeparator` to a menu, via the `Separator` property. Options are `After`, `Before` or `None`. The default value is `None`. You can also disable a menu by setting its `IsEnabled` property to `false`. Menus are enabled by default. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/build-todo-example-extension.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/build-todo-example-extension.md new file mode 100644 index 00000000000..28122a4db4d --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/build-todo-example-extension.md @@ -0,0 +1,920 @@ +--- +title: "Build a Todo Example Extension Using C#" +linktitle: "ToDo Example" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-10/build-todo-example-extension/ +weight: 20 +--- + +## Introduction + +This document describes how to build an example extension that adds a simple todo list extension to Studio Pro. With this example extension, you can add new todo items to a list. The example extension will be added to the main menu of Studio Pro and you will add a user interface for the example extension by using a dockable pane and some web content. + +This document covers the following topics: + +* How to create an extension project and configure it for use as an extension in Studio Pro +* How to create a web-based user interface for Studio Pro +* How to store information in a local storage JSON file +* How to interact with the Mendix metamodel +* How to host your user interface within the Studio Pro IDE + +## Prerequisites + +Before you start the procedure, make sure that you have installed the following tools on your local development environment: + +* Microsoft Visual Studio 2022 or another equivalent development environment, such as visual studio code or JetBrains Rider. This example will assume that you are using Microsoft Visual Studio 2022. +* Studio Pro version 10.6 or higher + +## Creating the Project and Configuring It as an Extension + +### Creating the Project + +In order for your extension to be loaded correctly as an extension in Studio Pro, you will first need to create a project: + +1. In Visual Studio, create a new project. +2. Select the *Class Library* project template and click **Next**. + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/extensibility-api-howtos/build-todo-example-extension/step-one.png" >}} + +3. Name the project *Mendix.ToDoExtension*. +4. Choose a location to store your extension, and click **Next**. + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/extensibility-api-howtos/build-todo-example-extension/step-two.png" >}} + +5. Set **Framework** to *.NET 8.0 (Long Term Support)* and click **Create**. + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/extensibility-api-howtos/build-todo-example-extension/step-three.png" max-width=80% >}} + +Now you have an empty project. + +### Installing Extensions API NuGet Package + +You must do the following steps to configure the project so that it can be used as an extension in Studio Pro: + +1. Reference the extensibility API NuGet package +2. Add a `manifest.json` file to the solution. + +The details of each step are described below. + +#### Referencing the Extensibility API NuGet Package + +1. In Visual Studio, go to **Tools** > **NuGet Package Manager** > **Manage NuGet Packages for Solution**. + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/extensibility-api-howtos/build-todo-example-extension/step-four.png" >}} + +2. On the **Browse** tab, search for **Mendix ExtensionsAPI**. + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/extensibility-api-howtos/build-todo-example-extension/step-five.png" max-width=50% >}} + +3. Select the NuGet package and click **Install**. + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/extensibility-api-howtos/build-todo-example-extension/step-six.png" max-width=50% >}} + +#### Adding a `manifest.json` File + +You now have a class library that can be loaded as an extension by Studio Pro. However, Studio Pro still needs some help in determining how to load the assemblies of your class library. Studio Pro reads a special file called *manifest.json*. This file instructs Studio Pro on which assemblies to load into each execution context. + +1. In Visual Studio, go to **View** > **Solution Explorer** to open the Solution Explorer. +2. Right-click in the Solution Explorer and add a new file called *manifest.json*. +3. Right-click in the Solution Explorer and select **Properties**. +4. Ensure that you set the **Copy to Output Directory** property to **Copy always** to ensure that this file is included in your extensions output files. +5. Replace the contents of your manifest.json file with the following code: + + ```json + { + "mx_extensions": [ "ToDoExtension.dll" ], + "mx_build_extensions": [ ] + } + ``` + + Within the *manifest.json* file, you specify which assemblies Studio Pro needs to load for the different execution contexts your extension needs to provide. If your extension only needs access to design time features and requires a user interface, then you can add it to the `mx_extensions` option. However, if your extension needs access to runtime information or perhaps needs to translate design time concepts into runtime concepts, then you will add it to `mx_build_extensions`. + + {{% alert color="warning" %}}Build extensions specified in `mx_build_extensions` will not have access to any user interfaces and attempting to link to user interface libraries will fail. Similarly, extensions loaded from `mx_extensions` cannot access any runtime features and are strictly design time only.{{% /alert %}} + + This will instruct Studio Pro to load `Mendix.ToDoExtension.dll`, whenever it loads Studio Pro extensions for your app. Adjust your local project names as needed. + +6. Remove the `Class1.cs` file, as you will not need it anymore. + +## Adding a Dockable Pane to Host Your User Interface + +In this section, you will host a dockable pane within Studio Pro. This will provide you with a window where you can render the User Interface of the extension. + +1. Add a new file to the solution called `ToDoListDockablePaneExtension.cs`. +2. Replace the contents of the file with the following code: + + ```csharp + using System.ComponentModel.Composition; + using Mendix.StudioPro.ExtensionsAPI.Services; + using Mendix.StudioPro.ExtensionsAPI.UI.DockablePane; + + namespace Mendix.ToDoExtension; + + [Export(typeof(DockablePaneExtension))] + public class ToDoListDockablePaneExtension : DockablePaneExtension + { + private readonly ILogService _logService; + public const string PaneId = "ToDoList"; + + [ImportingConstructor] + public ToDoListDockablePaneExtension(ILogService logService) + { + _logService = logService; + } + + public override string Id => PaneId; + + public override DockablePaneViewModelBase Open() + { + return new ToDoListDockablePaneViewModel(WebServerBaseUrl, () => CurrentApp, _logService) { Title = "To Do List" }; + } + } + ``` + +{{% alert color="info" %}} +You are expected to get an error at this point around the `ToDoListDockablePaneViewModel`. +{{% /alert %}} + +### Explanation + +There are a few notable features of the class in the code above: +First, the top of the class is decorated with an `Export` attribute: + +```csharp +[Export(typeof(DockablePaneExtension))] +``` + +Studio Pro uses this attribute to identify which extension type to inject this class into. If you do not specify this attribute, Studio Pro will not load your extension type. Additionally, the extension descends from `DockablePaneExtension`. Studio Pro uses abstract classes to enforce behavior for your extensions. + +```csharp +public class ToDoListDockablePaneExtension : DockablePaneExtension +``` + +In order for your type to be loaded, you will need to add a `ImportingConstructor` attribute to the preferred constructor. Studio Pro will use this constructor when instantiating your extension class. + +When instantiating your class, Studio Pro will attempt to perform dependency injection for any of the types that you define in the constructor. + +If you wish to inject your own custom types, they will also need to be decorated with the `Export` attribute. + +```csharp + [ImportingConstructor] + public ToDoListDockablePaneExtension(ILogService logService) + { + _logService = logService; + } +``` + +In this constructor, you will note that you request an instance of the `ILogService` and then save the instance in a private field. + +```csharp + public override string Id => PaneId; + + public override DockablePaneViewModelBase Open() + { + return new ToDoListDockablePaneViewModel(WebServerBaseUrl, () => CurrentApp, _logService) { Title = "To Do List" }; + } +``` + +In the final portion of the class, provide some necessary information to Studio Pro: + +First, you override the `Id` property. This property provides Studio Pro with a way to uniquely identify your dockable pane extension. Second, you override the `Open` method. Within this method you need to return a valid implementation of `DockablePaneViewModelBase` which studio Pro will use to render your pane's contents. + +In summary, in this section you performed the following: + +1. Create a new class that descends from `DockablePaneExtension`. +2. Decorate your class with the `Export` attribute. +3. Decorate your preferred constructor with the `ImportingConstructor` attribute. +4. Inject the `ILogService`. +5. Return a valid view model from the open method. + +## Creating a View Model to Host Your View Data + +In this section, you will add a view model to store our view data: + +1. Add a new file to the solution and name it `ToDoListDockablePaneViewModel.cs`. +2. Replace the contents of the file with the following code: + + ```csharp + using Mendix.StudioPro.ExtensionsAPI.Model; + using Mendix.StudioPro.ExtensionsAPI.Services; + using Mendix.StudioPro.ExtensionsAPI.UI.DockablePane; + using Mendix.StudioPro.ExtensionsAPI.UI.WebView; + + namespace Mendix.ToDoExtension; + + public class ToDoListDockablePaneViewModel : WebViewDockablePaneViewModel { + + private readonly Uri _baseUri; + private readonly Func _getCurrentApp; + private readonly ILogService _logService; + + public ToDoListDockablePaneViewModel(Uri baseUri, Func getCurrentApp, ILogService logService) + { + _baseUri = baseUri; + _getCurrentApp = getCurrentApp; + _logService = logService; + } + + public override void InitWebView(IWebView webView) + { + webView.Address = new Uri(_baseUri, "index"); + + webView.MessageReceived += (_, args) => + { + var currentApp = _getCurrentApp(); + if (currentApp == null) return; + + if (args.Message == "AddToDo") + { + var toDoText = args.Data["toDoText"]?.GetValue() ?? "New To Do"; + AddToDo(currentApp, toDoText); + webView.PostMessage("RefreshToDos"); + } + + if (args.Message == "ChangeToDoStatus") + { + var toDoId = args.Data["id"]!.GetValue(); + var newIsDone = args.Data["isDone"]!.GetValue(); + + ChangeToDoStatus(currentApp, toDoId, newIsDone); + webView.PostMessage("RefreshToDos"); + } + + if (args.Message == "ClearDone") + { + ClearDone(currentApp); + webView.PostMessage("RefreshToDos"); + } + }; + } + + private void AddToDo(IModel currentApp, string toDoText) + { + var toDoStorage = new ToDoStorage(currentApp, _logService); + var toDoList = toDoStorage.LoadToDoList(); + toDoList.ToDos.Add(new ToDoModel(toDoText, false)); + toDoStorage.SaveToDoList(toDoList); + } + + private void ChangeToDoStatus(IModel currentApp, string toDoId, bool newIsDone) + { + var toDoStorage = new ToDoStorage(currentApp, _logService); + var toDoList = toDoStorage.LoadToDoList(); + var toDo = toDoList.ToDos.FirstOrDefault(x => x.Id == toDoId); + if (toDo != null) + { + toDo.IsDone = newIsDone; + toDoStorage.SaveToDoList(toDoList); + } + } + + private void ClearDone(IModel currentApp) + { + var toDoStorage = new ToDoStorage(currentApp, _logService); + var toDoList = toDoStorage.LoadToDoList(); + toDoList.ToDos.RemoveAll(x => x.IsDone); + toDoStorage.SaveToDoList(toDoList); + } + } + ``` + +{{% alert color="warning" %}} +{{% snippet file="/static/_includes/apidocs-mxsdk/warning-wwwroot.md" %}} +{{% /alert %}} + +### Explanation + +The first important thing to note about this view model class is that you do not decorate this class with the export attribute. This means that the extension will be responsible for instantiating this type within the extension. This also means that you can specify any type you like within the constructor. You already set up the instantiation of this class in the previous section. The important bit here is that you pass in the `baseUri`, `getCurrentApp` lambda expression and an instance of the logging class. + +```csharp + public ToDoListDockablePaneViewModel(Uri baseUri, Func getCurrentApp, ILogService logService) + { + _baseUri = baseUri; + _getCurrentApp = getCurrentApp; + _logService = logService; + } +``` + +In order to host a web interface inside Studio Pro, your viewmodel must implement `InitWebView`. Within this method you are passed an instance of `IWebView`. This is your application's isolated webview. You now need to provide the webview some information so that it will render its data correctly. + +```csharp + public override void InitWebView(IWebView webView) + { + webView.Address = new Uri(_baseUri, "index"); + + webView.MessageReceived += (_, args) => + { + var currentApp = _getCurrentApp(); + if (currentApp == null) return; + + if (args.Message == "AddToDo") + { + var toDoText = args.Data["toDoText"]?.GetValue() ?? "New To Do"; + AddToDo(currentApp, toDoText); + webView.PostMessage("RefreshToDos"); + } + + if (args.Message == "ChangeToDoStatus") + { + var toDoId = args.Data["id"]!.GetValue(); + var newIsDone = args.Data["isDone"]!.GetValue(); + + ChangeToDoStatus(currentApp, toDoId, newIsDone); + webView.PostMessage("RefreshToDos"); + } + + if (args.Message == "ClearDone") + { + ClearDone(currentApp); + webView.PostMessage("RefreshToDos"); + } + }; + } +``` + +{{% alert color="warning" %}} +{{% snippet file="/static/_includes/apidocs-mxsdk/warning-wwwroot.md" %}} +{{% /alert %}} + +Firstly, you set the default address to `new Uri(_baseUri, "index")`. You will delve a bit deeper into where this index comes from later in the guide. If you want to skip ahead, see [Setting up Communication Between the User Interface and Extension](#set-up-communication) + +Secondly, you add an event handler for the `MessageReceived` event. You will be using this method send and respond to messages from the webview. Within Studio Pro, use a two-way message bus as the primary communication method between your web based user interface and your extension logic. + +Inside the message received event handler, add some code to handle the tasks you need to perform: + +* `AddToDo` will add a Todo Item to the list. +* `ChangeToDoStatus` will change the status of a Todo item. +* `ClearDone` will remove all items flagged as done. + +```csharp + var currentApp = _getCurrentApp(); + if (currentApp == null) return; + + if (args.Message == "AddToDo") + { + var toDoText = args.Data["toDoText"]?.GetValue() ?? "New To Do"; + AddToDo(currentApp, toDoText); + webView.PostMessage("RefreshToDos"); + } + + if (args.Message == "ChangeToDoStatus") + { + var toDoId = args.Data["id"]!.GetValue(); + var newIsDone = args.Data["isDone"]!.GetValue(); + + ChangeToDoStatus(currentApp, toDoId, newIsDone); + webView.PostMessage("RefreshToDos"); + } + + if (args.Message == "ClearDone") + { + ClearDone(currentApp); + webView.PostMessage("RefreshToDos"); + } +``` + +Now, create the methods responsible for performing the logic: + +```csharp + private void AddToDo(IModel currentApp, string toDoText) + { + var toDoStorage = new ToDoStorage(currentApp, _logService); + var toDoList = toDoStorage.LoadToDoList(); + toDoList.ToDos.Add(new ToDoModel(toDoText, false)); + toDoStorage.SaveToDoList(toDoList); + } + + private void ChangeToDoStatus(IModel currentApp, string toDoId, bool newIsDone) + { + var toDoStorage = new ToDoStorage(currentApp, _logService); + var toDoList = toDoStorage.LoadToDoList(); + var toDo = toDoList.ToDos.FirstOrDefault(x => x.Id == toDoId); + if (toDo != null) + { + toDo.IsDone = newIsDone; + toDoStorage.SaveToDoList(toDoList); + } + } + + private void ClearDone(IModel currentApp) + { + var toDoStorage = new ToDoStorage(currentApp, _logService); + var toDoList = toDoStorage.LoadToDoList(); + toDoList.ToDos.RemoveAll(x => x.IsDone); + toDoStorage.SaveToDoList(toDoList); + } +``` + +## Creating a Model to Store the Todo Information + +In order to store the information to disk, add some model classes that will be able to store the Todo information. + +1. Add a new class that will host the To do information itself. Call the file `ToDoModel.cs` +2. Replace the contents of the file with the following code: + + ```csharp + using System.Text.Json.Serialization; + + namespace Mendix.ToDoExtension; + + public record ToDoModel + { + [JsonConstructor] + public ToDoModel(string id, string text, bool isDone) + { + Id = id; + Text = text; + IsDone = isDone; + } + + public ToDoModel(string text, bool isDone) + : this(Guid.NewGuid().ToString(), text, isDone) + { + } + + public string Id { get; set; } + public string Text { get; set; } + public bool IsDone { get; set; } + } + ``` + + You will also need a model class that will store a list of all the todos that you have available. + +3. Add another class and name it `ToDoListModel.cs`. +4. Replace the contents of this file with the following code: + + ```csharp + using System.Text.Json.Serialization; + + namespace Mendix.ToDoExtension; + + public record ToDoListModel + { + [JsonConstructor] + public ToDoListModel(List toDos) + { + ToDos = toDos; + } + + public List ToDos { get; } + } + ``` + +## Creating a Storage Handler to Store the Todo Information + +With the models created, you can now create a storage handler that will manage storing these models to disk. + +1. Add a new class file and call it `ToDoStorage.cs`. +2. Replace the contents of the file with the following code: + + ```csharp + using System.Text; + using System.Text.Json; + using Mendix.StudioPro.ExtensionsAPI.Model; + using Mendix.StudioPro.ExtensionsAPI.Services; + + namespace Mendix.ToDoExtension; + + public class ToDoStorage + { + private readonly ILogService _logService; + private readonly string _toDoFilePath; + + public ToDoStorage(IModel currentApp, ILogService logService) + { + _logService = logService; + _toDoFilePath = Path.Join(currentApp.Root.DirectoryPath, "to-do-list.json"); + } + + public ToDoListModel LoadToDoList() + { + ToDoListModel? toDoList = null; + + try + { + toDoList = JsonSerializer.Deserialize(File.ReadAllText(_toDoFilePath, Encoding.UTF8)); + } + catch (Exception exception) + { + _logService.Error($"Error while loading To Dos from {_toDoFilePath}", exception); + } + + return toDoList ?? new ToDoListModel(new[] + { + new ToDoModel("Buy milk", false), + new ToDoModel("Fix house", false), + new ToDoModel("Shave yak", true) + }.ToList()); + } + + public void SaveToDoList(ToDoListModel toDoList) + { + var jsonText = JsonSerializer.Serialize(toDoList, new JsonSerializerOptions() { WriteIndented = true }); + File.WriteAllText(_toDoFilePath, jsonText, Encoding.UTF8); + } + } + ``` + +### Explanation + +The `ToDoStorage` class will be responsible for storing the todo information to disk. In order to store the file in the correct path, you need to request the path from the `CurrentApp` instance. + +```csharp + public ToDoStorage(IModel currentApp, ILogService logService) + { + _logService = logService; + _toDoFilePath = Path.Join(currentApp.Root.DirectoryPath, "to-do-list.json"); + } +``` + +You also need to handle loading and saving of the todo data. + +```csharp + public ToDoListModel LoadToDoList() + { + ToDoListModel? toDoList = null; + + try + { + toDoList = JsonSerializer.Deserialize(File.ReadAllText(_toDoFilePath, Encoding.UTF8)); + } + catch (Exception exception) + { + _logService.Error($"Error while loading To Dos from {_toDoFilePath}", exception); + } + + return toDoList ?? new ToDoListModel(new[] + { + new ToDoModel("Buy milk", false), + new ToDoModel("Fix house", false), + new ToDoModel("Shave yak", true) + }.ToList()); + } + + public void SaveToDoList(ToDoListModel toDoList) + { + var jsonText = JsonSerializer.Serialize(toDoList, new JsonSerializerOptions() { WriteIndented = true }); + File.WriteAllText(_toDoFilePath, jsonText, Encoding.UTF8); + } +``` + +## Adding a Menu Item to Open the Extension from the Main Menu + +In this section, you will add a menu item to the toolbar that will allow you to select the ToDo list from a menu item. + +1. Create a `MenuExtension`. +2. Add another class and call it `ToDoListMenuExtension.cs`. +3. Replace the contents of the file with the following code: + + ```csharp + using System.Collections.Generic; + + using System.ComponentModel.Composition; + using Mendix.StudioPro.ExtensionsAPI.UI.DockablePane; + using Mendix.StudioPro.ExtensionsAPI.UI.Menu; + using Mendix.StudioPro.ExtensionsAPI.UI.Services; + + namespace Mendix.ToDoExtension; + + [Export(typeof(Mendix.StudioPro.ExtensionsAPI.UI.Menu.MenuExtension))] + public class ToDoListMenuBarExtension : MenuExtension + { + private readonly IDockingWindowService _dockingWindowService; + + [ImportingConstructor] + public ToDoListMenuBarExtension(IDockingWindowService dockingWindowService) + { + _dockingWindowService = dockingWindowService; + } + + public override IEnumerable GetMenus() + { + yield return new MenuViewModel("To Do List", () => _dockingWindowService.OpenPane(ToDoListDockablePaneExtension.PaneId)); + } + } + ``` + +## Adding a Web-based User Interface + +Up to now you have been adding all the logic that will allow your extension to run inside Studio Pro. In this section, you will add a user interface for the extension. In Studio Pro, you need to load your user interface elements as web content. This web content is then rendered from within an isolated web view in Studio Pro. + +{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/extensibility-api-howtos/build-todo-example-extension/add-web-items.png" >}} + +1. Add a new folder to the solution and call it `wwwroot`. +2. In the folder, add two files: + + * A HTML page that contains the layout of the user interface. Call it `index.html` + * A JavaScript file that contains the client side logic for the user interface. Call it `main.js` + +3. Open `index.html`. +4. Replace its contents with the following: + + ```html + + + To Do List + + + + +
+

To Do

+
+

Done

+
+ + + + + ``` + +5. Open `main.js` and add the JavaScript logic by replacing the contents of the file with the following: + + ```js + function postMessage(message, data) { + window.chrome.webview.postMessage({ message, data }); + } + + // Register message handler. + window.chrome.webview.addEventListener("message", handleMessage); + // Indicate that you are ready to receive messages. + postMessage("MessageListenerRegistered"); + + async function handleMessage(event) { + const { message, data } = event.data; + if (message === "RefreshToDos") { + await refreshToDos(); + } + } + + async function refreshToDos() { + let todosResponse = await fetch("./todos"); + let todos = await todosResponse.json(); + + let todoDiv = document.getElementById("todo"); + let doneDiv = document.getElementById("done"); + + let todoItems = []; + let doneItems = []; + + for (const todo of todos.ToDos) { + let item = document.createElement("div"); + + let checkbox = document.createElement("input"); + checkbox.type = "checkbox"; + checkbox.id = `todo-${todo.Id}`; + checkbox.checked = todo.IsDone; + checkbox.addEventListener("click", () => { + postMessage("ChangeToDoStatus", { id: todo.Id, isDone: !todo.IsDone }); + }); + + let label = document.createElement("label"); + label.htmlFor = checkbox.id; + label.innerText = todo.Text; + + item.replaceChildren(checkbox, label); + + if (todo.IsDone) { + doneItems.push(item); + } else { + todoItems.push(item); + } + } + + todoDiv.replaceChildren(...todoItems); + doneDiv.replaceChildren(...doneItems); + } + + async function addToDo(){ + let addToDoInput = document.getElementById("addToDoInput"); + const toDoText = addToDoInput.value; + postMessage("AddToDo", { toDoText }); + addToDoInput.value = ""; + } + + document.getElementById("addToDoButton").addEventListener("click", addToDo); + document.getElementById("clearDoneButton").addEventListener("click", () => { + postMessage("ClearDone"); + }); + + await refreshToDos(); + ``` + +### Explanation + +This HTML page is self-explanatory, as you are providing a very simple interface with some added CSS styling provided by Tailwind CSS. + +Within the JavaScript file, you need to add some logic so that the web view can communicate with your extension logic correctly. + +You add a small helper function to simplify the call to the browser API: + +```javascript +function postMessage(message, data) { + window.chrome.webview.postMessage({ message, data }); +} +``` + +You also need to perform some initialization to ensure that you can respond to messages send to JavaScript and + +```javascript +// Register message handler. +window.chrome.webview.addEventListener("message", handleMessage); +// Indicate that you are ready to receive messages. +postMessage("MessageListenerRegistered"); + +async function handleMessage(event) { + const { message, data } = event.data; + if (message === "RefreshToDos") { + await refreshToDos(); + } +} +``` + +It is important to set these two `index.html` and `main.js` files to *Copy always* or *Copy if newer* in their **Copy to Output Directory** property; otherwise, they will not be present in the build output folder when you are ready to start using the extension. + +## Setting up Communication Between the User Interface and Extension {#set-up-communication} + +So far you have configured the extension to be usable in Studio Pro. You added support for storing the to do items. You also added a user interface that users can interact with. The last step in this process is to link the extension c# logic with the web-based JavaScript logic. + +1. Start with adding a utility class to help simplify the way you interact with web responses. Call the file `HttpListenerResponseUtils.cs`. +2. Replace the contents of the file with the following: + + ```csharp + using System.Net; + using System.Text; + + namespace Mendix.ToDoExtension; + + public static class HttpListenerResponseUtils + { + public static async Task SendFileAndClose(this HttpListenerResponse response, string contentType, string filePath, CancellationToken ct) + { + response.AddDefaultHeaders(200); + + var fileContents = await File.ReadAllBytesAsync(filePath, ct); + + response.ContentType = contentType; + response.ContentLength64 = fileContents.Length; + + await response.OutputStream.WriteAsync(fileContents, ct); + + response.Close(); + } + + public static void SendJsonAndClose(this HttpListenerResponse response, MemoryStream jsonStream) + { + response.AddDefaultHeaders(200); + + response.ContentType = "application/json"; + response.ContentEncoding = Encoding.UTF8; + response.ContentLength64 = jsonStream.Length; + + jsonStream.WriteTo(response.OutputStream); + + response.Close(); + } + + public static void SendNoBodyAndClose(this HttpListenerResponse response, int statusCode) + { + response.AddDefaultHeaders(statusCode); + + response.Close(); + } + + static void AddDefaultHeaders(this HttpListenerResponse response, int statusCode) + { + response.StatusCode = statusCode; + + // Makes sure the web-code can receive responses + response.AddHeader("Access-Control-Allow-Origin", "*"); + } + } + ``` + +### Explanation + +Your web-based user interface is hosted inside Studio Pro in an isolated web container. As such to communicate with it you are adding some utility functionality to help you improve the code. + +The first method you add is `SendFileAndClose`. This function will allow you to send the contents of a file located on your hard drive to the webpage where your user interface is hosted. + +Next you add `SendJsonAndClose`. This method functions similarly to `SendFileAndClose`, but will accept a json stream instead of a file path. + +After that you add `SendNoBodyAndClose`. This sends an empty response with just a status code to the webpage. + +The final method `AddDefaultHeaders` is a utility method that adds some default http headers to the requests. + +## Next Steps + +In this section, you will add a web server extension. This extension type allows you to serve web content easily within extensions. + +1. Add a new file called: `ToDoListWebServerExtension.cs`. +2. Replace the contents of the file with the following code: + + ```csharp + using System.ComponentModel.Composition; + using System.Net; + using System.Text.Json; + using Mendix.StudioPro.ExtensionsAPI.Services; + using Mendix.StudioPro.ExtensionsAPI.UI.WebServer; + + namespace Mendix.ToDoExtension; + + [Export(typeof(WebServerExtension))] + public class ToDoListWebServerExtension : WebServerExtension + { + private readonly IExtensionFileService _extensionFileService; + private readonly ILogService _logService; + + [ImportingConstructor] + public ToDoListWebServerExtension(IExtensionFileService extensionFileService, ILogService logService) + { + _extensionFileService = extensionFileService; + _logService = logService; + } + + public override void InitializeWebServer(IWebServer webServer) + { + webServer.AddRoute("index", ServeIndex); + webServer.AddRoute("main.js", ServeMainJs); + webServer.AddRoute("todos", ServeToDos); + } + + private async Task ServeIndex(HttpListenerRequest request, HttpListenerResponse response, CancellationToken ct) + { + var indexFilePath = _extensionFileService.ResolvePath("wwwroot", "index.html"); + await response.SendFileAndClose("text/html", indexFilePath, ct); + } + + private async Task ServeMainJs(HttpListenerRequest request, HttpListenerResponse response, CancellationToken ct) + { + var indexFilePath = _extensionFileService.ResolvePath("wwwroot", "main.js"); + await response.SendFileAndClose("text/javascript", indexFilePath, ct); + } + + private async Task ServeToDos(HttpListenerRequest request, HttpListenerResponse response, CancellationToken ct) + { + if (CurrentApp == null) + { + response.SendNoBodyAndClose(404); + return; + } + + var toDoList = new ToDoStorage(CurrentApp, _logService).LoadToDoList(); + var jsonStream = new MemoryStream(); + await JsonSerializer.SerializeAsync(jsonStream, toDoList, cancellationToken: ct); + + response.SendJsonAndClose(jsonStream); + } + } + ``` + +This class is the web container that allows Studio Pro to interact with your user interface. Within this class you will serve the web content to your extension logic. + +Note that you inherit from `WebServerExtension`. `WebServerExtension` serves web content to Studio Pro. + +Additionally, you override the `InitializeWebServer` method. Studio Pro will call this method during startup, and you should place all your initialization logic in here. This implementation adds three web routes. These web routes are the locations where your user interface can be accessed from. + +`ServeIndex`, `ServeMainJs`, and `ServeToDos` serve the contents of the 3 routes to your extension logic. + +## Hosting the Extension in Studio Pro + +All the code you need should now be complete. The last step in the process is building your solution and adding your binary output as an extension inside your app. + +To do this you will need to do the following: + +1. Build your solution in Visual Studio by selecting the **Build** > **Build Solution**. +2. Navigate to the Mendix app where your extension will be hosted. +3. Create a new folder and name it `extensions`. +4. Add a subfolder and name it `TodoExtension`. You now have a folder with a path similar to this `[Mendix App]/extensions/MyTodoExtension`/. +5. Copy the files from your Visual Studio extension projects `bin/debug` subfolder into your mendix app extension folder: [`Mendix App]/extensions/MyTodoExtension`. +6. Run Studio Pro. + +While developing extensions, also use command line flag to enable extensions as follows: + +1. Navigate to your Studio Pro Installation folder. +2. From the command line, call the following command: `.\studiopro.exe --enable-extension-development`. + +This will start an instance of Studio Pro and load your extension. You can now view your new extension interface from the **View** > **Todo** menu item. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/create-context-menu.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/create-context-menu.md new file mode 100644 index 00000000000..a5cd5e4a0c6 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/create-context-menu.md @@ -0,0 +1,119 @@ +--- +title: "Create a Context Menu Using C#" +linktitle: "Context Menu" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-context-menu/ +weight: 6 +--- + +## Introduction + +It is possible to add a context menu to an `IEntity` in Studio Pro or to a `IDocument`, such as microflows and pages, etc. Context menus will be placed under a menu named after the extension that contains them (e.g. `MyExtension`) and context menus can modify those items they relate to. You can achieve this simple by specifying the type when creating the extension. + +This how-to describes how you can add a context menu to an entity. Before you start this how-to, it is recommended to [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-menu-extension/) first. + +You can download the example in this how-to in [this GitHub repository](https://github.com/mendix/ExtensionAPI-Samples). + +## Creating an Entity Context Menu Extension Class + +1. Open the project that you previously created when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-menu-extension/). +2. Add a new class to the project and name it `MyEntityContextMenuExtension.cs`. +3. Replace the code in the file with the following code: + + ```csharp + namespace MyCompany.MyProject.MendixExtension; + + [method: ImportingConstructor] + [Export(typeof(Mendix.StudioPro.ExtensionsAPI.UI.Menu_V2.ContextMenuExtension<>))] + class MyEntityContextMenuExtension(IMessageBoxService messageBoxService) : Mendix.StudioPro.ExtensionsAPI.UI.Menu_V2.ContextMenuExtension + { + MenuViewModel? disabledMenu; + + public override IEnumerable GetContextMenus(IEntity entity) + { + var nudgeDownLeft = new MenuViewModel("Left", () => NudgeIt(entity, right: false, down: true)); + var nudgeDownLeftRight = new MenuViewModel("Right", () => NudgeIt(entity, right: true, down: true) ); + var nudgeDown = new MenuViewModel("Down", [nudgeDownLeft, nudgeDownLeftRight]); + + var nudgeUpLeft = new MenuViewModel("Left", () => NudgeIt(entity, right: false, down: false)); + var nudgeUpRight = new MenuViewModel("Right", () => NudgeIt(entity, right: true, down: false)); + var nudgeUp = new MenuViewModel("Up", [nudgeUpLeft, nudgeUpRight]) + { + Separator = MenuSeparator.Before + }; + + yield return new MenuViewModel("Nudge it!", [nudgeDown, nudgeUp]); + + // new group + var randomRenameString = new MenuViewModel("Random string", () => RenameEntity(entity, number: false)); + var randomRenameNumber = new MenuViewModel("Random number", () => RenameEntity(entity, number: true)); + + yield return new MenuViewModel("Randomly rename", [randomRenameString, randomRenameNumber]); + + // show location + yield return new MenuViewModel("Show location", () => ShowLocation(entity, messageBoxService)); + + var showName = new MenuViewModel("Show current name", () => messageBoxService.ShowInformation(entity.Name)); + // "Randomly rename" was added previously, thus it will trigger an exception when collected if uncommented + //yield return new MenuViewModel("Randomly rename", [showName]); + + // Enabling a disabled menu + disabledMenu ??= new MenuViewModel("Disabled menu", () => + { + messageBoxService.ShowInformation("I'm enabled now. But not for long!"); + disabledMenu!.IsEnabled = false; + }){ IsEnabled = false }; + var enablingMenu = new MenuViewModel("Enable disabled menu", () => disabledMenu.IsEnabled = true ); + + yield return new MenuViewModel("Enabling menus", [disabledMenu, enablingMenu]); + + } + + void NudgeIt(IEntity entity, bool right, bool down) + { + var incrementRight = right ? 20 : -20; + var incrementDown = down ? 20 : -20; + using var transaction = CurrentApp!.StartTransaction("nudge with context menu"); + entity.Location = new Location(entity.Location.X + incrementRight, entity.Location.Y + incrementDown); + + transaction.Commit(); + } + + void RenameEntity(IEntity entity, bool number) + { + using var transaction = CurrentApp!.StartTransaction("rename with context menu"); + + entity.Name = number ? $"E_{new Random().Next()}" : $"E_{Guid.NewGuid().ToString().Replace("-", "")}"; + + transaction.Commit(); + } + static void ShowLocation(IEntity entity, IMessageBoxService messageBoxService) => messageBoxService.ShowInformation($"X: {entity.Location.X}, Y: {entity.Location.Y}"); + } + ``` + +The code above creates a series of context menu items for any entity. It is important to note the type `IEntity` is passed in so that the context menu will only apply to entities. It adds menus using the same logic as `MenuExtension.cs`. For more examples, see [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-menu-extension/). + +In the code above, you can see a few context menus that can perform changes on the entity they belong to. In The entity's location on the canvas can be changed, it can be renamed, and some of its information is shown in a message box. + +It is also possible to add menus that are disabled until some action is performed. + +## Adding a Context Menu to a Document + +You can also add a context menu to a document. All you have to do is specify the type `IDocument` in the context menu extension. + +```csharp +namespace MyCompany.MyProject.MendixExtension; + +[method: ImportingConstructor] +[Export(typeof(ContextMenuExtension<>))] +class MyDocumentContextMenuExtension(IMessageBoxService messageBoxService) : ContextMenuExtension +{ + public override IEnumerable GetContextMenus(IDocument document) + { + if (document is IMicroflow microflow) + yield return new MenuViewModel("This document is a microflow", () => messageBoxService.ShowInformation(microflow.Name)); + + else if (document is IPage page) + yield return new MenuViewModel("This document is a page", () => messageBoxService.ShowInformation(page.Name)); + } +} +``` diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/create-dockable-pane.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/create-dockable-pane.md new file mode 100644 index 00000000000..5c6dd72f0ae --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/create-dockable-pane.md @@ -0,0 +1,94 @@ +--- +title: "Create a Dockable Pane Extension Using C#" +linktitle: "Dockable Pane" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-dockable-pane-extension/ +weight: 5 +--- + +## Introduction + +This how-to describes how you can add a custom dockable web pane window to Studio Pro. Before you start this how-to, it is recommended to [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-menu-extension/) first. + +You can download the example in this how-to in [this GitHub repository](https://github.com/mendix/ExtensionAPI-Samples). + +## Creating a Dockable Pane Extension Class + +1. Open the project that you previously created when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-menu-extension/). +2. Add a new class to the project and name it `MyDockablePaneExtension.cs`. +3. Replace the code in the file with the following code: + + ```csharp + using System.ComponentModel.Composition; + using Mendix.StudioPro.ExtensionsAPI.UI.DockablePane; + + namespace MyCompany.MyProject.MendixExtension; + + [Export(typeof(DockablePaneExtension))] + public class MyDockablePaneExtension : DockablePaneExtension + { + public const string ID = "my-dockable-pane"; + public override string Id => ID; + + public override DockablePaneViewModelBase Open() => new MyDockablePaneExtensionWebViewModel("http://mendix.com"); + } + ``` + +## The View Model for the Extension Tab + +The dockable pane will have content, and this content comes in the form of a view model. The view model is an implementation of `WebViewDockablePaneViewModel`. + +You need to override the `InitWebView` method in which you can set up the content of your web view inside the dockable pane. In this example, it will contain the home page of `http://mendix.com`. + +Below is a small example code of the view model: + +```csharp +using Mendix.StudioPro.ExtensionsAPI.UI.DockablePane; +using Mendix.StudioPro.ExtensionsAPI.UI.WebView; + +namespace MyCompany.MyProject.MendixExtension; + +public class MyDockablePaneExtensionWebViewModel(string homePage) : WebViewDockablePaneViewModel +{ + public override void InitWebView(IWebView webView) => webView.Address = new Uri(homePage); +} +``` + +{{% alert color="warning" %}} +{{% snippet file="/static/_includes/apidocs-mxsdk/warning-wwwroot.md" %}} +{{% /alert %}} + +The code above creates a new web-enabled tab view. You still need a way to show the new dockable pane. To do so, you need to modify the menu extension you added when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-menu-extension/). Simple replace the existing content of `MyMenuExtension.cs` with the following code: + +```csharp +using System.ComponentModel.Composition; +using Mendix.StudioPro.ExtensionsAPI.UI.Menu; +using Mendix.StudioPro.ExtensionsAPI.UI.Services; + +namespace MyCompany.MyProject.MendixExtension; + +[Export(typeof(MenuExtension))] +public class MyMenuExtension(IDockingWindowService dockingWindowService, IMessageBoxService messageBoxService) : MenuExtension +{ + public override IEnumerable GetMenus() + { + yield return new MenuViewModel("Say hello", () => messageBoxService.ShowInformation("Hello World!")); + yield return new MenuViewModel("Open My Dockable Pane", () => dockingWindowService.OpenPane(MyDockablePaneExtension.ID)); + } +} +``` + +The code above introduces a few new concepts. + +Firstly, you inject the `IDockingWindowService` so that you can open a new dockable pane. + +Secondly, you add a new menu item with the caption **Open My Dockable Pane** that you will use to open your new dockable pane using the `IDockingWindow` service that you have injected. + +In order for the changes to reflect, you need to build your project. If you have opted to not automatically copy the output to the destination folder, then you will also need to manually copy the bin output from your project to your extension folder you created when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-menu-extension/). + +## Showing a Dockable Pane Without Adding a Custom Menu + +Instead of adding a separate menu to open the docking pane, you can override the `ViewMenuCaption` property in the implementation of the `DockablePaneExtension`. This means that the menu that opens will be placed under the `View` top-level menu in Studio Pro and will have the caption provided. There is no need for a separate `MenuExtension` in this case. + +```csharp +public override string? ViewMenuCaption => "My pane without custom menu"; +``` diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/create-menu-extension.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/create-menu-extension.md new file mode 100644 index 00000000000..ed46ceb8f08 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/create-menu-extension.md @@ -0,0 +1,110 @@ +--- +title: "Create a Menu Extension Using C#" +linktitle: "Create Menu" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-menu-extension/ +weight: 4 +--- + +## Introduction + +This how-to describes how you can create an extension that adds an item to Studio Pro menu from scratch. + +You can download the example in this how-to in [this GitHub repository](https://github.com/mendix/ExtensionAPI-Samples). + +## Creating an Extension Project + +1. Create a new project in Visual Studio based on `C# Class Library` template. +2. Choose a name for the project. Use a format similar to `MyCompany.MyProject.MendixExtension`, but it is not a hard requirement. +3. Choose `.NET 8.0` Framework. +4. Add `Mendix.StudioPro.ExtensionsAPI` NuGet package to the project references. Pick the version that does not exceed the Studio Pro version you installed. To do so, perform the following steps: + + 1. Include a reference to the Extensions API [NuGet package](https://www.nuget.org/packages/Mendix.StudioPro.ExtensionsAPI): + 2. Add new file named `manifest.json` to your project. Put the following content into it: + + ```json + { "mx_extensions": [ ".dll" ] } + ``` + + 3. For the `manifest.json` file, right-click **Solution Explorer** > **Properties** and change the **Copy to Output Directory** property to **Copy always**. + +## Creating a Test Mendix App + +It is handy to have an app where the extension is used for the testing purposes. Perhaps you even want to share this app with your team by committing its code next to the extension project. + +1. Create a new Mendix app to use for testing the extension, based on a starter app of your choice. You can also use an existing app. +2. Go to **App** > **Show App Directory in Explorer** to open the app directory. +3. Create a new folder `extensions` inside the app directory. +4. Create a subfolder named after your extension, for example, `MyCompany`, inside the `extensions` folder. +5. Copy the full path of the subfolder by pressing Shift and right-clicking at the same time, and then selecting **Copy as path**. +6. Add the `Post-build event` script below to your extension project [Build > Events configuration](https://docs.microsoft.com/en-us/visualstudio/ide/how-to-specify-build-events-csharp?view=vs-2022): + `xcopy /y /s /i "$(TargetDir)" ""` + +Now if you build your extension project (usually you can do this by pressing Ctrl + Shift + B) and click [Synchronize App Directory](/refguide/app-menu/#synchronize) in Studio Pro (or press F4), the latest version of your extension will be loaded. + +## Creating Your First Extension + +To introduce a simple extension that adds a menu item to Studio Pro, add the following class: + +```csharp +using System.ComponentModel.Composition; +using Mendix.StudioPro.ExtensionsAPI.UI.Menu; +using Mendix.StudioPro.ExtensionsAPI.UI.Services; + +namespace MyCompany.MyProject.MendixExtension; + +[method: ImportingConstructor] +[Export(typeof(MenuExtension))] +public class MyMenuExtension(IMessageBoxService messageBoxService) : MenuExtension +{ + public override IEnumerable GetMenus() + { + yield return new MenuViewModel("Say hello", () => messageBoxService.ShowInformation("Hello World!")); + } +} +``` + +Build your extension and press F4 in Studio Pro. Extension menu items are placed under a corresponding menu with the extensions name. If your extension is named "My Extension", then your menu items will be located under the **Extensions** > **My Company** submenu. + +The Extensibility API provides you with several services you can use and they are injected into your extension classes by using the `ImportingConstructor` attribute. + +It is also possible to get notified when your extension has been successfully loaded into Studio Pro and also just before it gets unloaded. It is as simple as subscribing to the `ExtensionLoaded` and `ExtensionUnloading` events. + +```csharp +using Mendix.StudioPro.ExtensionsAPI.UI.Events; + +namespace MyCompany.MyProject.MendixExtension; + +[method: ImportingConstructor] +[Export(typeof(MenuExtension))] +public class MyMenuExtension() : MenuExtension +{ + public MyMenuExtension() + { + Subscribe(onEvent: () => { MyActionOnLoaded() }); + Subscribe(onEvent: () => { MyActionOnUnloading() }); + } +} +``` + +## Debugging Your Extension + +1. Make sure that the current version of the extension code is loaded in Studio Pro. +2. Attach to Studio Pro process in Visual Studio debugger as follows: + 1. On the **Debug** menu, open the **Attach to Process** dialog box (or press Ctrl + Alt + P). + 2. Search for `studiopro.exe` among the processes. + 3. Select the only found process (or the one with correct title, if you have many) and select **Attach**. + +3. Add a Breakpoint inside `Action` delegate in `MyMenuExtension.GetMenus()`. It will be hit when you click **Extensions** > **MyCompany** > **Say hello** menu item. + +## Adding a NuGet Dependency + +You can freely use [NuGet packages](https://www.nuget.org/) from extensions to access reusable .NET libraries. The following one-time additional setup is required: + +1. Open your extension project `.csproj` file by right-clicking **Solution Explorer** > **Edit Project File**. +2. Add the following line into the first ``: + + ```xml + true + ``` + +Then you can add a NuGet dependency, for example, using the **Manage NuGet Packages** menu. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/create-microflow-service.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/create-microflow-service.md new file mode 100644 index 00000000000..1aa5475a308 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/create-microflow-service.md @@ -0,0 +1,145 @@ +--- +title: "Create a Microflow and Add Activities Using C#" +linktitle: "Microflows and Activities" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-microflow-add-activities/ +weight: 14 +--- + +## Introduction + +The `IMicroflowService` is the service used to perform actions related to microflows. This how-to describes how you can create a new microflow and add some activities to it. + +## `Initialize` + +The `Initialize` method initializes a microflow that was previously created. It is part of a series of steps required for adding a microflow to the model: + +1. Start a transaction (`IModel.StartTransaction`). +2. Create a microflow and add it to the module (`IModel.Create`). +3. Call `IMicroflowService.Initialize`. + +Internally, the `Initialize` method sets up the start and end flows, and adds any parameters that might be passed in (in the example below, you are passing a single parameter `boolParameter` of `DataType.Boolean`). + +In the example below, you also add activities to the microflow, with `IMicroflowService.TryInsertAfterStart` (adding an activity as the first) or `IMicroflowService.TryInsertBeforeActivity` (adding an activity before another). + +```csharp +public void Initialize(IModel currentApp, params IActionActivity[] actionActivities) +{ + var module = currentApp.Root.GetModules().Single(m => m.Name == "MyFirstModule"); + + using var transaction = currentApp.StartTransaction("Create and initialize microflow"); + + var microflow = currentApp.Create(); + microflow.Name = "Microflow"; + module!.AddDocument(microflow); + + _microflowService.Initialize(microflow, ("boolParameter", DataType.Boolean)); + + for (int i = 0; i < activities.Length; i++) + { + var activity = activities[i]; + if (i == 0) + _microflowService.TryInsertAfterStart(microflow, activity); + else + _microflowService.TryInsertBeforeActivity(activity, activities[i-1]); + } + + transaction.Commit(); +} +``` + +As you can see, this `IMicroflowService.Initialize` method can be cumbersome to use, since it is only part of the whole process of creating a new microflow. To have an easier method of creating microflows, use the `MicroflowService.CreateMicroflow` method. This method is described in the next section. + +## `CreateMicroflow` + +The `CreateMicroflow` method is the more advanced and comprehensive method to create microflows. It is a good alternative to the `IMicroflowService.Initialize` method. +The `CreateMicroflow` takes care of initialization and adding everything to the model in one single step. It requires the current `IModel`, the `IFolderBase` (module or folder) in which to save the microflow, a name, an optional `MicroflowReturnValue`, and an optional list of parameters. See the code below for a few examples. + +### Creating a Simple Microflow + +As shown in the code below, all that is required to create a microflow and add it to the model is the `IModel`, the `IFolderBase` in which to add the microflow, and its name. + +```csharp +public void CreateMicroflow(IModel currentApp) +{ + var module = currentApp.Root.GetModules().Single(m => m.Name == "MyFirstModule"); + + using var transaction = currentApp.StartTransaction("Create microflows"); + + var microflow = _microflowService.CreateMicroflow(currentApp, module, "Microflow"); + + transaction.Commit(); +} +``` + +### Creating Microflow with Return Type and Parameters + +In this more advanced example, you will see the `IMicroflowExpressionService.CreateFromString` method, which allows you to create expressions that can be then used as the `MicroflowReturnValue` of the microflow. Here, the expression is a simple addition of two values, and the return type is of `DataType.Integer`. + +```csharp + void CreateMicroflow(IModel currentApp) + { + var module = currentApp.Root.GetModules().Single(m => m.Name == "MyFirstModule"); + string returnValueExpression = "1 + 2"; + + var microflow = microflowService.CreateMicroflow(currentApp, module, "Microflow", + new MicroflowReturnValue(DataType.Integer, microflowExpressionService.CreateFromString(returnValueExpression))); + + transaction.Commit(); + } +``` + +The `IMicroflowService.CreateMicroflow` method is a bit easier to use than the `IMicroflowService.Initialize` method because it doesn't require manually creating the microflow with `IModel.Create` and then manually adding it to the `IFolderBase` container. It can do everything behind the scenes as long as everything is supplied to it. For a comprehensive example on how to create microflows, see [Create Microflows for Calculations Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-microflows-for-calculations/). + +## `TryInsertAfterStart` and `TryInsertBeforeActivity` + +The `TryInsertAfterStart` and `TryInsertBeforeActivity` methods enable inserting an activity in the flow. It can be added right after the start event of the microflow, or be inserted before another specific activity. + +```csharp +microflowService.TryInsertAfterStart(microflow, newActivity); +microflowService.TryInsertBeforeActivity(newAactivity, existingActivity); +``` + +## `GetParameters` + +The `GetParameters` method enables retrieving all the parameters that are inputs into a microflow. It returns a list of `IMicroflowParameterObject`, which is composed of its name, its `IQualifiedName` identifier, a description, and its `DataType`. Any parameters passed into the microflow will be returned here together with their type. + +```csharp +IReadOnlyList parameters = _microflowService.GetParameters(microflow); +``` + +## `GetAllMicroflowActivities` + +The `GetAllMicroflowActivities` method enables retrieving all the activities in the flow of a microflow. It returns a list of `IActivity`. + +```csharp +IReadOnlyList activities = _microflowService.GetAllMicroflowActivities(microflow); +``` + +## `IsVariableNameInUse` + +The `IsVariableNameInUse` method can check if the microflow already contains a variable with the name provided. This can be called before adding a new activity to the flow whose output variable name can overlap with existing variables. An example is as follows: + +```csharp +public void AddNewActivity(IModel currentApp, IMicroflow microflow, string activityName) +{ + using var transaction = currentApp.StartTransaction("Create microflows"); + + var microflowCallActivity = currentApp.Create(); + var microflowCallAction = currentApp.Create(); + microflowCallAction.MicroflowCall = currentApp.Create(); + microflowCallAction.MicroflowCall.Microflow = microflow.QualifiedName; + microflowCallActivity!.Action = microflowCallAction; + + if (!_microflowService.IsVariableNameInUse(microflow, activityName)) + microflowCallAction.OutputVariableName = activityName; + else + { + _messageBoxService.ShowError("That variable name is already in use."); + return; + } + + _microflowService.TryInsertAfterStart(microflow, microflowCallActivity); + + transaction.Commit(); +} +``` diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/create-microflows-calculations.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/create-microflows-calculations.md new file mode 100644 index 00000000000..12782fe75ee --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/create-microflows-calculations.md @@ -0,0 +1,296 @@ +--- +title: "Create Microflows for Calculations Using C#" +linktitle: "Calculation Microflows" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-microflows-for-calculations/ +weight: 8 +--- + +## Introduction + +This how-to describes how you can create microflows which perform some calculations and return the result. + +You can download the example in this how-to in [this GitHub repository](https://github.com/mendix/ExtensionAPI-Samples). + +## Creating an Extension Class that Creates Microflows + +1. Open the project that you previously created when you [created a menu extension](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-menu-extension/). +2. Add a new folder *MicroflowTutorial* to your solution. +3. Create a `MenuExtension` class. +4. Add a new class to the project and call it `CreateMicroflowsMenu.cs`. +5. Replace the code in the file with the code below. + + ```csharp + using Mendix.StudioPro.ExtensionsAPI.UI.Menu; + using System.ComponentModel.Composition; + + namespace MicroflowTutorial; + + [Export(typeof(MenuExtension))] + [method: ImportingConstructor] + class CreateMicroflowsMenu(CalculationsMicroflowCreator microflowCreator) : MenuExtension + { + public override IEnumerable GetMenus() + { + yield return new MenuViewModel("Create microflows", () => + { + if (CurrentApp == null) + return; + + microflowCreator.CreateMicroflows(CurrentApp); + } + ); + } + } + ``` + + As you can see, the `GetMenus` method is overridden to add your own menus to Studio Pro. The class `CalculationsMicroflowCreator` (which you will add shortly) will be called from the action of your menu. You can see that this class has been injected in the constructor of your menu extension. + +## Microflow Creator + +In this section, you will add the class `CalculationsMicroflowCreator.cs`, which will be injected into your menu extension so that it can be called from your menu action. Make sure to add the `Export` attribute to the class; otherwise, it will not be injected into your menu extension. The `ImportingConstructor` attribute on the constructor is also very important, as this allows the class to receive any required services via dependency injection. + +```csharp +using Mendix.StudioPro.ExtensionsAPI.Model; +using Mendix.StudioPro.ExtensionsAPI.Model.DataTypes; +using Mendix.StudioPro.ExtensionsAPI.Model.Microflows; +using Mendix.StudioPro.ExtensionsAPI.Model.Projects; +using Mendix.StudioPro.ExtensionsAPI.Services; +using System.ComponentModel.Composition; + +namespace MicroflowTutorial; + +[Export(typeof(CalculationsMicroflowCreator))] +[method: ImportingConstructor] +class CalculationsMicroflowCreator(IMicroflowService microflowService, IMicroflowExpressionService microflowExpressionService) +{ + +} +``` + +This class contains one public method, which is the one called by your menu. The method `CreateMicroflows` requires the current app as the parameter. The `CreateMicroflowsMenu` extension has access to the `CurrentApp` property, so it will pass it to the method when calling it from the menu action. The `CurrentApp` property is the `IModel` for the app that is currently open in Studio Pro. Every extension that inherits the type `UIExtensionBase` (such as a `MenuBarExtension`) has access to the `CurrentApp` property and can interact and modify the model. + +Add the method as follows: + +```csharp +public void CreateMicroflows(IModel currentApp) +{ + var module = currentApp.Root.GetModules().Single(m => m.Name == "MyFirstModule"); + + using var transaction = currentApp.StartTransaction("Create microflows"); + + CreateMicroflowsInFolder(currentApp, module); + + transaction.Commit(); +} +``` + +As you can see, the `CreateMicroflows` method starts a new transaction, by calling `currentApp.StartTransaction`, which is the only way an extension can modify the model of the app. If your class tried to create microflows outside of a transaction, an error will be thrown. For more information, see [Interact with the Model API Using C#](/apidocs-mxsdk/apidocs/interact-with-model-api/). + + `IMicroflowService` enables creating microflows. For details, see [Create a Microflow and Add Activities Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-microflow-add-activities/). It requires the current model (`IModel`), the containing module or folder inside a module (`IFolderBase`), a name, and an optional `MicroflowReturnValue`. The return value is actually used in the example, so you will see how to create one as well. + +A microflow returns a value with `IMicroflowExpression`. This can be achieved by using your `IMicroflowExpressionService`, which returns an expression from a String input, and set that expression as the microflow's return value. + +A very simple `MicroflowReturnValue` can be created as follows: + +```csharp +new MicroflowReturnValue(DataType.Boolean, microflowExpressionService.CreateFromString("true or false")); +``` + +However, the example will have more complicated expressions, which use parameter names. These parameter names match the return values from called microflows. +This simple extension will create three microflows. Two of them will perform mathematical calculations (multiplication and addition) and each of them will return their result. The other microflow will call these two microflows in sequence, compute their two results (subtract the addition result from the multiplication result), and return true or false if the value is larger than 0. + +The method `CreateMicroflowsInFolder` will create the two microflows and the return values. Add the method. + +```csharp +void CreateMicroflowsInFolder(IModel currentApp, IFolderBase folder) +{ + string multiplicationResult = "multiplicationResult"; + string additionResult = "additionResult"; + + string returnValueExpression = $"(${multiplicationResult} - round(${additionResult}) > 0)"; + + var callingMicroflow = microflowService.CreateMicroflow(currentApp, folder, "Microflow", + new MicroflowReturnValue(DataType.Boolean, microflowExpressionService.CreateFromString(returnValueExpression))); + + CreateMultiplicationMicroflow(currentApp, folder, callingMicroflow, multiplicationResult); + CreateAdditionMicroflow(currentApp, folder, callingMicroflow, additionResult); +} +``` + +To create a microflow which performs a multiplication between two input parameters (two numbers in this case), you can use the code below. As you can see, the String `multiplication1` and the String `multiplication2` match the parameters used in the expression for the return value. Note that for an expression, the dollar sign `$` must be put in front of the parameter name in order to be recognized as a variable input. + +You can also see that the `DataType` of both parameters is integer. + +```csharp +void CreateMultiplicationMicroflow(IModel currentApp, IFolderBase folder, IMicroflow callingMicroflow, string outputVariableName) +{ + var multiplication1Param = "multiplication1"; + var multiplication2Param = "multiplication2"; + + var returnExpression = microflowExpressionService.CreateFromString($"${multiplication1Param} * ${multiplication2Param}"); + var returnValue = new MicroflowReturnValue(DataType.Integer, returnExpression); + + var multiplicationMicroflow = microflowService.CreateMicroflow(currentApp, folder, "MultiplicationMicroflow", + returnValue, + (multiplication1Param, DataType.Integer), + (multiplication2Param, DataType.Integer)); + + CreateMicroflowCallActivity(currentApp, callingMicroflow, multiplicationMicroflow, + outputVariableName, + (multiplication1Param, "10"), + (multiplication2Param, "100")); +} +``` + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/multiplication-microflow.png" >}} + +To create a microflow that performs an addition between two decimal values, you can use the code below. Just like the multiplication microflow example above, you can see that the String `addition1` and the String `addition2` match the parameters used in the expression for the return value. You can also see that their `DataType` is decimal. + +```csharp +void CreateAdditionMicroflow(IModel currentApp, IFolderBase folder, IMicroflow callingMicroflow, string outputVariableName) +{ + var addition1Param = "addition1"; + var addition2Param = "addition2"; + + var returnExpression = microflowExpressionService.CreateFromString($"${addition1Param} + ${addition2Param}"); + var returnValue = new MicroflowReturnValue(DataType.Decimal, returnExpression); + + var additionMicroflow = microflowService.CreateMicroflow(currentApp, folder, "AdditionMicroflow", + returnValue, + (addition1Param, DataType.Decimal), + (addition2Param, DataType.Decimal)); + + CreateMicroflowCallActivity(currentApp, callingMicroflow, additionMicroflow, + outputVariableName, + (addition1Param, "1.2"), + (addition2Param, "2.2")); +} +``` + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/addition-microflow.png" >}} + +Once a microflow is created, in order to enable this microflow to be called by other microflows, you need to add a call activity (`IActionActivity`). In the example, you have a method called `CreatMicroflowCallActivity` that can be used by both your multiplication and addition microflows. + +There are a few prerequisites that you must complete before a microflow can be called by another microflow. This method can be broken down into parts: + +```csharp +var microflowCallActivity = currentApp.Create(); +var microflowCallAction = currentApp.Create(); +microflowCallAction.MicroflowCall = currentApp.Create(); +microflowCallAction.MicroflowCall.Microflow = calledMicroflow.QualifiedName; +microflowCallActivity.Action = microflowCallAction; + +microflowCallAction.OutputVariableName = outputVariableName; +``` + +In order to create `IActionActivity`, `IMicroflowCallAction` must also be created, and set as the `Action` property of the `IActionActivity`. + +Then, for `IMicroflowCallAction`, `IMicroflowCall` must also be created and set as the `MicroflowCall` property of the `IMicroflowCallAction`. + +Next, `QualifiedName` of the microflow, which is to be called by this activity, must be set as the `Microflow` property of the `MicroflowCall` object. + +Finally, you can set `OutputVariableName` on `IActionActivity`, which is what the calling microflow will read from the called microflow. + +## Passing Parameters + +It is also possible to pass a set of parameters to the action activity, which will be the inputs for the called microflow. This set of parameters is a simple `Tuple` of a name and an expression. In the example, these parameters are the two integers for the multiplication microflow and the two decimals for the addition microflow. + +```csharp +foreach (var (parameterName, expression) in parameters) +{ + var parameterInCalledMicroflow = microflowService.GetParameters(calledMicroflow).Single(p => p.Name == parameterName); + var parameterMapping = currentApp.Create(); + parameterMapping.Argument = microflowExpressionService.CreateFromString(expression); + parameterMapping.Parameter = parameterInCalledMicroflow.QualifiedName; + microflowCallAction.MicroflowCall.AddParameterMapping(parameterMapping); +} +``` + +The method in its entirety is below and can be pasted into your `CalculationsMicroflowCreator` class. + +```csharp +void CreateMicroflowCallActivity(IModel currentApp, + IMicroflow microflowThatCalls, + IMicroflow calledMicroflow, + string outputVariableName, + params (string parameterName, string expression)[] parameters) +{ + var microflowCallActivity = currentApp.Create(); + var microflowCallAction = currentApp.Create(); + microflowCallAction.MicroflowCall = currentApp.Create(); + microflowCallAction.MicroflowCall.Microflow = calledMicroflow.QualifiedName; + microflowCallActivity.Action = microflowCallAction; + microflowCallAction.OutputVariableName = outputVariableName; + + foreach (var (parameterName, expression) in parameters) + { + var parameterInCalledMicroflow = microflowService.GetParameters(calledMicroflow).Single(p => p.Name == parameterName); + var parameterMapping = currentApp.Create(); + parameterMapping.Argument = microflowExpressionService.CreateFromString(expression); + parameterMapping.Parameter = parameterInCalledMicroflow.QualifiedName; + microflowCallAction.MicroflowCall.AddParameterMapping(parameterMapping); + } + + microflowService.TryInsertAfterStart(microflowThatCalls, microflowCallActivity); +} +``` + +To create a call activity for your multiplication and addition microflows, you can use something like the code below. As you can see, the parameter names for the activity match the parameter name from the microflow and their values are also passed in for integers and decimals. + +```csharp +CreateMicroflowCallActivity(currentApp, callingMicroflow, mathMicroflow, + outputVariableName, + ("multiplication1", "10"), + ("multiplication2", "100")); + +CreateMicroflowCallActivity(currentApp, callingMicroflow, additionMicroflow, + outputVariableName, + ("addition1", "1.2"), + ("addition2", "2.2")); +``` + +The calling microflow looks as follows: + +{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/main-microflow.png" >}} + +## Java Actions + +Outside of this calculation examples, you might want to create a microflow activity that calls a Java action file. See below for how to add an activity, and action and a call to the microflow to achieve that. Same as in the previous examples, you have to do this inside a transaction (`IModel.StartTransaction`). + +First, create an `IActionActivity`, just like the calculation example above, but then, its `Action` property will have the type `IJavaActionCallAction` instead of `IMicroflowCallAction`. This `IJavaActionCallAction` will need to know which `IJavaAction` is linked to. You can achieve this by setting the property `JavaAction` on the `IJavaActionCallAction` object to the `IQualifiedName` of the `IJavaAction`. If you are creating a brand new `IJavaAction`, it is important to add it to the module before accessing its `IQualifiedName`. If you have `IJavaAction` already, and you want to set up a call for that one, find it in the app and pass along its `IQualifiedName`. See below for an example. + +```csharp +public void CreateMicroflowAndJavaAction(IModule module, IModel currentApp) +{ + using var transaction = currentApp.StartTransaction("Create microflows"); + + var microflow = microflowService.CreateMicroflow(currentApp, module, "Microflow"); + + var javaActionActivity = currentApp.Create(); + var javaCallAction = currentApp.Create(); + var javaAction = currentApp.Create(); + javaAction.Name = "java_action"; + + // must add Java action file to module before using its qualified name + module.AddDocument(javaAction); + + javaCallAction.JavaAction = javaAction.QualifiedName; + javaActionActivity.Action = javaCallAction; + + microflowService.TryInsertAfterStart(microflow, javaActionActivity); + + transaction.Commit(); +} +``` + +If you already have a Java action file that you previously created, simply pass its `IQualifiedName` to the Java action. You will need to query the model in order to retrieve the actual object. You can do so as follows: + +```csharp +IQualifiedName FindJavaAction(string name, IModule module) +{ + var javaAction = module.GetDocuments().OfType().Single(ja => ja.Name == name); + return javaAction.QualifiedName; +} +``` + +Download the [whole code](https://github.com/mendix/ExtensionAPI-Samples) to see the way it works in its entirety. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/create-modal-web-view.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/create-modal-web-view.md new file mode 100644 index 00000000000..5076a53f137 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/create-modal-web-view.md @@ -0,0 +1,171 @@ +--- +title: "Create a Web View Hosted Inside a Modal Dialog Using C#" +linktitle: "Modal Web View" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-10/create-modal-web-view/ +weight: 7 +--- + +## Introduction + +This how-to describes how you can create a new web view hosted inside a modal dialog. You will then open the modal from a new menu item. + +You can download the example in this how-to in [this GitHub repository](https://github.com/mendix/ExtensionAPI-Samples) + +## Adding a View Model for Your New Modal + +Add a view model for your new model. The view model takes care of the messages and the overall lifecycle of the modal web view. + +```csharp +namespace MyCompany.MyProject.MendixExtension; + +class MyModalWebViewViewModel( + string title, + IModel currentApp, + IDialogService dialogService, + IMessageBoxService messageBoxService, + Uri webServerBaseUrl) : WebViewModalDialogViewModel(title) +{ + public override void InitWebView(IWebView webView) + { + webView.MessageReceived += Browser_MessageReceived; + OnClosing = HandleOnClosed; + webView.Address = new Uri(webServerBaseUrl + "index"); + } + + void HandleOnClosed(CancelEventArgs cancelEventArgs) => messageBoxService.ShowInformation("Entity was created."); + + void Browser_MessageReceived(object? sender, MessageReceivedEventArgs e) + { + using var transaction = currentApp.StartTransaction("create entity from modal"); + + var entity = currentApp.Create(); + entity.Name = e.Message.Replace("\\", "").Replace("\"", ""); + currentApp.Root.GetModules().First(m => m.Name == "MyFirstModule").DomainModel.AddEntity(entity); + + transaction.Commit(); + + dialogService.CloseDialog(this); + } +} +``` + +{{% alert color="warning" %}} +{{% snippet file="/static/_includes/apidocs-mxsdk/warning-wwwroot.md" %}} +{{% /alert %}} + +## Adding a Controller Class + +You are creating a controller to delegate specific tasks, allowing you to keep the menu item free of unrelated business logic, for instance, conducting basic setup and displaying the web view. This approach is recommended, but not mandatory. + +{{% alert color="info" %}} +Specify both `Height` and `Width` properties; otherwise, they will default to 0,0. +{{% /alert %}} + +```csharp +namespace MyCompany.MyProject.MendixExtension; + +[method: ImportingConstructor] +[Export(typeof(MyModalWebViewController))] +class MyModalWebViewController(IDialogService dialogService, IMessageBoxService messageBoxService) +{ + public void ShowDialog(IModel currentApp, Uri webServerBaseUrl) + { + var modalViewModel = new MyModalWebViewViewModel("Modal WebView", currentApp, dialogService, messageBoxService, webServerBaseUrl) + { + Height = 160, Width = 400, + }; + + dialogService.ShowDialog(modalViewModel); + } +} +``` + +## Adding a Content Server + +To open static pages, you need to source them from either a file system path or via the `WebServerExtension` route. This document covers the latter, as this is the preferred way to provide static web content to an extension. + +```csharp +namespace MyCompany.MyProject.MendixExtension; + +[Export(typeof(WebServerExtension))] +class ContentServer : WebServerExtension +{ + private const string Content = """ + + + + + +
+

Entity Name

+ +

+
+ + +"""; + + public override void InitializeWebServer(IWebServer webServer) + { + webServer.AddRoute("index", async (_, response, _) => + { + response.ContentType = "text/html"; + response.StatusCode = 200; + var content = Encoding.ASCII.GetBytes(Content); + response.ContentLength64 = content.Length; + await response.OutputStream.WriteAsync(content, CancellationToken.None); + }); + } +} +``` + +## Adding a Menu Item That Opens the Modal Dialog + +Finally, you need to add a menu item to open the dialog. Replace the contents of `MyMenuExtension.cs` with the code below: + +```csharp +namespace MyCompany.MyProject.MendixExtension; + +[Export(typeof(MenuExtension))] +[method: ImportingConstructor] +class MyMenuExtension(MyModalWebViewController myModalWebViewController) : MenuExtension +{ + public override IEnumerable GetMenus() + { + yield return new MenuViewModel("Create Entity From Dialog", () => myModalWebViewController.ShowDialog(CurrentApp!, WebServerBaseUrl)); + } +} +``` + +These changes inject your new controller class into the `MyMenuExtension` class. Then you add a new menu item called `Create Entity From Dialog` and call the controller's `ShowDialog` method. + +Note that in this example, the parameter `currentApp` will be necessary, if the dialog needs to interact with the model. Additionally, `WebServerBaseUrl` is crucial, because, without the base path, navigating to the route you defined in the web server extension would not be possible. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/export-an-extension.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/export-an-extension.md new file mode 100644 index 00000000000..11107134efb --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/export-an-extension.md @@ -0,0 +1,33 @@ +--- +title: "Export a C# Extension" +linktitle: "Export Extension" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-10/export-an-extension/ +weight: 99 +--- + +## Introduction + +This how-to describes how you can export an extension so that you can publish it in the Marketplace or directly share it with other Mendix developers. + +## Prerequisites + +To be able to export extension add-on modules, you need to have the feature flag `--enable-extension-development` enabled. For more information, see [Get Started with the Extensibility API](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/get-started/). + +## Procedure + +1. In Studio Pro, open the app that contains your development extension. +2. Create a new module with the same name as your extension folder. For example, if your extension folder is called 'MyFirstExtension', your module must also be called 'MyFirstExtension'. +3. In **App Explorer**, go to **Settings** of this module and then go to the **Export** tab. + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/extensibility-api-howtos/export-an-extension/export-tab.png" max-width=80% >}} + +4. Set **Module type** to *Add-on module*. +5. In the **Module version** field, enter the version number of the extension. +6. In the **Extension Name** field, select the name of your extension. This must match with your module name. + + {{% alert color="info" %}} If you do not set the **Extension name** field, your feature flag is not configured correctly. For more information, see [Get Started with the Extensibility API](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/get-started/). {{% /alert %}} + +7. Click **OK** to save the settings. +8. In **App Explorer**, right-click the module and click **Export** to export the extension. + +Now you can [publish the extension in the Marketplace](/appstore/submit-content/#adding) or directly share it with other Mendix developers. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/interact-with-model-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/interact-with-model-api.md new file mode 100644 index 00000000000..d5d07680639 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/interact-with-model-api.md @@ -0,0 +1,61 @@ +--- +title: "Interact with the Model API Using C#" +linktitle: "Model Interaction" +url: /apidocs-mxsdk/apidocs/interact-with-model-api-10/ +weight: 11 +--- + +## Introduction + +After you create some basic extensions, you want to interact with the Studio Pro model in order to make changes to your app. The Model API allows you to do just that. The model representation is exposed via the `Mendix.StudioPro.ExtensionsAPI.Model` namespace. + +## Gaining Access to the Mendix Model SDK + +The easiest way to gain access to the model is by using the `CurrentApp` property of Extension class. All extension classes implement the [`Mendix.StudioPro.ExtensionsAPI.UI.UIExtensionBase`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.UI/UIExtensionBase.md) base class which provides this access. + +The `CurrentApp` property exposes an implementation of [`IModel`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Model/IModel.md). With a `IModel` reference you can gain access to any other model elements. However, any changes that you introduce must be contained within a model transaction. + +## Interacting with Model Elements + +Any modification to the model must be done within a transaction; otherwise, a `System.InvalidOperationException` is thrown. There can be only a single active (i.e. not committed or rolled back) `ITransaction` for the whole app. + +Transactions do not provide a way to isolate changes, but to group them. That is, any change to a model is immediately visible to all code working with the model. When transaction is rolled back programmatically or is undone by a user, all changes included in it are reverted. + +To create transaction you can call [`IModel.StartTransaction`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Model/IModel/StartTransaction.md). This method will return a transaction object which implements [`ITransaction`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Model/ITransaction.md). + +For your changes to reflect within the model, you must first commit the transaction by calling [`ITransaction.Commit`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Model/ITransaction/Commit.md). + +Alternatively, if you wish to abort or revert your changes, you can call [`ITransaction.Rollback`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Model/ITransaction/Rollback.md). + +## Examples + +The most common use case for changing an app is to change one or few properties synchronously. + +In the following example, an existing folder is renamed: + +```csharp +using (var transaction = model.StartTransaction("rename folder")) +{ + folder.Name = "New_Name"; + transaction.Commit(); +} +``` + +Here is another simple example. It adds an entity named `testEntity` and adds an attribute called `testAttribute` to it. + +```csharp +using (var transaction = model.StartTransaction("add entity")) +{ + var entity = CurrentApp.Create(); + var attr = CurrentApp.Create(); + entity.Name = "testEntity"; + attr.Name = "testAttribute"; + entity.AddAttribute(attr); + var copyEntity = CurrentApp.Copy(entity); + transaction.Commit(); +} +``` + +## Read More + +* [Understanding the Mendix Metamodel](/apidocs-mxsdk/mxsdk/mendix-metamodel/) diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/untyped-model-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/untyped-model-api.md new file mode 100644 index 00000000000..2d7c5f63c71 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-howtos/untyped-model-api.md @@ -0,0 +1,111 @@ +--- +title: "Use the Untyped Model Access API Using C#" +linktitle: "Untyped Model API" +url: /apidocs-mxsdk/apidocs/untyped-model-access-api-10/ +weight: 24 +--- + +## Introduction + +The Untyped Model Access API caters to the advanced users who are familiar with the internals of the Mendix platform. With the Untyped Model API, you can access the rich data of model elements. + +When you need to use model elements, units, and property names in the Untyped Model API, you can reference the [Mendix Model SDK](https://apidocs.rnd.mendix.com/modelsdk/latest/index.html). Moreover, you can find the type names used by the Untyped Model Access API under the [structureTypeName](https://apidocs.rnd.mendix.com/modelsdk/latest/classes/Structure.html#structureTypeName) property of any model element. + +{{% alert color="info" %}} +All methods provided by the Untyped Model API are recursive to reduce the amount of API calls necessary to get to the content you are looking for. +{{% /alert %}} + +## Prerequisites + +To see the Untyped Model API in action, as described in the examples in this document, you must create a microflow having the default name `MyFirstLogic` with an action, and add an entity to the domain model. + +## Getting Started + +To start using the Untyped Model API, import it: + +```csharp +class Sample(IUntypedModelAccessService untypedModelAccessService) +{ +} +``` + +## Gaining Access to the Model Root + +To gain access to the model `Root`, request it from the newly added API service: + +```csharp +class Sample2(IUntypedModelAccessService untypedModelAccessService, IModel currentApp) +{ + public IModelRoot GetUntypedModelRoot() => untypedModelAccessService.GetUntypedModel(currentApp); +} + +``` + +## Requesting Top-level Model Elements + +To start going through the model elements, such as `Apps` and `Modules`, choose a starting point first: + +```csharp +class Sample3(IUntypedModelAccessService untypedModelAccessService, IModel currentApp) +{ + public IModelUnit GetProjectData() => + untypedModelAccessService.GetUntypedModel(currentApp) + .GetUnitsOfType("Projects$Project") + .Single(); + + public IModelUnit GetMyModuleData() => + untypedModelAccessService.GetUntypedModel(currentApp) + .GetUnitsOfType("Projects$Module") + .Single(unit => unit.Name == "MyFirstModule"); +} +``` + +## Accessing Child Elements + +It is possible to access the child elements of a model element, such as the actions of a microflow or entities of a domain model. + +Using either `GetElements` or `GetElementsOfType`, can help you, for example, analyze these elements' properties to implement custom validation rules. See an example below: + +```csharp +class Sample4(IUntypedModelAccessService untypedModelAccessService, IModel currentApp) +{ + public IReadOnlyList GetMicroflowActionActivities() => + untypedModelAccessService.GetUntypedModel(currentApp) + .GetUnitsOfType("Projects$Module") + .Single(unit => unit.Name == "MyFirstModule") + .GetUnitsOfType("Microflows$Microflow") + .Single(unit => unit.Name == "MyFirstLogic") + .GetElementsOfType("Microflows$ActionActivity"); + + public IReadOnlyList GetDomainModelEntities() => + untypedModelAccessService.GetUntypedModel(currentApp) + .GetUnitsOfType("Projects$Module") + .Single(unit => unit.Name == "MyFirstModule") + .GetUnitsOfType("DomainModels$DomainModel").Single() + .GetElementsOfType("DomainModels$Entity"); +} +``` + +## Getting Model Unit's Properties + +In case you would like to extract data out of a model element or unit, you need to access its properties. See an example below: + +```csharp +class Sample5(IUntypedModelAccessService untypedModelAccessService, IModel currentApp) +{ + private IReadOnlyList GetProjectRuntimeSettingsProperties() => + untypedModelAccessService.GetUntypedModel(currentApp) + .GetUnitsOfType("Settings$ProjectSettings") + .Single() + .GetElements() + .Single(element => element.Type == "Settings$RuntimeSettings") + .GetProperties(); + + + public string? GetAfterStartupMicroflowId() => + GetProjectRuntimeSettingsProperties() + .Single(property => property.Name == "AfterStartupMicroflowId") + .Value? + .ToString(); +} +``` diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-reference-guide/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-reference-guide/_index.md new file mode 100644 index 00000000000..76290e6f12a --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-reference-guide/_index.md @@ -0,0 +1,16 @@ +--- +title: "Reference Guide for the C# Extensibility API" +linktitle: "Reference Guide" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-10/refguide/ +weight: 2 +no_list: false +description_list: true +--- + +## Introduction + +In the **Reference Guide**, you can explore a variety of topics related to the Extensibility API in detail. + +### Documentation in This Category + +The **Reference Guide** category contains the following documents: diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-reference-guide/extension-points.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-reference-guide/extension-points.md new file mode 100644 index 00000000000..1766c6c600f --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-reference-guide/extension-points.md @@ -0,0 +1,40 @@ +--- +title: "Extensibility Extension Points in C#" +linktitle: "Extension Points" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-10/extension-points/ +weight: 9 +--- + +## Introduction + +Extension points allow you to hook functionality into various areas of the Studio Pro IDE. Extension point is a base class that you as an extension developer can inherit from. Your functionality will then be loaded by Studio Pro. These classes all have the `*Extension` suffix, inherit from [`ExtensionBase`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI/ExtensionBase.md) base class, and contain a few virtual or abstract members. + +Extension point is the only way you can add a custom behavior to Studio Pro. The rest of the APIs are there only to aid with implementing or expressing these behaviors. + +To be injected, your class must be decorated with [ExportAttribute](https://docs.microsoft.com/en-us/dotnet/api/system.composition.exportattribute?view=dotnet-plat-ext-6.0) +like in the example above. This attribute is part of [Managed Extensibility Framework](https://docs.microsoft.com/en-us/dotnet/framework/mef/) +that is employed by Studio Pro. + +## List of Available Extension Points + +### Studio Pro UI Extensions + +* [ContextMenuExtension](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.UI.Menu/ContextMenuExtension-1.md) – This allows injecting new context menu items into model elements. + +* [MenuExtension](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.UI.Menu/MenuExtension.md) – This allows injecting new menu items into Studio Pro menu bar. + +* [DockablePaneExtension](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.UI.DockablePane/DockablePaneExtension.md) – allows introducing new + [dockable pane](/refguide/studio-pro-overview/#panes) like Connector or Documentation. Panes integrate with Studio Pro + [layout system](/refguide/view-menu/#layout-of-panes) automatically. + + {{% alert color="info" %}}It is advised to introduce a new **View** menu item for each pane, so that Studio Pro users have a way to open it.{{% /alert %}} + +Additionally, there are additional features that provide access to the following: + +* [Studio Pro configuration](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI/ExtensionBase/Configuration.md) +* [The currently opened app](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.UI/UIExtensionBase/CurrentApp.md), as well as event subscription mechanism to that app +* Events can be subscribed to by using the subscribe and unsubscribe methods exposed in [UIExtensionBase](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.UI/UIExtensionBase.md). + +### Studio Pro and MxBuild Extensions + +* [ConsistencyCheckExtension](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.ConsistencyCheck/ConsistencyCheckExtension-1.md) – This allows injecting custom logic into the [Consistency check](/refguide/consistency-errors/) process. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-reference-guide/services.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-reference-guide/services.md new file mode 100644 index 00000000000..ca3b8aa0f6e --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-reference-guide/services.md @@ -0,0 +1,26 @@ +--- +title: "Studio Pro Extensibility Services in C#" +linktitle: "Studio Pro Services" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-10/services/ +weight: 10 +--- + +## Introduction + +A Studio Pro service is an interface that exposes some core Studio Pro functionality to extensions. These interfaces are named `I*Service` and can be found in `Mendix.StudioPro.ExtensionsAPI.Services` or `Mendix.StudioPro.ExtensionsAPI.UI.Services` namespaces. It can be injected using the Microsoft Extensions Framework, also referred to as MEF. For more information about MEF and how to use it please refer to the official [Microsoft documentation](https://learn.microsoft.com/en-us/dotnet/framework/mef/) + +{{% alert color="info" %}}You should not implement these interfaces in your production code, although it is possible to make sense to do so for unit testing purposes.{{% /alert %}} + +## List of Available Services + +* Helpers to operate on the app model or parts of it: + * [`IMicroflowService`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Services/IMicroflowService.md) + * [`IMicroflowExpressionService`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Services/IMicroflowExpressionService.md) +* Services giving access to interactive operations: + * [`IAppService`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.UI.Services/IAppService.md) + * [`ISelectorDialogService`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.UI.Services/ISelectorDialogService.md) + * [`IDockingWindowService`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.UI.Services/IDockingWindowService.md) +* Utility services: + * [`IConfigurationService`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Services/IConfigurationService.md) + * [`ILogService`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Services/ILogService.md) + * [`IExtensionFileService`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Services/IExtensionFileService.md) diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-reference-guide/web-view.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-reference-guide/web-view.md new file mode 100644 index 00000000000..498479061df --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/extensibility-api-reference-guide/web-view.md @@ -0,0 +1,46 @@ +--- +title: "Extensibility Web Views in C#" +linktitle: "Web Views" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-10/web-views/ +weight: 12 +--- + +## Introduction + +In all places where the Studio Pro Extensibility API allows you to add custom UI, you can use web technology to implement the UI. + +Studio Pro contains a built-in web view that you can leverage to show your web-based UI. +Studio Pro also contains a built-in web server that can be used to serve the web UI, as well as to serve data to the web UI. + +In addition, there is a two-way message passing mechanism for direct communication between the web content and the C# part of your extension. + +## Showing a Web View in the UI + +There are a number of places where the Studio Pro Extensibility API allows you to add custom UI. + +Typically, the Extensibility API requires you to return a view model for your UI, and for every view model type, there is a corresponding base class for showing the UI in a web view. + +The following table shows the APIs that allow you to add custom UI, and the corresponding view model base class: + +| UI element | API for adding UI | Base class for view model | +|--------------------------------|------------------------------------------|--------------------------------| +| Tab (in working area) | `IDockingWindowManager.OpenTab(...)` | `WebViewTabViewModel` | +| Document tab (in working area) | `AppExplorerExtension.EditDocument(...)` | `WebViewDocumentTabViewModel` | +| Dockable pane | `DockablePaneExtension.Open(...)` | `WebViewDockablePaneViewModel` | +| Modal dialog | `IDialogService.ShowDialog(...)` | `WebViewModalDialogViewModel` | + +The view model base classes are `abstract`, so you are required to create your own view model class that derives from the base class. + +Each view model class has a method called `InitWebView` that you must override to initialize the web view. + +In this method, you can tell the web view to navigate to the (local) URL that contains your web content. + +In addition, the view model class can be used to house the logic for communicating with the web view. + +## Serving Content to the Web View + +For serving content to the web view and communicating both ways with it, see [Build a Todo Example Extension](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/build-todo-example-extension/). + +{{% alert color="warning" %}} +{{% snippet file="/static/_includes/apidocs-mxsdk/warning-wwwroot.md" %}} +{{% /alert %}} diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/get-started.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/get-started.md new file mode 100644 index 00000000000..fcbb0eb7c65 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/csharp/get-started.md @@ -0,0 +1,69 @@ +--- +title: "Get Started with the C# Extensibility API" +linktitle: "Get Started" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-10/get-started/ +weight: 2 +--- + +## Introduction + +This document helps you set up a basic development environment for building extensions. Additionally, you can find links here to additional tutorials that help familiarize you to the extensibility API topics. + +## Development Setup + +The recommended development environment is [Visual Studio 2022](https://visualstudio.microsoft.com/) running on Windows. This documentation is centered on this setup. (You can also use other IDEs and other .NET compatible programming languages like [F#](https://fsharp.org/)). + +Install the latest Studio Pro version from the Mendix [Marketplace](https://marketplace.mendix.com/link/studiopro/). + +{{% alert color="info" %}} +Make sure to keep this Studio Pro installation up-to-date to benefit from new features and fixes. +{{% /alert %}} + +{{% alert color="info" %}} +Extensions can be built on any operating system as the underlying framework is cross-platform. +{{% /alert %}} + +## Using Extensibility API via a Hosted NuGet Package + +You can start extension development by simply including the `Mendix.StudioPro.ExtensionsAPI` NuGet package to your project by searching for *Mendix.Studio.ExtensionsAPI* in NuGet. + +{{% alert color="warning" %}}The initial released package version compatible with Mendix 10.12.0 was 10.12.38909. This has been updated to 10.12.0+38909 to match the Studio Pro version. +{{% /alert %}} + +Depending on your local environment setup, you possibly need to manually add a NuGet package to your solution. You can add a package source to Visual Studio via the menu: **Tools** > **Options** > **NuGet Package Manager** > **Package Sources** + +## Importing Extensibility API via a NuGet Package Hosted on a Local Repository + +Another option to start extension development is to import a locally hosted `Mendix.StudioPro.ExtensionsAPI` NuGet package into your project. + +To create a local NuGet repository that will let you host and use NuGet packages shared with you, follow the procedure below: + +1. Go to **Tools** > **Options** > **NuGet Package Manager** > **Package Sources**. +2. Press the green plus sign and specify a local folder, instead of a network location. +3. Drop the package into the local folder. +4. Refresh the NuGet manager to see the package alongside the other packages. + +{{% alert color="info" %}} +To make the search process easier, you can specifically select a certain package source inside the NuGet manager window. +{{% /alert %}} + +{{% alert color="info" %}} +For more information on local NuGet repositories, see the official [Local Feeds](https://learn.microsoft.com/en-us/nuget/hosting-packages/local-feeds) in *Microsoft Documentation*. +{{% /alert %}} + +## Extensions Development Setup + +Extensions load from within your mendix application and will only be loaded while the app is open. + +If you specify the `--enable-extension-development` flag you can debug your extension by placing it into a new subfolder within your app directory. The format of the folder must be `\extensions\` where all extensions that you want to load must exist inside the <`Mendix app folder>\extensions` subfolder. + +Studio Pro will load your extension, only if it complies with the following rules: + +* Your extension needs to provide a manifest.json file. +* This manifest file must contain a list of entry points for your extension. For example: + + ``` + { + "mx_extensions": [ "MyExtension.dll" ] + } + ``` diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/packaging-your-extension.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/packaging-your-extension.md similarity index 95% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/packaging-your-extension.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/packaging-your-extension.md index db1dd8e2037..aee884bb178 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/packaging-your-extension.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/packaging-your-extension.md @@ -1,29 +1,29 @@ ---- -title: "Packaging Your Extension" -url: /apidocs-mxsdk/apidocs/extensibility-api/packaging-your-extension -weight: 30 ---- - -# Packaging your extension - -Once you have finished development on your extension, you might want to package it into an add-on module so that others can start using it. Once you have created the add-on module, it can then be published to the Mendix Marketplace for your extension users to download into their Studio Pro app. - -To package your extension, you will still need the `--enable-extension-development` command line option turned on. Create a new module in your Studio Pro app containing your dev extension, give it an appropriate name. Open the module's settings form and set it to be an Add-on module. In the `Extension name` dropdown, select the extension you want to package into it. - -![Extension Add-on Module](/attachments/apidocs-mxsdk/apidocs/extensibility-api/extensionAddOnModule.png) - -After you've created your add-on module with its extension, you can now export it, by right-clicking the module in the App Explorer and choosing `Export add-on module package`, as shown below. - -![Export Module](/attachments/apidocs-mxsdk/apidocs/extensibility-api/exportAddOnModule.png) - -You can now save the add-on module to a location of your choice. - -# Importing the extension add-on module - -Once the add-on module is available to a Studio Pro user, they are now able to add it in their application. They can so so by right-clicking the app in the App Explorer and choosing `Import module package`, as shown below. - -![Import Module](/attachments/apidocs-mxsdk/apidocs/extensibility-api/importAddOnModule.png) - -Once an add-on module containing an extension is imported in the app, Studio Pro will show a warning to the user, asking to trust the extension contained in it. If the user does not choose to trust, the module will still be imported but the extension inside it won't be loaded. - +--- +title: "Packaging Your Extension" +url: /apidocs-mxsdk/apidocs/extensibility-api-10/packaging-your-extension +weight: 30 +--- + +# Packaging your extension + +Once you have finished development on your extension, you might want to package it into an add-on module so that others can start using it. Once you have created the add-on module, it can then be published to the Mendix Marketplace for your extension users to download into their Studio Pro app. + +To package your extension, you will still need the `--enable-extension-development` command line option turned on. Create a new module in your Studio Pro app containing your dev extension, give it an appropriate name. Open the module's settings form and set it to be an Add-on module. In the `Extension name` dropdown, select the extension you want to package into it. + +![Extension Add-on Module](/attachments/apidocs-mxsdk/apidocs/extensibility-api/extensionAddOnModule.png) + +After you've created your add-on module with its extension, you can now export it, by right-clicking the module in the App Explorer and choosing `Export add-on module package`, as shown below. + +![Export Module](/attachments/apidocs-mxsdk/apidocs/extensibility-api/exportAddOnModule.png) + +You can now save the add-on module to a location of your choice. + +# Importing the extension add-on module + +Once the add-on module is available to a Studio Pro user, they are now able to add it in their application. They can so so by right-clicking the app in the App Explorer and choosing `Import module package`, as shown below. + +![Import Module](/attachments/apidocs-mxsdk/apidocs/extensibility-api/importAddOnModule.png) + +Once an add-on module containing an extension is imported in the app, Studio Pro will show a warning to the user, asking to trust the extension contained in it. If the user does not choose to trust, the module will still be imported but the extension inside it won't be loaded. + ![Trust Extension](/attachments/apidocs-mxsdk/apidocs/extensibility-api/trustExtension.png) \ No newline at end of file diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/_index.md new file mode 100644 index 00000000000..27e6fb7c857 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/_index.md @@ -0,0 +1,37 @@ +--- +title: "Extensibility API for Web Developers" +linktitle: "Web Extensibility API" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-10/ +description: "The web extensibility API allows your custom Studio Pro 10 extensions developed using JavaScript to interact with some internal services of Studio Pro." +weight: 20 +--- + +{{% alert color="warning" %}} This feature is in beta. For more information, see [Beta Releases](/releasenotes/beta-features/). {{% /alert %}} + +{{% alert color="info" %}}For information on new releases of the Extensibility API, see [Extensibility: Web API Release Notes](/releasenotes/studio-pro/web-extensibility-api/). +{{% /alert %}} + +## Introduction + +Extensions can be written in Typescript or other web languages, described here, or using a C# API which is documented separately in [Extensibility API for C# Developers](/apidocs-mxsdk/apidocs/csharp-extensibility-api-10/). + +## Prerequisites + +* You need at least a basic understanding of the Mendix platform. +* You need some understanding of the Mendix Model. +* You need to have some TypeScript development experience. + +## Getting Started + +For detailed explanation on how to get started with extensions, check out [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-10/getting-started/). + +## How-tos + +Here is a list of how-tos for you to begin with: + +* [How to Create a Dockable Pane Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-10/dockable-pane-api/) +* [How to Interact With Local App Files Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-10/local-app-files-api/) +* [How to Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-10/menu-api/) +* [How to Show a Message Box Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-10/messagebox-api/) +* [How to Access a Mendix Model Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-10/model-api/) +* [How to Open a Tab Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-10/tab-api/) diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/get-started.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/get-started.md new file mode 100644 index 00000000000..7a56f76a448 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/get-started.md @@ -0,0 +1,124 @@ +--- +title: "Get Started with the Web Extensibility API" +linktitle: "Get Started" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-10/getting-started/ +weight: 2 +--- + +## Introduction + +Studio Pro extensions can be developed using typescript and use standard web development technologies to extend the Studio Pro development environment. This guide shows you how to set up a basic development environment for building an extension using the web extensibility API. + +### Prerequisites + +You will need the following prerequisites: + +* Mendix Studio Pro version 10.21.0 or higher [Mendix Studio Pro](https://marketplace.mendix.com/link/studiopro). +* Install the latest Studio Pro version from the Mendix [Marketplace](https://marketplace.mendix.com/link/studiopro/). +* A development IDE to develop your extensions. We recommend using [Visual Studio Code](https://code.visualstudio.com/). +* Install the latest version 22.x.x of Node: https://nodejs.org/en/download. + +{{% alert color="info" %}} +Extensions can be built on any operating system as the underlying framework is cross-platform. +{{% /alert %}} + +## Creating Your First Extension + +This section will show you how to build and test an extension. + +### Create a Test App + +1. Create a new app using the **Blank Web App** template. +1. Install the [Studio Pro Web Extension Template](https://github.com/mendix/web-extension-template) from GitHub using the instructions in the repository. + +### Building the Extension + +From within Visual Studio Code: + +1. Select **File** -> **Open Folder** +1. Navigate to the folder you just extracted your extension source code to. +1. Click **Select Folder**. +1. Select **Yes** if you are asked whether you trust this folder. +1. Now open a Terminal by selecting **Terminal** -> **New Terminal** from the top menu. +1. From the Terminal type `npm install`. This installs all dependencies for the extension +1. Build your extension using the command `npm run build` in the terminal. + +Once completed you should now have a build artifact which we can deploy to your Mendix app. + +You can explore the extension a bit more to understand what it will do when it is installed. Do the following: + +1. From the Explorer window navigate to `src/main/index.ts` select it to open the file. + + Reading through the source code you should see the following: + +1. Line 7 adds a menu + + ```typescript + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "MyExtension Menu", + subMenus: [{ menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }], + }); + ``` + +1. Line 14 opens a tab + + ```typescript + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener("menuItemActivated", (args) => { + if (args.menuId === "myextension.ShowTabMenuItem") { + studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + } + }); + ``` + +When you install the extension you will see a new menu item within Studio Pro. + +### Testing the Extension + +To test the extension, do the following in File Explorer. + +1. Navigate to the folder where you extracted the extension source code. +1. Open the `dist` folder. +1. Copy the `myextension` folder. +1. Navigate to the folder where you created your app. +1. Create a new folder called `webextensions`. +1. Paste the `myextension` folder into the `webextensions` folder you just created. + + The extension files have now been added to the app. + +1. Start Studio Pro with the following command line parameters to tell it to use the extensions in the folder. + + `--enable-extension-development --enable-webview-debugging` + + These flags instruct Studio Pro to do the following: + + * Load extensions from the webextensions folder + * Enable web debugging tools which will be useful when developing your extension + +1. In Studio Pro, open the new app. + + You will see a new `Extensions` item in the top menu. + +## Conclusion + +Using this guide we have: + +* Created a new app +* Downloaded a new extension from GitHub +* Built the extension and installed it in our app +* Tested our extension from within Studio Pro. + +## Extensibility Feedback + +If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) + +Any feedback is much appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/_index.md new file mode 100644 index 00000000000..f887ed255fc --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/_index.md @@ -0,0 +1,12 @@ +--- +title: "Web Extensibility API How-tos" +linktitle: "How-tos" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-10/how-tos/ +weight: 3 +no_list: false +description_list: true +--- + +## Introduction + +The following how-tos teach you how to use the Extensibility API for Web Developers in different use cases. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/dockable-pane-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/dockable-pane-api.md similarity index 95% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/dockable-pane-api.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/dockable-pane-api.md index 935bc215407..826c0a178e6 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/dockable-pane-api.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/dockable-pane-api.md @@ -1,349 +1,349 @@ ---- -title: "Create a Dockable Pane Using Web API" -linktitle: "Dockable Pane" -url: /apidocs-mxsdk/apidocs/web-extensibility-api/dockable-pane-api/ -weight: 10 ---- - -## Introduction - -This guide explains how to create and manage a dockable pane using the web extensions API. A Dockable pane allows you to create a web view that can be docked and moved within the Studio Pro user interface. Examples of dockable panes in Studio Pro are: - -* Marketplace -* Errors -* Stories -* Toolbox - -## Prerequisites - -This guide uses the app created in [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api/getting-started/). Please complete that how-to before starting this one. - -## Creating a Dockable Pane - -To open a dockable pane you must first register the dockable pane handle with the API. To do this, add a call to register the pane to the extension loaded method in the `src/main/index.ts`. - -```typescript - const paneHandle = await studioPro.ui.panes.register( - { - title: "My Extension Pane", - initialPosition: "right", - }, - { - componentName: "extension/myextension", - uiEntrypoint: "dockablepane", - }); -``` - -Use the 'paneHandle' you registered to interact with the dockable pane. - -After adding this call the `loaded()` method looks like this: - -```typescript {hl_lines=["11-19"]} - async loaded() { - // Add a menu item to the Extensions menu - await studioPro.ui.extensionsMenu.add({ - menuId: "myextension.MainMenu", - caption: "MyExtension Menu", - subMenus: [ - { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, - ], - }); - - const paneHandle = await studioPro.ui.panes.register( - { - title: "My Extension Pane", - initialPosition: "right", - }, - { - componentName: "extension/myextension", - uiEntrypoint: "dockablepane", - }); - - // Open a tab when the menu item is clicked - studioPro.ui.extensionsMenu.addEventListener( - "menuItemActivated", - (args) => { - if (args.menuId === "myextension.ShowTabMenuItem") { - studioPro.ui.tabs.open( - { - title: "My Extension Tab", - }, - { - componentName: "extension/myextension", - uiEntrypoint: "tab", - } - ); - } - } - ); - } -``` - -## Adding a Menu To Open the Dockable Pane - -You will now add a menu that will open the pane when it is selected. - -1. Add a new submenu to the existing `extensionsMenu.add()` method on line 10. - - ```typescript {linenos=table linenostart=10} - // Add a menu item to the Extensions menu - await studioPro.ui.extensionsMenu.add({ - menuId: "myextension.MainMenu", - caption: "MyExtension Menu", - subMenus: [ - { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, - { menuId: "myextension.ShowDockMenuItem", caption: "Show dock pane" }, - ], - }); - ``` - -1. Add lines to the `addEventListener()` call to handle opening the dockable pane once the menu has been selected, as follows: - - ```typescript - // Open a tab when the menu item is clicked - studioPro.ui.extensionsMenu.addEventListener( - "menuItemActivated", - (args) => { - if (args.menuId === "myextension.ShowTabMenuItem") { - studioPro.ui.tabs.open( - { - title: "My Extension Tab", - }, - { - componentName: "extension/myextension", - uiEntrypoint: "tab", - } - ); - } - else if (args.menuId === "myextension.ShowDockMenuItem") { - studioPro.ui.panes.open(paneHandle); - } - } - ); - ``` - -Your `loaded()` method should now look like this: - -```typescript {hl_lines=["3-10","22-41"]} - async loaded() { - // Add a menu item to the Extensions menu - await studioPro.ui.extensionsMenu.add({ - menuId: "myextension.MainMenu", - caption: "MyExtension Menu", - subMenus: [ - { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, - { menuId: "myextension.ShowDockMenuItem", caption: "Show dock pane" }, - ], - }); - - const paneHandle = await studioPro.ui.panes.register( - { - title: "My Extension Pane", - initialPosition: "right", - }, - { - componentName: "extension/myextension", - uiEntrypoint: "dockablepane", - }); - - // Open a tab when the menu item is clicked - studioPro.ui.extensionsMenu.addEventListener( - "menuItemActivated", - (args) => { - if (args.menuId === "myextension.ShowTabMenuItem") { - studioPro.ui.tabs.open( - { - title: "My Extension Tab", - }, - { - componentName: "extension/myextension", - uiEntrypoint: "tab", - } - ); - } - else if (args.menuId === "myextension.ShowDockMenuItem") { - studioPro.ui.panes.open(paneHandle); - } - } - ); - } -``` - -## Specifying a Web View Endpoint - -### Adding New Endpoint Handlers - -You must now create a new web view endpoint where the user interface to be rendered within the pane is defined. You can use the existing endpoint and rename it to something more appropriate. - -1. Rename `ui/index.tsx` to `ui/tab.tsx` -1. Add the new endpoint file, `ui/dockablepane.tsx` by copying `ui/tab.tsx`. - -You must also alter the `vite.config.ts` and `manifest.json` files to bind to the correct endpoint, as described in the following sections: - -### Altering `vite.config.js` - -Replace the entry section of `vite.config.js` with the following: - -```typescript - entry: { - main: "src/main/index.ts", - tab: "src/ui/tab.tsx", - dockablepane: "src/ui/dockablepane.tsx", - } -``` - -This instructs vite that the tab endpoint is connected to `src/ui/tab.tsx` and the dockable pane endpoint is connected to `src/ui/dockablepane.tsx`. - -`vite.config.js` should now look like this: - -```typescript {hl_lines=["7-11"]} -import { defineConfig, ResolvedConfig, UserConfig } from "vite"; - -export default defineConfig({ - build: { - lib: { - formats: ["es"], - entry: { - main: "src/main/index.ts", - tab: "src/ui/tab.tsx", - dockablepane: "src/ui/dockablepane.tsx", - }, - }, - rollupOptions: { - external: ["@mendix/component-framework", "@mendix/model-access-sdk"], - }, - outDir: "./dist/myextension", - }, -} satisfies UserConfig); -``` - -### Altering `public/manifest.json` - -You also need to instruct Studio Pro to load the endpoint that you just created. To do this, modify the manifest file `public/manifest.json`. - -Alter the "ui" section by changing the `tab` endpoint and adding the `dockablepane` endpoint. - -```typescript - "ui": { - "tab": "tab.js", - "dockablepane": "dockablepane.js" - } -``` - -The `manifest.json file` should now look like this: - -```typescript {hl_lines=["5-8"]} -{ - "mendixComponent": { - "entryPoints": { - "main": "main.js", - "ui": { - "tab": "tab.js", - "dockablepane": "dockablepane.js" - } - } - } -} -``` - -## Closing the Dockable Pane - -Now that you have registered a pane and can open, it would also be a good idea to close it. - -You will close your pane using a new menu item. - -First add a new sub menu item to the menu on line 11. - -```typescript {linenos=table linenostart=11} - { menuId: "myextension.HideDockMenuItem", caption: "Hide dock pane" }, -``` - -You must also alter the event handler for the new menu at the end of the loaded method: - -```typescript - // Open a tab when the menu item is clicked - studioPro.ui.extensionsMenu.addEventListener( - "menuItemActivated", - (args) => { - if (args.menuId === "myextension.ShowTabMenuItem") { - studioPro.ui.tabs.open( - { - title: "My Extension Tab", - }, - { - componentName: "extension/myextension", - uiEntrypoint: "tab", - } - ); - } - else if (args.menuId === "myextension.ShowDockMenuItem") { - studioPro.ui.panes.open(paneHandle); - } - else if (args.menuId === "myextension.HideDockMenuItem") { - studioPro.ui.panes.close(paneHandle); - } - } - ); -``` - -The loaded method should now look like this: - -```typescript {hl_lines=["9","24-45"]} - async loaded() { - // Add a menu item to the Extensions menu - await studioPro.ui.extensionsMenu.add({ - menuId: "myextension.MainMenu", - caption: "MyExtension Menu", - subMenus: [ - { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, - { menuId: "myextension.ShowDockMenuItem", caption: "Show dock pane" }, - { menuId: "myextension.HideDockMenuItem", caption: "Hide dock pane" }, - ], - }); - - const paneHandle = await studioPro.ui.panes.register( - { - title: "My Extension Pane", - initialPosition: "right", - }, - { - componentName: "extension/myextension", - uiEntrypoint: "dockablepane", - }); - - // Open a tab when the menu item is clicked - studioPro.ui.extensionsMenu.addEventListener( - "menuItemActivated", - (args) => { - if (args.menuId === "myextension.ShowTabMenuItem") { - studioPro.ui.tabs.open( - { - title: "My Extension Tab", - }, - { - componentName: "extension/myextension", - uiEntrypoint: "tab", - } - ); - } - else if (args.menuId === "myextension.ShowDockMenuItem") { - studioPro.ui.panes.open(paneHandle); - } - else if (args.menuId === "myextension.HideDockMenuItem") { - studioPro.ui.panes.close(paneHandle); - } - } - ); - } -``` - -## Conclusion - -You now have a new dockable pane with its own user interface which you can modify as you like. -You can also open and close the dockable pane from a menu. - -## Extensibility Feedback - -If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) - -Any feedback is much appreciated. +--- +title: "Create a Dockable Pane Using Web API" +linktitle: "Dockable Pane" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-10/dockable-pane-api/ +weight: 10 +--- + +## Introduction + +This guide explains how to create and manage a dockable pane using the web extensions API. A Dockable pane allows you to create a web view that can be docked and moved within the Studio Pro user interface. Examples of dockable panes in Studio Pro are: + +* Marketplace +* Errors +* Stories +* Toolbox + +## Prerequisites + +This guide uses the app created in [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-10/getting-started/). Please complete that how-to before starting this one. + +## Creating a Dockable Pane + +To open a dockable pane you must first register the dockable pane handle with the API. To do this, add a call to register the pane to the extension loaded method in the `src/main/index.ts`. + +```typescript + const paneHandle = await studioPro.ui.panes.register( + { + title: "My Extension Pane", + initialPosition: "right", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "dockablepane", + }); +``` + +Use the 'paneHandle' you registered to interact with the dockable pane. + +After adding this call the `loaded()` method looks like this: + +```typescript {hl_lines=["11-19"]} + async loaded() { + // Add a menu item to the Extensions menu + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "MyExtension Menu", + subMenus: [ + { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, + ], + }); + + const paneHandle = await studioPro.ui.panes.register( + { + title: "My Extension Pane", + initialPosition: "right", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "dockablepane", + }); + + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + (args) => { + if (args.menuId === "myextension.ShowTabMenuItem") { + studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + } + } + ); + } +``` + +## Adding a Menu To Open the Dockable Pane + +You will now add a menu that will open the pane when it is selected. + +1. Add a new submenu to the existing `extensionsMenu.add()` method on line 10. + + ```typescript {linenos=table linenostart=10} + // Add a menu item to the Extensions menu + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "MyExtension Menu", + subMenus: [ + { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, + { menuId: "myextension.ShowDockMenuItem", caption: "Show dock pane" }, + ], + }); + ``` + +1. Add lines to the `addEventListener()` call to handle opening the dockable pane once the menu has been selected, as follows: + + ```typescript + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + (args) => { + if (args.menuId === "myextension.ShowTabMenuItem") { + studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + } + else if (args.menuId === "myextension.ShowDockMenuItem") { + studioPro.ui.panes.open(paneHandle); + } + } + ); + ``` + +Your `loaded()` method should now look like this: + +```typescript {hl_lines=["3-10","22-41"]} + async loaded() { + // Add a menu item to the Extensions menu + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "MyExtension Menu", + subMenus: [ + { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, + { menuId: "myextension.ShowDockMenuItem", caption: "Show dock pane" }, + ], + }); + + const paneHandle = await studioPro.ui.panes.register( + { + title: "My Extension Pane", + initialPosition: "right", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "dockablepane", + }); + + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + (args) => { + if (args.menuId === "myextension.ShowTabMenuItem") { + studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + } + else if (args.menuId === "myextension.ShowDockMenuItem") { + studioPro.ui.panes.open(paneHandle); + } + } + ); + } +``` + +## Specifying a Web View Endpoint + +### Adding New Endpoint Handlers + +You must now create a new web view endpoint where the user interface to be rendered within the pane is defined. You can use the existing endpoint and rename it to something more appropriate. + +1. Rename `ui/index.tsx` to `ui/tab.tsx` +1. Add the new endpoint file, `ui/dockablepane.tsx` by copying `ui/tab.tsx`. + +You must also alter the `vite.config.ts` and `manifest.json` files to bind to the correct endpoint, as described in the following sections: + +### Altering `vite.config.js` + +Replace the entry section of `vite.config.js` with the following: + +```typescript + entry: { + main: "src/main/index.ts", + tab: "src/ui/tab.tsx", + dockablepane: "src/ui/dockablepane.tsx", + } +``` + +This instructs vite that the tab endpoint is connected to `src/ui/tab.tsx` and the dockable pane endpoint is connected to `src/ui/dockablepane.tsx`. + +`vite.config.js` should now look like this: + +```typescript {hl_lines=["7-11"]} +import { defineConfig, ResolvedConfig, UserConfig } from "vite"; + +export default defineConfig({ + build: { + lib: { + formats: ["es"], + entry: { + main: "src/main/index.ts", + tab: "src/ui/tab.tsx", + dockablepane: "src/ui/dockablepane.tsx", + }, + }, + rollupOptions: { + external: ["@mendix/component-framework", "@mendix/model-access-sdk"], + }, + outDir: "./dist/myextension", + }, +} satisfies UserConfig); +``` + +### Altering `public/manifest.json` + +You also need to instruct Studio Pro to load the endpoint that you just created. To do this, modify the manifest file `public/manifest.json`. + +Alter the "ui" section by changing the `tab` endpoint and adding the `dockablepane` endpoint. + +```typescript + "ui": { + "tab": "tab.js", + "dockablepane": "dockablepane.js" + } +``` + +The `manifest.json file` should now look like this: + +```typescript {hl_lines=["5-8"]} +{ + "mendixComponent": { + "entryPoints": { + "main": "main.js", + "ui": { + "tab": "tab.js", + "dockablepane": "dockablepane.js" + } + } + } +} +``` + +## Closing the Dockable Pane + +Now that you have registered a pane and can open, it would also be a good idea to close it. + +You will close your pane using a new menu item. + +First add a new sub menu item to the menu on line 11. + +```typescript {linenos=table linenostart=11} + { menuId: "myextension.HideDockMenuItem", caption: "Hide dock pane" }, +``` + +You must also alter the event handler for the new menu at the end of the loaded method: + +```typescript + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + (args) => { + if (args.menuId === "myextension.ShowTabMenuItem") { + studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + } + else if (args.menuId === "myextension.ShowDockMenuItem") { + studioPro.ui.panes.open(paneHandle); + } + else if (args.menuId === "myextension.HideDockMenuItem") { + studioPro.ui.panes.close(paneHandle); + } + } + ); +``` + +The loaded method should now look like this: + +```typescript {hl_lines=["9","24-45"]} + async loaded() { + // Add a menu item to the Extensions menu + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "MyExtension Menu", + subMenus: [ + { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, + { menuId: "myextension.ShowDockMenuItem", caption: "Show dock pane" }, + { menuId: "myextension.HideDockMenuItem", caption: "Hide dock pane" }, + ], + }); + + const paneHandle = await studioPro.ui.panes.register( + { + title: "My Extension Pane", + initialPosition: "right", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "dockablepane", + }); + + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + (args) => { + if (args.menuId === "myextension.ShowTabMenuItem") { + studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + } + else if (args.menuId === "myextension.ShowDockMenuItem") { + studioPro.ui.panes.open(paneHandle); + } + else if (args.menuId === "myextension.HideDockMenuItem") { + studioPro.ui.panes.close(paneHandle); + } + } + ); + } +``` + +## Conclusion + +You now have a new dockable pane with its own user interface which you can modify as you like. +You can also open and close the dockable pane from a menu. + +## Extensibility Feedback + +If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) + +Any feedback is much appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/local-app-files-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/local-app-files-api.md similarity index 92% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/local-app-files-api.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/local-app-files-api.md index d44ef63e916..3c104381a73 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/local-app-files-api.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/local-app-files-api.md @@ -1,138 +1,138 @@ ---- -title: "Interact With Local App Files Using Web API" -linktitle: "Local Files" -url: /apidocs-mxsdk/apidocs/web-extensibility-api/local-app-files-api/ -weight: 20 ---- - -## Introduction - -This how-to shows you how to interact with local application files from within an extension. It adds three new buttons to the tab and three new event handlers for saving, loading, and deleting a file called `HelloWorld.txt`. - -## Prerequisites - -This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api/getting-started/). Please complete that how-to before starting this one. - -## Adding Some Interactivity - -First, you will add all the code. The changes will then be explained so that you can understand what the code does. - -1. Open `src/ui/index.tsx`. -1. Replace the contents of the file with the following code: - -```typescript -import { studioPro } from "@mendix/extensions-api"; -import { StrictMode, useCallback } from "react"; -import { createRoot } from "react-dom/client"; - -const saveFile = async () => { - await studioPro.app.files.putFile( - "HelloWorld.txt", - "Hello world from a file!" - ); - studioPro.ui.messageBoxes.show("info", "Saving HelloWorld.txt"); -}; - -const loadFile = async () => { - const message = await studioPro.app.files.getFile("HelloWorld.txt"); - studioPro.ui.messageBoxes.show( - "info", - `Loaded HelloWorld.txt it contained: ${message}` - ); -}; - -const deleteFile = async () => { - await studioPro.app.files.deleteFile("HelloWorld.txt"); - studioPro.ui.messageBoxes.show("info", "Deleted HelloWorld.txt"); -}; - -createRoot(document.getElementById("root")!).render( - -

Mendix Studio Pro Extension

-

Hello from an extension!

-

- - - -

-
-); -``` - -## What Does the Code Do? - -The following sections explain the various parts of the code. - -### saveFile - -The `saveFile` callback calls the `putFile` API setting the filename to `HelloWorld.txt` and the content to `Hello world from a file!`. - -```typescript -const saveFile = async () => { - await studioPro.app.files.putFile( - "HelloWorld.txt", - "Hello world from a file!" - ); - studioPro.ui.messageBoxes.show("info", "Saving HelloWorld.txt"); -}; -``` - -### loadFile - -The loadFile callback calls the getFile API requesting to load `HelloWorld.txt`. It then shows message box displaying the content of the file. - -```typescript -const loadFile = async () => { - const message = await studioPro.app.files.getFile("HelloWorld.txt"); - studioPro.ui.messageBoxes.show( - "info", - `Loaded HelloWorld.txt it contained: ${message}` - ); -}; -``` - -### deleteFile - -The deleteFile callback calls the deleteFile API requesting to delete `HelloWorld.txt` - -```typescript -const deleteFile = async () => { - await studioPro.app.files.deleteFile("HelloWorld.txt"); - studioPro.ui.messageBoxes.show("info", "Deleted HelloWorld.txt"); -}; -``` - -### Adding the Buttons - -The final part of the code adds three new buttons which, when clicked, call the callbacks described above. - -```typescript -createRoot(document.getElementById("root")!).render( - -

Mendix Studio Pro Extension

-

Hello from an extension!

-

- - - -

-
-); -``` - -## Some Restrictions - -The App files API allows you to modify files within your application's folder. It will not: - -* serve restricted files such as the `.mpr` file or the contents of some folders such as the `.git` folder. -* allow access to files outside of the app folder. - -## Conclusion - -You can now create and extension which save, load, and delete files within the app folder. - -## Extensibility Feedback - -If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) - -Any feedback is much appreciated. +--- +title: "Interact With Local App Files Using Web API" +linktitle: "Local Files" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-10/local-app-files-api/ +weight: 20 +--- + +## Introduction + +This how-to shows you how to interact with local application files from within an extension. It adds three new buttons to the tab and three new event handlers for saving, loading, and deleting a file called `HelloWorld.txt`. + +## Prerequisites + +This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-10/getting-started/). Please complete that how-to before starting this one. + +## Adding Some Interactivity + +First, you will add all the code. The changes will then be explained so that you can understand what the code does. + +1. Open `src/ui/index.tsx`. +1. Replace the contents of the file with the following code: + +```typescript +import { studioPro } from "@mendix/extensions-api"; +import { StrictMode, useCallback } from "react"; +import { createRoot } from "react-dom/client"; + +const saveFile = async () => { + await studioPro.app.files.putFile( + "HelloWorld.txt", + "Hello world from a file!" + ); + studioPro.ui.messageBoxes.show("info", "Saving HelloWorld.txt"); +}; + +const loadFile = async () => { + const message = await studioPro.app.files.getFile("HelloWorld.txt"); + studioPro.ui.messageBoxes.show( + "info", + `Loaded HelloWorld.txt it contained: ${message}` + ); +}; + +const deleteFile = async () => { + await studioPro.app.files.deleteFile("HelloWorld.txt"); + studioPro.ui.messageBoxes.show("info", "Deleted HelloWorld.txt"); +}; + +createRoot(document.getElementById("root")!).render( + +

Mendix Studio Pro Extension

+

Hello from an extension!

+

+ + + +

+
+); +``` + +## What Does the Code Do? + +The following sections explain the various parts of the code. + +### saveFile + +The `saveFile` callback calls the `putFile` API setting the filename to `HelloWorld.txt` and the content to `Hello world from a file!`. + +```typescript +const saveFile = async () => { + await studioPro.app.files.putFile( + "HelloWorld.txt", + "Hello world from a file!" + ); + studioPro.ui.messageBoxes.show("info", "Saving HelloWorld.txt"); +}; +``` + +### loadFile + +The loadFile callback calls the getFile API requesting to load `HelloWorld.txt`. It then shows message box displaying the content of the file. + +```typescript +const loadFile = async () => { + const message = await studioPro.app.files.getFile("HelloWorld.txt"); + studioPro.ui.messageBoxes.show( + "info", + `Loaded HelloWorld.txt it contained: ${message}` + ); +}; +``` + +### deleteFile + +The deleteFile callback calls the deleteFile API requesting to delete `HelloWorld.txt` + +```typescript +const deleteFile = async () => { + await studioPro.app.files.deleteFile("HelloWorld.txt"); + studioPro.ui.messageBoxes.show("info", "Deleted HelloWorld.txt"); +}; +``` + +### Adding the Buttons + +The final part of the code adds three new buttons which, when clicked, call the callbacks described above. + +```typescript +createRoot(document.getElementById("root")!).render( + +

Mendix Studio Pro Extension

+

Hello from an extension!

+

+ + + +

+
+); +``` + +## Some Restrictions + +The App files API allows you to modify files within your application's folder. It will not: + +* serve restricted files such as the `.mpr` file or the contents of some folders such as the `.git` folder. +* allow access to files outside of the app folder. + +## Conclusion + +You can now create and extension which save, load, and delete files within the app folder. + +## Extensibility Feedback + +If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) + +Any feedback is much appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/menu-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/menu-api.md similarity index 93% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/menu-api.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/menu-api.md index fd96dfd9b37..84aa7aaacee 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/menu-api.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/menu-api.md @@ -1,209 +1,209 @@ ---- -title: "Create a Menu Using Web API" -linktitle: "Create Menu" -url: /apidocs-mxsdk/apidocs/web-extensibility-api/menu-api/ -weight: 30 ---- - -## Introduction - -This how-to shows you how to create both a simple menu item and a menu item with subsidiary items beneath it using the web extension API. - -## Prerequisites - -This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api/getting-started/). Please complete that how-to before starting this one. - -## Creating a Simple Menu - -In this section, you will add a simple menu to your extension. - -The code below will: - -* create a menu item under the menu that was added in [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api/getting-started/) -* show a dialog when the menu is clicked - -Replace your `src/main/index.ts` file with the following: - -```typescript -import { IComponent, Menu, studioPro } from "@mendix/extensions-api"; - -const menuApi = studioPro.ui.extensionsMenu; - -const messageBoxApi = studioPro.ui.messageBoxes; -const menuId = "my-menu-unique-id"; -const caption = "My First Menu"; - -// Open a message box when the menu item is clicked -studioPro.ui.extensionsMenu.addEventListener("menuItemActivated", (args) => { - if (args.menuId === "my-menu-unique-id") { - messageBoxApi.show("info", `My menu '${args.menuId}' was clicked`); - } -}); -class Main implements IComponent { - async loaded() { - const menu: Menu = { - caption: caption, - menuId: menuId, - subMenus: [], - hasSeparatorBefore: false, - hasSeparatorAfter: true, - enabled: true, - }; - - await menuApi.add(menu); - } -} - -export const component: IComponent = new Main(); -``` - -The code imports the following: - -* `menuApi` from `studioPro.ui.extensionsMenu` to allow you to use the menu API -* `messageBoxApi` from `studioPro.ui.messageBoxes` to show a dialog. - -It starts listening to the `menuItemActivated` endpoint which will notify the extension when **My First Menu** is clicked. - -The `menuItemActivated` listener event handles the actual function of the menu. The arguments `args` contain the data returned by Studio Pro to the extension, notifying it which menu item was activated (clicked). This is passed as the `menuId` that was used when creating the menu item and the `menuId` in the `if` statement is used to identify this. In this example it is `"my-menu-unique-id"` and the handler calls the `messageBoxApi` to show an information dialog. - -Your extensions should now appear like this: - -{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/menus/my_first_menu.png" >}} - -## Menu Properties - -The menu has the following properties: - -* `caption` – the text of the menu item -* `menuId` – a unique id for the menu item -* `subMenus` – a list of menus subsidiary to this menu item -* `hasSeparatorBefore` (default: `false`) – shows a visual separator before this menu item -* `hasSeparatorAfter` (default: `false`) – shows a visual separator after this menu item -* `enabled` (default: `true`) – indicates that this menu item notifies the listener when clicked - -{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/menus/grouped_menus.png" >}} - - -## Creating a Menu with Submenus - -You can also have a number of submenus that branch out your menu. - -To do so, add additional menu items to your code and add these to the `subMenus` array for the relevant menu item. These child menus can in turn have their own submenus, and so on. Only parent menus (menus that are not sub menus to any others) should be added through the `await menuApi.add()` call, as shown in the code sample below. - -{{% alert color="info" %}} -Parent menus (with `subMenus`) do not create `menuItemActivated` events. These only get sent when a leaf menu (a menu that does not have any submenus) is clicked. -{{% /alert %}} - -The following `src/main/index.ts` generates one menu item with sub menus and one menu item without sub menus. - -```typescript -import { IComponent, Menu, studioPro } from "@mendix/extensions-api"; - -const menuApi = studioPro.ui.extensionsMenu; -const messageBoxApi = studioPro.ui.messageBoxes; - -// Open a message box when the menu item is clicked -studioPro.ui.extensionsMenu.addEventListener("menuItemActivated", (args) => { - messageBoxApi.show("info", `Child menu '${args.menuId}' was clicked`); -}); -class Main implements IComponent { - async loaded() { - const grandChild: Menu = { - caption: "Grandchild Menu", - menuId: "grandChild", - }; - - const childMenu1: Menu = { - caption: "Child Menu 1", - menuId: "child_1", - subMenus: [grandChild], - }; - - const childMenu2: Menu = { - caption: "Child Menu 2", - menuId: "child_2", - }; - - const menu1: Menu = { - caption: "Menu 1", - menuId: "menu1", - subMenus: [childMenu1, childMenu2], - }; - - const menu2: Menu = { - caption: "Menu 2", - menuId: "menu2", - subMenus: [], - }; - - await menuApi.add(menu1); - await menuApi.add(menu2); - } -} - -export const component: IComponent = new Main(); -``` - -The menu hierarchy will then be displayed like this: - -{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/menus/child_menus.png" >}} - -## Updating a menu - -Sometimes you might want to disable a menu or update its caption depending on a condition. You can do so by calling the menu API's `updateMenu` method. - -An example is shown in the code below. If you click on the menu item, it will be disabled and its caption will be updated. - -{{% alert color="info" %}} -Only `caption` and `enabled` can be updated. -{{% /alert %}} - -You can test it by the following code as the contents of `src/main/index.ts`. - -```typescript -import { IComponent, Menu, studioPro } from "@mendix/extensions-api"; - -const menuApi = studioPro.ui.extensionsMenu; - -const menuId = "my-menu-unique-id"; -const caption = "My First Menu"; - -menuApi.addEventListener("menuItemActivated", (args) => { - if (args.menuId !== menuId) return; - menuApi.update(menuId, { - caption: `${caption} (Disabled)`, - enabled: false, - }); -}); -class Main implements IComponent { - async loaded() { - const menu: Menu = { - caption: caption, - menuId: menuId, - subMenus: [], - hasSeparatorBefore: false, - hasSeparatorAfter: true, - enabled: true, - }; - - await menuApi.add(menu); - } -} - -export const component: IComponent = new Main(); -``` - -The disabled state is shown in the image below. - -{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/menus/disabled_menu.png" >}} - -## Conclusion - -You have seen how to create simple menu items and menu items with sub menus. -You can also dynamically change the enabled status and caption of a menu item. - -## Extensibility Feedback - -If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) - -Any feedback is much appreciated. +--- +title: "Create a Menu Using Web API" +linktitle: "Create Menu" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-10/menu-api/ +weight: 30 +--- + +## Introduction + +This how-to shows you how to create both a simple menu item and a menu item with subsidiary items beneath it using the web extension API. + +## Prerequisites + +This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-10/getting-started/). Please complete that how-to before starting this one. + +## Creating a Simple Menu + +In this section, you will add a simple menu to your extension. + +The code below will: + +* create a menu item under the menu that was added in [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-10/getting-started/) +* show a dialog when the menu is clicked + +Replace your `src/main/index.ts` file with the following: + +```typescript +import { IComponent, Menu, studioPro } from "@mendix/extensions-api"; + +const menuApi = studioPro.ui.extensionsMenu; + +const messageBoxApi = studioPro.ui.messageBoxes; +const menuId = "my-menu-unique-id"; +const caption = "My First Menu"; + +// Open a message box when the menu item is clicked +studioPro.ui.extensionsMenu.addEventListener("menuItemActivated", (args) => { + if (args.menuId === "my-menu-unique-id") { + messageBoxApi.show("info", `My menu '${args.menuId}' was clicked`); + } +}); +class Main implements IComponent { + async loaded() { + const menu: Menu = { + caption: caption, + menuId: menuId, + subMenus: [], + hasSeparatorBefore: false, + hasSeparatorAfter: true, + enabled: true, + }; + + await menuApi.add(menu); + } +} + +export const component: IComponent = new Main(); +``` + +The code imports the following: + +* `menuApi` from `studioPro.ui.extensionsMenu` to allow you to use the menu API +* `messageBoxApi` from `studioPro.ui.messageBoxes` to show a dialog. + +It starts listening to the `menuItemActivated` endpoint which will notify the extension when **My First Menu** is clicked. + +The `menuItemActivated` listener event handles the actual function of the menu. The arguments `args` contain the data returned by Studio Pro to the extension, notifying it which menu item was activated (clicked). This is passed as the `menuId` that was used when creating the menu item and the `menuId` in the `if` statement is used to identify this. In this example it is `"my-menu-unique-id"` and the handler calls the `messageBoxApi` to show an information dialog. + +Your extensions should now appear like this: + +{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/menus/my_first_menu.png" >}} + +## Menu Properties + +The menu has the following properties: + +* `caption` – the text of the menu item +* `menuId` – a unique id for the menu item +* `subMenus` – a list of menus subsidiary to this menu item +* `hasSeparatorBefore` (default: `false`) – shows a visual separator before this menu item +* `hasSeparatorAfter` (default: `false`) – shows a visual separator after this menu item +* `enabled` (default: `true`) – indicates that this menu item notifies the listener when clicked + +{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/menus/grouped_menus.png" >}} + + +## Creating a Menu with Submenus + +You can also have a number of submenus that branch out your menu. + +To do so, add additional menu items to your code and add these to the `subMenus` array for the relevant menu item. These child menus can in turn have their own submenus, and so on. Only parent menus (menus that are not sub menus to any others) should be added through the `await menuApi.add()` call, as shown in the code sample below. + +{{% alert color="info" %}} +Parent menus (with `subMenus`) do not create `menuItemActivated` events. These only get sent when a leaf menu (a menu that does not have any submenus) is clicked. +{{% /alert %}} + +The following `src/main/index.ts` generates one menu item with sub menus and one menu item without sub menus. + +```typescript +import { IComponent, Menu, studioPro } from "@mendix/extensions-api"; + +const menuApi = studioPro.ui.extensionsMenu; +const messageBoxApi = studioPro.ui.messageBoxes; + +// Open a message box when the menu item is clicked +studioPro.ui.extensionsMenu.addEventListener("menuItemActivated", (args) => { + messageBoxApi.show("info", `Child menu '${args.menuId}' was clicked`); +}); +class Main implements IComponent { + async loaded() { + const grandChild: Menu = { + caption: "Grandchild Menu", + menuId: "grandChild", + }; + + const childMenu1: Menu = { + caption: "Child Menu 1", + menuId: "child_1", + subMenus: [grandChild], + }; + + const childMenu2: Menu = { + caption: "Child Menu 2", + menuId: "child_2", + }; + + const menu1: Menu = { + caption: "Menu 1", + menuId: "menu1", + subMenus: [childMenu1, childMenu2], + }; + + const menu2: Menu = { + caption: "Menu 2", + menuId: "menu2", + subMenus: [], + }; + + await menuApi.add(menu1); + await menuApi.add(menu2); + } +} + +export const component: IComponent = new Main(); +``` + +The menu hierarchy will then be displayed like this: + +{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/menus/child_menus.png" >}} + +## Updating a menu + +Sometimes you might want to disable a menu or update its caption depending on a condition. You can do so by calling the menu API's `updateMenu` method. + +An example is shown in the code below. If you click on the menu item, it will be disabled and its caption will be updated. + +{{% alert color="info" %}} +Only `caption` and `enabled` can be updated. +{{% /alert %}} + +You can test it by the following code as the contents of `src/main/index.ts`. + +```typescript +import { IComponent, Menu, studioPro } from "@mendix/extensions-api"; + +const menuApi = studioPro.ui.extensionsMenu; + +const menuId = "my-menu-unique-id"; +const caption = "My First Menu"; + +menuApi.addEventListener("menuItemActivated", (args) => { + if (args.menuId !== menuId) return; + menuApi.update(menuId, { + caption: `${caption} (Disabled)`, + enabled: false, + }); +}); +class Main implements IComponent { + async loaded() { + const menu: Menu = { + caption: caption, + menuId: menuId, + subMenus: [], + hasSeparatorBefore: false, + hasSeparatorAfter: true, + enabled: true, + }; + + await menuApi.add(menu); + } +} + +export const component: IComponent = new Main(); +``` + +The disabled state is shown in the image below. + +{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/menus/disabled_menu.png" >}} + +## Conclusion + +You have seen how to create simple menu items and menu items with sub menus. +You can also dynamically change the enabled status and caption of a menu item. + +## Extensibility Feedback + +If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) + +Any feedback is much appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/messagebox-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/messagebox-api.md similarity index 90% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/messagebox-api.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/messagebox-api.md index a323a83e13a..a5a1aebb745 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/messagebox-api.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/messagebox-api.md @@ -1,100 +1,100 @@ ---- -title: "Show a Message Box Using Web API" -linktitle: "Message Box" -url: /apidocs-mxsdk/apidocs/web-extensibility-api/messagebox-api/ -weight: 50 ---- - -## Introduction - -This how-to shows you how to show a message box to the Studio Pro user. - -## Prerequisites - -This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api/getting-started/). Please complete that how-to before starting this one. - -## Showing a Message Box - -Firstly, you need to create a menu which will display a dialog with text. This is done in the `loaded` event in `Main`. - -You can learn how to do that in [Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api/menu-api/). - -In this example you create three menu items: - -* Show Info -* Show Error -* Show Warning - -You then need to add event listeners to receive and act upon the notifications generated by the menu items. Here the listener events show a different message box, depending on which menu item is selected. The message has the following format: - -`messageBoxApi.show(, , )` - -where - -* `` is the type of message, indicated in the pane title and indicated by an icon. Values are "information" {{% icon name="info-circle" color="blue" %}}, "warning" {{% icon name="alert-triangle" color="yellow" %}}, and "error" {{% icon name="remove-circle" color="red" %}}. -* `` is the message to display. -* `` is an optional extended message which is displayed in an expandable area which is initially collapsed. - -The full typescript file to implement these three menu items and message boxes is as follows. - -```typescript -import { IComponent, Menu, studioPro } from "@mendix/extensions-api"; -const messageBoxApi = studioPro.ui.messageBoxes; -const menuApi = studioPro.ui.extensionsMenu; - -const show_info_menu_id = "show-info-id"; -const show_error_menu_id = "show-error-id"; -const show_warning_menu_id = "show-warning-id"; - -menuApi.addEventListener("menuItemActivated", (args) => { - if (args.menuId === show_info_menu_id) - messageBoxApi.show("info", "This is information.", "Extra info"); - if (args.menuId === show_error_menu_id) - messageBoxApi.show("error", "This is an error.", "Extra error details"); - if (args.menuId === show_warning_menu_id) - messageBoxApi.show( - "warning", - "This is a warning.", - "Extra warning details" - ); -}); - -class Main implements IComponent { - async loaded() { - const infoMenu: Menu = { - caption: "Show Info", - menuId: show_info_menu_id, - }; - - const errorMenu: Menu = { - caption: "Show Error", - menuId: show_error_menu_id, - }; - - const warningMenu: Menu = { - caption: "Show Warning", - menuId: show_warning_menu_id, - }; - - await menuApi.add(infoMenu); - await menuApi.add(errorMenu); - await menuApi.add(warningMenu); - } -} - -export const component: IComponent = new Main(); -``` - -For example, the **Show Info** menu item will display the following message box. - -{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/messageBoxes/info.png" >}} - -## Conclusion - -You have seen how to implement message boxes triggered by menu items. - -## Extensibility Feedback - -If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) - -Any feedback is much appreciated. +--- +title: "Show a Message Box Using Web API" +linktitle: "Message Box" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-10/messagebox-api/ +weight: 50 +--- + +## Introduction + +This how-to shows you how to show a message box to the Studio Pro user. + +## Prerequisites + +This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-10/getting-started/). Please complete that how-to before starting this one. + +## Showing a Message Box + +Firstly, you need to create a menu which will display a dialog with text. This is done in the `loaded` event in `Main`. + +You can learn how to do that in [Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-10/menu-api/). + +In this example you create three menu items: + +* Show Info +* Show Error +* Show Warning + +You then need to add event listeners to receive and act upon the notifications generated by the menu items. Here the listener events show a different message box, depending on which menu item is selected. The message has the following format: + +`messageBoxApi.show(, , )` + +where + +* `` is the type of message, indicated in the pane title and indicated by an icon. Values are "information" {{% icon name="info-circle" color="blue" %}}, "warning" {{% icon name="alert-triangle" color="yellow" %}}, and "error" {{% icon name="remove-circle" color="red" %}}. +* `` is the message to display. +* `` is an optional extended message which is displayed in an expandable area which is initially collapsed. + +The full typescript file to implement these three menu items and message boxes is as follows. + +```typescript +import { IComponent, Menu, studioPro } from "@mendix/extensions-api"; +const messageBoxApi = studioPro.ui.messageBoxes; +const menuApi = studioPro.ui.extensionsMenu; + +const show_info_menu_id = "show-info-id"; +const show_error_menu_id = "show-error-id"; +const show_warning_menu_id = "show-warning-id"; + +menuApi.addEventListener("menuItemActivated", (args) => { + if (args.menuId === show_info_menu_id) + messageBoxApi.show("info", "This is information.", "Extra info"); + if (args.menuId === show_error_menu_id) + messageBoxApi.show("error", "This is an error.", "Extra error details"); + if (args.menuId === show_warning_menu_id) + messageBoxApi.show( + "warning", + "This is a warning.", + "Extra warning details" + ); +}); + +class Main implements IComponent { + async loaded() { + const infoMenu: Menu = { + caption: "Show Info", + menuId: show_info_menu_id, + }; + + const errorMenu: Menu = { + caption: "Show Error", + menuId: show_error_menu_id, + }; + + const warningMenu: Menu = { + caption: "Show Warning", + menuId: show_warning_menu_id, + }; + + await menuApi.add(infoMenu); + await menuApi.add(errorMenu); + await menuApi.add(warningMenu); + } +} + +export const component: IComponent = new Main(); +``` + +For example, the **Show Info** menu item will display the following message box. + +{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/messageBoxes/info.png" >}} + +## Conclusion + +You have seen how to implement message boxes triggered by menu items. + +## Extensibility Feedback + +If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) + +Any feedback is much appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/model-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/model-api.md new file mode 100644 index 00000000000..bf635357d69 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/model-api.md @@ -0,0 +1,108 @@ +--- +title: "Access a Mendix Model Using Web API" +linktitle: "Mendix Model" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-10/model-api/ +weight: 40 +--- + +## Introduction + +The Model Access API allow access to the Mendix model. This how-to provides guidance on using the Model Access API. It is split into the following sections: + +* [Using the Model Access API](#using-api) +* [Reading the Units Info and Loading Units](#units-info-load) +* [Reading the Unit Content](#read) +* [Modifying the Unit Content](#modify) + +## Using the Model Access API {#using-api} + +The model is split in several components exposed via `studioPro.app.model` object. Currently supported components are: + +* buildingBlocks +* domainModels +* enumerations +* pages +* snippets + +You can include these components using syntax as shown below, which includes pages and domain models. + +```ts +const { pages, domainModels } = studioPro.app.model; +``` + +## Reading the Units Info and Loading Units {#units-info-load} + +A unit is a Mendix document (for example, a page or a domain model) containing elements. Each element is within a container element and may contain other elements. An element is part of a Mendix model and all elements together form the logic of the model. For more information see [Mendix Metamodel](/apidocs-mxsdk/mxsdk/mendix-metamodel/). + +Each component, for example pages (`studioPro.app.model.pages`) exposes the units it is responsible for. You can only access all the content of a unit once you have loaded the unit info for that unit. + +The unit info, described by the `UnitInfo` interface, contains the the following fields: + +| Name | Description | Example value | +| --- | --- | --- | +| `$ID` | The unique id of the unit | `077d1338-a548-49a9-baee-c291e93d19af` | +| `$Type` | The type of the unit | `Pages$Page` | +| `moduleName` | (Optional) The name of the module containing the unit | `MyFirstModule` | +| `name` | (Optional) The name of the unit | `ExamplePage` | + +For example, you can retrieve all the units managed by the `domainModels` component using the following code: + +```ts +const unitsInfo: Primitives.UnitInfo[] = await domainModels.getUnitsInfo() +``` + +A unit can be loaded by supplying a function, `fn` to `component.loadAll(fn)`. The function `fn` should return `true` to load a specified unit. + +{{% alert color="warning" %}} +Loading units is a resource intensive process. Only load units when you need them. +{{% /alert %}} + +For example, the following snippet loads the `domainModel` for the module named `MyFirstModule`: + +```ts +const [domainModel] = await domainModels.loadAll((info: Primitives.UnitInfo) => info.moduleName === 'MyFirstModule'); +``` + +And this example snippet loads the page named `Home_Web` in the module named `MyFirstModule`: + +```ts +const [page] = await pages.loadAll((info: Primitives.UnitInfo) => info.moduleName === 'MyFirstModule' && info.name === 'Home_Web') +``` + +## Reading the Unit Content {#read} + +Elements within units can be accessed using the `get` helper methods. + +For example, the following snippet will get the entity named `MyEntity` from the previously loaded `DomainModels` unit: + +```ts +const entity: DomainModels.Entity = domainModel.getEntity("MyEntity"); +``` + +## Modifying the Unit Content {#modify} + +You can modify a Mendix model by leveraging the `add` helper methods. + +{{% alert color="warning" %}} +Always invoke the `component.save(unit)` method after making changes to your unit. This method must be invoked for each modified unit, so changes to multiple units need to be saved separately. +{{% /alert %}} + +The following snippet creates a new entity inside the previously loaded `DomainModels` unit: + +```ts +const newEntity: DomainModels.Entity = await domainModel.addEntity({ name: "NewEntity", attributes: [{ name: "MyAttribute", type: "AutoNumber" }]}); + +newEntity.documentation = "New documentation"; + +await domainModels.save(domainModel); +``` + +## Conclusion + +You now know how to interact with units and elements in the Mendix model. + +## Extensibility Feedback + +If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) + +Any feedback is much appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/tab-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/tab-api.md similarity index 92% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/tab-api.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/tab-api.md index aef1e50704c..3be1c9038e3 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/tab-api.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/extensibility-api/web/web-extensions-howtos/tab-api.md @@ -1,233 +1,233 @@ ---- -title: "Open a Tab Using Web API" -linktitle: "Open a Tab" -url: /apidocs-mxsdk/apidocs/web-extensibility-api/tab-api/ -weight: 60 ---- - -## Introduction - -This how-to shows you how to open a tab in Studio Pro from an extension. This tab will contain your web content. - -## Prerequisites - -This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api/getting-started/). Please complete that how-to before starting this one. You should also be familiar with creating menus as described in [Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api/menu-api/). - -## Opening a Tab - -Firstly, create a menu item to open the tab. This is done inside the `loaded` event in `Main`. For more information see [Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api/menu-api/). - -In a listener event called `menuItemActivated` the `studioPro.ui.tabs.open(, )` call opens a new tab where: - -* `` is an object containing the `title` of the tab, which will be shown in the title bar of your tab in Studio Pro. -* `` is an object containing two required properties: - - * `componentName` which is the name of the extension prefixed with "extension/". For example "extension/myextension" in the following example. - * `uiEntryPoint` which is the name mapped from the `manifest.json` file. See below for examples with multiple tabs. - -{{% alert color="info" %}} -Whenever the tabs API `open` method is called, the `TabHandle` returned must be tracked by the extension so that it can be closed later by calling the `close` method. -{{% /alert %}} - -An example of the class `Main` to open a tab called **My Extension Tab** looks similar to the following: - -```typescript -import { IComponent, studioPro, TabHandle } from "@mendix/extensions-api"; - -class Main implements IComponent { - tabs: { [menuId: string]: Promise } = {}; - async loaded() { - // Add menu items to the Extensions menu to open and close our tab - await studioPro.ui.extensionsMenu.add({ - menuId: "myextension.MainMenu", - caption: "MyExtension Menu", - subMenus: [ - { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, - { - menuId: "myextension.CloseTabMenuItem", - caption: "Close tab", - }, - ], - }); - - studioPro.ui.extensionsMenu.addEventListener( - "menuItemActivated", - async (args) => { - // Open a tab when the menu item is clicked - if (args.menuId === "myextension.ShowTabMenuItem") { - const handle = studioPro.ui.tabs.open( - { - title: "My Extension Tab", - }, - { - componentName: "extension/myextension", - uiEntrypoint: "tab", - } - ); - - // Track the open tab - this.tabs["myextension.MainMenu"] = handle; - } - - // Close the tab opened previously - if (args.menuId === "myextension.CloseTabMenuItem") { - studioPro.ui.tabs.close(await this.tabs["myextension.MainMenu"]); - } - } - ); - } -} - -export const component: IComponent = new Main(); -``` - -{{% alert color="info" %}} - - In this example, there is a dictionary that uses the parent menu id as the key to track the open `TabHandle`. -{{% /alert %}} - -## Filling the Tabs With Content - -In the previous example, the `uiEntryPoint` property of the `` object had the value "tab". This value must match the one from the manifest. - -If you want to have multiple tabs in your extension, you need to structure the folders and set up the manifest file correctly. - -To do this, follow these steps: - -1. Add a new method `createTabSpec` in your `Main` class. - - ```typescript - createTabSpec(tab: string, title: string): { info: TabInfo, ui: UISpec} { - const info: TabInfo = { title }; - const ui: UISpec = { - componentName: "extension/myextension", - uiEntrypoint: tab, - }; - - return {info, ui}; - } - ``` - -1. Add three folders inside the `ui` folder, one for each tab you want to display contents for. -1. Create an `index.tsx` file in each folder. -1. Put the following code in each `index.tsx` file (this example is for **tab3**): - - ```typescript - import { StrictMode } from "react"; - import { createRoot } from "react-dom/client"; - - createRoot(document.getElementById("root")!).render( - -

tab3

-
- ); - ``` - - In this example, we'll add 3 tabs: **tab1**, **tab2**, and **tab3**. - - {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/tabs/ui_folder_structure.png" >}} - -1. Create listener events in the `Main` class to open each of the three tabs. The `Main` class will then look like this: - - ```typescript - import { IComponent, studioPro, TabInfo, UISpec } from "@mendix/extensions-api"; - - class Main implements IComponent { - async loaded() { - // Add a menu item to the Extensions menu - await studioPro.ui.extensionsMenu.add({ - menuId: "myextension.MainMenu", - caption: "Show Tabs", - subMenus: [ - { menuId: "myextension.ShowTab1", caption: "Show tab 1" }, - { menuId: "myextension.ShowTab2", caption: "Show tab 2" }, - { menuId: "myextension.ShowTab3", caption: "Show tab 3" }, - ], - }); - - // Open a tab when the menu item is clicked - studioPro.ui.extensionsMenu.addEventListener( - "menuItemActivated", - async (args) => { - if (args.menuId === "myextension.ShowTab1") { - const tab1Spec = this.createTabSpec("tab1", "Tab 1 Title"); - studioPro.ui.tabs.open(tab1Spec.info, tab1Spec.ui); - } - if (args.menuId === "myextension.ShowTab2") { - const tab2Spec = this.createTabSpec("tab2", "Tab 2 Title"); - studioPro.ui.tabs.open(tab2Spec.info, tab2Spec.ui); - } - if (args.menuId === "myextension.ShowTab3") { - const tab3Spec = this.createTabSpec("tab3", "Tab 3 Title"); - studioPro.ui.tabs.open(tab3Spec.info, tab3Spec.ui); - } - } - ); - } - - createTabSpec(tab: string, title: string): { info: TabInfo; ui: UISpec } { - const info: TabInfo = { title }; - const ui: UISpec = { - componentName: "extension/myextension", - uiEntrypoint: tab, - }; - - return { info, ui }; - } - } - - export const component: IComponent = new Main(); - ``` - -1. Ensure the tabs are added to the `manifest.json` file. Here is an example of three tabs under the `ui` property. - - ```json - { - "mendixComponent": { - "entryPoints": { - "main": "main.js", - "ui": { - "tab1": "tab1.js", - "tab2": "tab2.js", - "tab3": "tab3.js" - } - } - } - } - ``` - -1. Update `vite.config` to match the manifest with an entry for each tab. For example: - - ```typescript - import { defineConfig, ResolvedConfig, UserConfig } from "vite"; - - export default defineConfig({ - build: { - lib: { - formats: ["es"], - entry: { - main: "src/main/index.ts", - tab1: "src/ui/tab1/index.tsx", - tab2: "src/ui/tab2/index.tsx", - tab3: "src/ui/tab3/index.tsx", - }, - }, - rollupOptions: { - external: ["@mendix/component-framework", "@mendix/model-access-sdk"], - }, - outDir: "./dist/myextension", - }, - } satisfies UserConfig); - ``` - -After building and installing the extension in our Studio Pro app, each tab will display the content specified in the related `index.tsx` file. - -## Conclusion - -You now know how to create tabs and populate them with content. - -## Extensibility Feedback - -If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) - -Any feedback is much appreciated. +--- +title: "Open a Tab Using Web API" +linktitle: "Open a Tab" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-10/tab-api/ +weight: 60 +--- + +## Introduction + +This how-to shows you how to open a tab in Studio Pro from an extension. This tab will contain your web content. + +## Prerequisites + +This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-10/getting-started/). Please complete that how-to before starting this one. You should also be familiar with creating menus as described in [Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-10/menu-api/). + +## Opening a Tab + +Firstly, create a menu item to open the tab. This is done inside the `loaded` event in `Main`. For more information see [Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-10/menu-api/). + +In a listener event called `menuItemActivated` the `studioPro.ui.tabs.open(, )` call opens a new tab where: + +* `` is an object containing the `title` of the tab, which will be shown in the title bar of your tab in Studio Pro. +* `` is an object containing two required properties: + + * `componentName` which is the name of the extension prefixed with "extension/". For example "extension/myextension" in the following example. + * `uiEntryPoint` which is the name mapped from the `manifest.json` file. See below for examples with multiple tabs. + +{{% alert color="info" %}} +Whenever the tabs API `open` method is called, the `TabHandle` returned must be tracked by the extension so that it can be closed later by calling the `close` method. +{{% /alert %}} + +An example of the class `Main` to open a tab called **My Extension Tab** looks similar to the following: + +```typescript +import { IComponent, studioPro, TabHandle } from "@mendix/extensions-api"; + +class Main implements IComponent { + tabs: { [menuId: string]: Promise } = {}; + async loaded() { + // Add menu items to the Extensions menu to open and close our tab + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "MyExtension Menu", + subMenus: [ + { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, + { + menuId: "myextension.CloseTabMenuItem", + caption: "Close tab", + }, + ], + }); + + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + async (args) => { + // Open a tab when the menu item is clicked + if (args.menuId === "myextension.ShowTabMenuItem") { + const handle = studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + + // Track the open tab + this.tabs["myextension.MainMenu"] = handle; + } + + // Close the tab opened previously + if (args.menuId === "myextension.CloseTabMenuItem") { + studioPro.ui.tabs.close(await this.tabs["myextension.MainMenu"]); + } + } + ); + } +} + +export const component: IComponent = new Main(); +``` + +{{% alert color="info" %}} + + In this example, there is a dictionary that uses the parent menu id as the key to track the open `TabHandle`. +{{% /alert %}} + +## Filling the Tabs With Content + +In the previous example, the `uiEntryPoint` property of the `` object had the value "tab". This value must match the one from the manifest. + +If you want to have multiple tabs in your extension, you need to structure the folders and set up the manifest file correctly. + +To do this, follow these steps: + +1. Add a new method `createTabSpec` in your `Main` class. + + ```typescript + createTabSpec(tab: string, title: string): { info: TabInfo, ui: UISpec} { + const info: TabInfo = { title }; + const ui: UISpec = { + componentName: "extension/myextension", + uiEntrypoint: tab, + }; + + return {info, ui}; + } + ``` + +1. Add three folders inside the `ui` folder, one for each tab you want to display contents for. +1. Create an `index.tsx` file in each folder. +1. Put the following code in each `index.tsx` file (this example is for **tab3**): + + ```typescript + import { StrictMode } from "react"; + import { createRoot } from "react-dom/client"; + + createRoot(document.getElementById("root")!).render( + +

tab3

+
+ ); + ``` + + In this example, we'll add 3 tabs: **tab1**, **tab2**, and **tab3**. + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/tabs/ui_folder_structure.png" >}} + +1. Create listener events in the `Main` class to open each of the three tabs. The `Main` class will then look like this: + + ```typescript + import { IComponent, studioPro, TabInfo, UISpec } from "@mendix/extensions-api"; + + class Main implements IComponent { + async loaded() { + // Add a menu item to the Extensions menu + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "Show Tabs", + subMenus: [ + { menuId: "myextension.ShowTab1", caption: "Show tab 1" }, + { menuId: "myextension.ShowTab2", caption: "Show tab 2" }, + { menuId: "myextension.ShowTab3", caption: "Show tab 3" }, + ], + }); + + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + async (args) => { + if (args.menuId === "myextension.ShowTab1") { + const tab1Spec = this.createTabSpec("tab1", "Tab 1 Title"); + studioPro.ui.tabs.open(tab1Spec.info, tab1Spec.ui); + } + if (args.menuId === "myextension.ShowTab2") { + const tab2Spec = this.createTabSpec("tab2", "Tab 2 Title"); + studioPro.ui.tabs.open(tab2Spec.info, tab2Spec.ui); + } + if (args.menuId === "myextension.ShowTab3") { + const tab3Spec = this.createTabSpec("tab3", "Tab 3 Title"); + studioPro.ui.tabs.open(tab3Spec.info, tab3Spec.ui); + } + } + ); + } + + createTabSpec(tab: string, title: string): { info: TabInfo; ui: UISpec } { + const info: TabInfo = { title }; + const ui: UISpec = { + componentName: "extension/myextension", + uiEntrypoint: tab, + }; + + return { info, ui }; + } + } + + export const component: IComponent = new Main(); + ``` + +1. Ensure the tabs are added to the `manifest.json` file. Here is an example of three tabs under the `ui` property. + + ```json + { + "mendixComponent": { + "entryPoints": { + "main": "main.js", + "ui": { + "tab1": "tab1.js", + "tab2": "tab2.js", + "tab3": "tab3.js" + } + } + } + } + ``` + +1. Update `vite.config` to match the manifest with an entry for each tab. For example: + + ```typescript + import { defineConfig, ResolvedConfig, UserConfig } from "vite"; + + export default defineConfig({ + build: { + lib: { + formats: ["es"], + entry: { + main: "src/main/index.ts", + tab1: "src/ui/tab1/index.tsx", + tab2: "src/ui/tab2/index.tsx", + tab3: "src/ui/tab3/index.tsx", + }, + }, + rollupOptions: { + external: ["@mendix/component-framework", "@mendix/model-access-sdk"], + }, + outDir: "./dist/myextension", + }, + } satisfies UserConfig); + ``` + +After building and installing the extension in our Studio Pro app, each tab will display the content specified in the related `index.tsx` file. + +## Conclusion + +You now know how to create tabs and populate them with content. + +## Extensibility Feedback + +If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) + +Any feedback is much appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/runtime-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/runtime-api.md similarity index 76% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/runtime-api.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/runtime-api.md index d7e3c96ed1b..560b97e925a 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/runtime-api.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/runtime-api.md @@ -1,6 +1,6 @@ --- title: "Mendix Runtime API" -url: /apidocs-mxsdk/apidocs/runtime-api/ +url: /apidocs-mxsdk/apidocs/runtime-api-10/ description: "The Mendix Runtime API provides all the functionality and information from both the application model and Mendix Runtime." weight: 75 --- @@ -13,8 +13,6 @@ Extend your application model using Java. All functionality and information from * [Mendix 11 Runtime API](https://apidocs.rnd.mendix.com/11/runtime/index.html) * [Mendix 10 Runtime API](https://apidocs.rnd.mendix.com/10/runtime/index.html) -* [Mendix 9 Runtime API](https://apidocs.rnd.mendix.com/9/runtime/index.html) -* [Mendix 8 Runtime API](https://apidocs.rnd.mendix.com/8/runtime/index.html) ## Read More diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/_index.md new file mode 100644 index 00000000000..9386b4fe73e --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/_index.md @@ -0,0 +1,11 @@ +--- +title: "APIs for Studio Pro 11" +url: /apidocs-mxsdk/apidocs/apis-for-studio-pro-11/ +no_list: false +description_list: true +description: "Provides the documentation of APIs for Studio Pro 11, including Extensibility API and Mendix Runtime API." +weight: 3 +linktitle: "Studio Pro 11" +--- + +Mendix offers the following APIs for Studio Pro 11: diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/_index.md new file mode 100644 index 00000000000..b49c0e0c549 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/_index.md @@ -0,0 +1,25 @@ +--- +title: "Extensibility API" +url: /apidocs-mxsdk/apidocs/extensibility-api-11/ +description: "The Extensibility API allows you to extend Studio Pro by adding custom functionality." +weight: 57 +no_list: false +description_list: true +cascade: + - beta: true +--- + +{{% alert color="warning" %}} This feature is in beta. For more information, see [Beta Releases](/releasenotes/beta-features/). {{% /alert %}} + +{{% alert color="info" %}} +For information on new releases of the Extensibility API see: + +* [Extensibility: C# API Release Notes](/releasenotes/studio-pro/csharp-extensibility-api/). +* [Extensibility: Web API Release Notes](/releasenotes/studio-pro/web-extensibility-api/). +{{% /alert %}} + +## Introduction + +Extensions are self-contained modules which users can add to Studio Pro. This means that with extensibility you can add new features and functionality to Studio Pro. The Extensibility API is an API that allows developers to interact with a curated list of internal systems of Studio Pro. This documentation provides guides and reference documentation for the Extensibility API. + +The API is provided in two flavors, depending which language you are developing in. C# and web based (via Typescript): diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/_index.md new file mode 100644 index 00000000000..2a25ecd1d66 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/_index.md @@ -0,0 +1,65 @@ +--- +title: "Extensibility API for C# Developers" +linktitle: "C# Extensibility API" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-11/ +description: "The C# extensibility API allows your custom Studio Pro extensions developed in C# to interact with some internal services of Studio Pro." +weight: 10 +no_list: false +description_list: true +aliases: + - /apidocs-mxsdk/apidocs/extensibility-api/ +--- + +{{% alert color="warning" %}} +This feature is in beta. For more information, see [Beta Releases](/releasenotes/beta-features/). +{{% /alert %}} + +{{% alert color="info" %}} +For information on new releases of the Extensibility API, see [Extensibility: C# API Release Notes](/releasenotes/studio-pro/csharp-extensibility-api/). +{{% /alert %}} + +## Introduction + +Extensions can be written in C#, described here, or using a web API which is documented separately in [Extensibility API for Web Developers](/apidocs-mxsdk/apidocs/web-extensibility-api-11/). + +If you need to add your own custom UI to Studio Pro, you can achieve this using web technology. Your web-based UI will be rendered in Studio Pro using a hosted web view, the API provides communication functionality between your web UI and the C# extension logic. + +## Prerequisites + +* You need at least a basic understanding of the Mendix platform. +* You need some understanding of the Mendix Model. +* You need to have some C# development experience. Extensions are developed using [C#](https://docs.microsoft.com/en-us/dotnet/), and compiled into a `.dll` assembly file. + +## Getting Started + +For detailed explanation on how to get started with extensions, check out [Get Started with the Extensibility API](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/get-started/). + +You can also check out our examples and [API reference documentation](https://github.com/mendix/ExtensionAPI-Samples). + +## How-tos + +Here is a list of how-tos for you to begin with: + +* [How to Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-menu-extension/) +* [How to Create a Dockable Pane Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-dockable-pane-extension/) +* [How to Create a Context Menu Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-context-menu/) +* [How to Create a Web View Hosted Inside a Modal Dialog Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-modal-web-view/) +* [How to Create Microflows for Calculations Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-microflows-for-calculations/) + +## Advanced APIs + +APIs for the Mendix platform's advanced users: + +* [Use the Untyped Model Access API Using C#](/apidocs-mxsdk/apidocs/untyped-model-access-api/) + +## Learn More + +You can dive into the following topics in depth: + +* [What are extension points](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/extension-points/) +* [What are the Extensibility API services](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/services/) +* [How to Interact with the Model API Using C#](/apidocs-mxsdk/apidocs/interact-with-model-api/) +* [How to host web content via a web view wrapper](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/web-views/) +* [How to Build a Todo Example Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/build-todo-example-extension/) + +## Documentation in This Category diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/_index.md new file mode 100644 index 00000000000..1e9031e6d8c --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/_index.md @@ -0,0 +1,12 @@ +--- +title: "C# Extensibility API How-tos" +linktitle: "How-tos" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-11/how-tos/ +weight: 3 +no_list: false +description_list: true +--- + +## Introduction + +The following how-tos teach you how to use the Extensibility API for C# Developers in different use cases. \ No newline at end of file diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/add-menu.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/add-menu.md new file mode 100644 index 00000000000..f3615b08a81 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/add-menu.md @@ -0,0 +1,66 @@ +--- +title: "Add Menus and Submenus to Studio Pro Using C#" +linktitle: "Structured Menus" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-11/add-menu/ +weight: 15 +--- + +## Introduction + +This how-to describes how you can add a menu that contains submenus, some of which also contain submenus of their own. Before you start this how-to, it is recommended to [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-menu-extension/) first. + +You can download the example in this how-to in [this GitHub repository](https://github.com/mendix/ExtensionAPI-Samples). + +## Creating Menu Extension Class + +1. Open the project that you previously created when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-menu-extension/). +2. Add a new class to the project and name it `MyMenuExtension.cs`. +3. Replace the code in the file with the following code: + + ```csharp + using Mendix.StudioPro.ExtensionsAPI.UI.Menu; + using Mendix.StudioPro.ExtensionsAPI.UI.Services; + using System.ComponentModel.Composition; + + namespace MyCompany.MyProject.MendixExtension; + + [Export(typeof(MenuExtension))] + public class MyMenuExtension : MenuExtension + { + readonly IMessageBoxService messageBoxService; + + [ImportingConstructor] + public MyMenuExtension(IMessageBoxService messageBoxService) + { + this.messageBoxService = messageBoxService; + } + + public override IEnumerable GetMenus() + { + var ristretto = new MenuViewModel("Ristretto", () => messageBoxService.ShowInformation("Ristretto")); + var regularExpresso = new MenuViewModel("Regular Espresso", () => messageBoxService.ShowInformation("Regular Espresso")); + var espresso = new MenuViewModel("Espresso", [regularExpresso, ristretto]); + var blackCoffee = new MenuViewModel("Black Coffee", () => messageBoxService.ShowInformation("Black Coffee")); + var decaf = new MenuViewModel("Decaf", () => messageBoxService.ShowInformation("Decaf")) { Separator = MenuSeparator.After }; + var coffee = new MenuViewModel("Coffee", [blackCoffee, decaf, espresso]); + + var tea = new MenuViewModel("Tea", () => messageBoxService.ShowInformation("Tea")); + + var hot = new MenuViewModel("Hot", [coffee, tea]); + + var soda = new MenuViewModel("Soda", () => messageBoxService.ShowInformation("Soda")); + var cold = new MenuViewModel("Cold", [soda]); + + var beverages = new MenuViewModel("Beverages", [hot, cold]); + yield return beverages; + } + } + ``` + +The code above creates a single menu, `Beverages`, which contains the submenus `Hot` and `Cold`, both of which contain some submenus. Note that when you are creating this menu structure, you only return the main parent menu (in this example, the `Beverages` menu) from the `GetMenus` method. You should only return the topmost parents in your list, so only the ones that do not have a parent should be returned. In the example above, there is only one. + +If an app contains one or more extensions, a top-level menu named `Extensions` will appear in the main menu bar of Studio Pro. Menus that are created from `MenuExtension` implementations will be grouped by their extension entry point name (in this example, `MyCompany`), and then placed under their own dedicated submenu under the main `Extensions` top-level menu. For example, the `MyMenuExtension` above will be placed as follows: **Extensions** > **MyCompany** > **MyMenuExtension**. + +A menu can only be a parent (namely, a menu that contains submenus) or have an action. You cannot create a menu with an action which also contains submenus. + +You can add a `MenuSeparator` to a menu, via the `Separator` property. Options are `After`, `Before` or `None`. The default value is `None`. You can also disable a menu by setting its `IsEnabled` property to `false`. Menus are enabled by default. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/build-todo-example-extension.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/build-todo-example-extension.md new file mode 100644 index 00000000000..96b6105ef57 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/build-todo-example-extension.md @@ -0,0 +1,920 @@ +--- +title: "Build a Todo Example Extension Using C#" +linktitle: "ToDo Example" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-11/build-todo-example-extension/ +weight: 20 +--- + +## Introduction + +This document describes how to build an example extension that adds a simple todo list extension to Studio Pro. With this example extension, you can add new todo items to a list. The example extension will be added to the main menu of Studio Pro and you will add a user interface for the example extension by using a dockable pane and some web content. + +This document covers the following topics: + +* How to create an extension project and configure it for use as an extension in Studio Pro +* How to create a web-based user interface for Studio Pro +* How to store information in a local storage JSON file +* How to interact with the Mendix metamodel +* How to host your user interface within the Studio Pro IDE + +## Prerequisites + +Before you start the procedure, make sure that you have installed the following tools on your local development environment: + +* Microsoft Visual Studio 2022 or another equivalent development environment, such as visual studio code or JetBrains Rider. This example will assume that you are using Microsoft Visual Studio 2022. +* Studio Pro version 10.6 or higher + +## Creating the Project and Configuring It as an Extension + +### Creating the Project + +In order for your extension to be loaded correctly as an extension in Studio Pro, you will first need to create a project: + +1. In Visual Studio, create a new project. +2. Select the *Class Library* project template and click **Next**. + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/extensibility-api-howtos/build-todo-example-extension/step-one.png" >}} + +3. Name the project *Mendix.ToDoExtension*. +4. Choose a location to store your extension, and click **Next**. + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/extensibility-api-howtos/build-todo-example-extension/step-two.png" >}} + +5. Set **Framework** to *.NET 8.0 (Long Term Support)* and click **Create**. + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/extensibility-api-howtos/build-todo-example-extension/step-three.png" max-width=80% >}} + +Now you have an empty project. + +### Installing Extensions API NuGet Package + +You must do the following steps to configure the project so that it can be used as an extension in Studio Pro: + +1. Reference the extensibility API NuGet package +2. Add a `manifest.json` file to the solution. + +The details of each step are described below. + +#### Referencing the Extensibility API NuGet Package + +1. In Visual Studio, go to **Tools** > **NuGet Package Manager** > **Manage NuGet Packages for Solution**. + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/extensibility-api-howtos/build-todo-example-extension/step-four.png" >}} + +2. On the **Browse** tab, search for **Mendix ExtensionsAPI**. + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/extensibility-api-howtos/build-todo-example-extension/step-five.png" max-width=50% >}} + +3. Select the NuGet package and click **Install**. + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/extensibility-api-howtos/build-todo-example-extension/step-six.png" max-width=50% >}} + +#### Adding a `manifest.json` File + +You now have a class library that can be loaded as an extension by Studio Pro. However, Studio Pro still needs some help in determining how to load the assemblies of your class library. Studio Pro reads a special file called *manifest.json*. This file instructs Studio Pro on which assemblies to load into each execution context. + +1. In Visual Studio, go to **View** > **Solution Explorer** to open the Solution Explorer. +2. Right-click in the Solution Explorer and add a new file called *manifest.json*. +3. Right-click in the Solution Explorer and select **Properties**. +4. Ensure that you set the **Copy to Output Directory** property to **Copy always** to ensure that this file is included in your extensions output files. +5. Replace the contents of your manifest.json file with the following code: + + ```json + { + "mx_extensions": [ "ToDoExtension.dll" ], + "mx_build_extensions": [ ] + } + ``` + + Within the *manifest.json* file, you specify which assemblies Studio Pro needs to load for the different execution contexts your extension needs to provide. If your extension only needs access to design time features and requires a user interface, then you can add it to the `mx_extensions` option. However, if your extension needs access to runtime information or perhaps needs to translate design time concepts into runtime concepts, then you will add it to `mx_build_extensions`. + + {{% alert color="warning" %}}Build extensions specified in `mx_build_extensions` will not have access to any user interfaces and attempting to link to user interface libraries will fail. Similarly, extensions loaded from `mx_extensions` cannot access any runtime features and are strictly design time only.{{% /alert %}} + + This will instruct Studio Pro to load `Mendix.ToDoExtension.dll`, whenever it loads Studio Pro extensions for your app. Adjust your local project names as needed. + +6. Remove the `Class1.cs` file, as you will not need it anymore. + +## Adding a Dockable Pane to Host Your User Interface + +In this section, you will host a dockable pane within Studio Pro. This will provide you with a window where you can render the User Interface of the extension. + +1. Add a new file to the solution called `ToDoListDockablePaneExtension.cs`. +2. Replace the contents of the file with the following code: + + ```csharp + using System.ComponentModel.Composition; + using Mendix.StudioPro.ExtensionsAPI.Services; + using Mendix.StudioPro.ExtensionsAPI.UI.DockablePane; + + namespace Mendix.ToDoExtension; + + [Export(typeof(DockablePaneExtension))] + public class ToDoListDockablePaneExtension : DockablePaneExtension + { + private readonly ILogService _logService; + public const string PaneId = "ToDoList"; + + [ImportingConstructor] + public ToDoListDockablePaneExtension(ILogService logService) + { + _logService = logService; + } + + public override string Id => PaneId; + + public override DockablePaneViewModelBase Open() + { + return new ToDoListDockablePaneViewModel(WebServerBaseUrl, () => CurrentApp, _logService) { Title = "To Do List" }; + } + } + ``` + +{{% alert color="info" %}} +You are expected to get an error at this point around the `ToDoListDockablePaneViewModel`. +{{% /alert %}} + +### Explanation + +There are a few notable features of the class in the code above: +First, the top of the class is decorated with an `Export` attribute: + +```csharp +[Export(typeof(DockablePaneExtension))] +``` + +Studio Pro uses this attribute to identify which extension type to inject this class into. If you do not specify this attribute, Studio Pro will not load your extension type. Additionally, the extension descends from `DockablePaneExtension`. Studio Pro uses abstract classes to enforce behavior for your extensions. + +```csharp +public class ToDoListDockablePaneExtension : DockablePaneExtension +``` + +In order for your type to be loaded, you will need to add a `ImportingConstructor` attribute to the preferred constructor. Studio Pro will use this constructor when instantiating your extension class. + +When instantiating your class, Studio Pro will attempt to perform dependency injection for any of the types that you define in the constructor. + +If you wish to inject your own custom types, they will also need to be decorated with the `Export` attribute. + +```csharp + [ImportingConstructor] + public ToDoListDockablePaneExtension(ILogService logService) + { + _logService = logService; + } +``` + +In this constructor, you will note that you request an instance of the `ILogService` and then save the instance in a private field. + +```csharp + public override string Id => PaneId; + + public override DockablePaneViewModelBase Open() + { + return new ToDoListDockablePaneViewModel(WebServerBaseUrl, () => CurrentApp, _logService) { Title = "To Do List" }; + } +``` + +In the final portion of the class, provide some necessary information to Studio Pro: + +First, you override the `Id` property. This property provides Studio Pro with a way to uniquely identify your dockable pane extension. Second, you override the `Open` method. Within this method you need to return a valid implementation of `DockablePaneViewModelBase` which studio Pro will use to render your pane's contents. + +In summary, in this section you performed the following: + +1. Create a new class that descends from `DockablePaneExtension`. +2. Decorate your class with the `Export` attribute. +3. Decorate your preferred constructor with the `ImportingConstructor` attribute. +4. Inject the `ILogService`. +5. Return a valid view model from the open method. + +## Creating a View Model to Host Your View Data + +In this section, you will add a view model to store our view data: + +1. Add a new file to the solution and name it `ToDoListDockablePaneViewModel.cs`. +2. Replace the contents of the file with the following code: + + ```csharp + using Mendix.StudioPro.ExtensionsAPI.Model; + using Mendix.StudioPro.ExtensionsAPI.Services; + using Mendix.StudioPro.ExtensionsAPI.UI.DockablePane; + using Mendix.StudioPro.ExtensionsAPI.UI.WebView; + + namespace Mendix.ToDoExtension; + + public class ToDoListDockablePaneViewModel : WebViewDockablePaneViewModel { + + private readonly Uri _baseUri; + private readonly Func _getCurrentApp; + private readonly ILogService _logService; + + public ToDoListDockablePaneViewModel(Uri baseUri, Func getCurrentApp, ILogService logService) + { + _baseUri = baseUri; + _getCurrentApp = getCurrentApp; + _logService = logService; + } + + public override void InitWebView(IWebView webView) + { + webView.Address = new Uri(_baseUri, "index"); + + webView.MessageReceived += (_, args) => + { + var currentApp = _getCurrentApp(); + if (currentApp == null) return; + + if (args.Message == "AddToDo") + { + var toDoText = args.Data["toDoText"]?.GetValue() ?? "New To Do"; + AddToDo(currentApp, toDoText); + webView.PostMessage("RefreshToDos"); + } + + if (args.Message == "ChangeToDoStatus") + { + var toDoId = args.Data["id"]!.GetValue(); + var newIsDone = args.Data["isDone"]!.GetValue(); + + ChangeToDoStatus(currentApp, toDoId, newIsDone); + webView.PostMessage("RefreshToDos"); + } + + if (args.Message == "ClearDone") + { + ClearDone(currentApp); + webView.PostMessage("RefreshToDos"); + } + }; + } + + private void AddToDo(IModel currentApp, string toDoText) + { + var toDoStorage = new ToDoStorage(currentApp, _logService); + var toDoList = toDoStorage.LoadToDoList(); + toDoList.ToDos.Add(new ToDoModel(toDoText, false)); + toDoStorage.SaveToDoList(toDoList); + } + + private void ChangeToDoStatus(IModel currentApp, string toDoId, bool newIsDone) + { + var toDoStorage = new ToDoStorage(currentApp, _logService); + var toDoList = toDoStorage.LoadToDoList(); + var toDo = toDoList.ToDos.FirstOrDefault(x => x.Id == toDoId); + if (toDo != null) + { + toDo.IsDone = newIsDone; + toDoStorage.SaveToDoList(toDoList); + } + } + + private void ClearDone(IModel currentApp) + { + var toDoStorage = new ToDoStorage(currentApp, _logService); + var toDoList = toDoStorage.LoadToDoList(); + toDoList.ToDos.RemoveAll(x => x.IsDone); + toDoStorage.SaveToDoList(toDoList); + } + } + ``` + +{{% alert color="warning" %}} +{{% snippet file="/static/_includes/apidocs-mxsdk/warning-wwwroot.md" %}} +{{% /alert %}} + +### Explanation + +The first important thing to note about this view model class is that you do not decorate this class with the export attribute. This means that the extension will be responsible for instantiating this type within the extension. This also means that you can specify any type you like within the constructor. You already set up the instantiation of this class in the previous section. The important bit here is that you pass in the `baseUri`, `getCurrentApp` lambda expression and an instance of the logging class. + +```csharp + public ToDoListDockablePaneViewModel(Uri baseUri, Func getCurrentApp, ILogService logService) + { + _baseUri = baseUri; + _getCurrentApp = getCurrentApp; + _logService = logService; + } +``` + +In order to host a web interface inside Studio Pro, your viewmodel must implement `InitWebView`. Within this method you are passed an instance of `IWebView`. This is your application's isolated webview. You now need to provide the webview some information so that it will render its data correctly. + +```csharp + public override void InitWebView(IWebView webView) + { + webView.Address = new Uri(_baseUri, "index"); + + webView.MessageReceived += (_, args) => + { + var currentApp = _getCurrentApp(); + if (currentApp == null) return; + + if (args.Message == "AddToDo") + { + var toDoText = args.Data["toDoText"]?.GetValue() ?? "New To Do"; + AddToDo(currentApp, toDoText); + webView.PostMessage("RefreshToDos"); + } + + if (args.Message == "ChangeToDoStatus") + { + var toDoId = args.Data["id"]!.GetValue(); + var newIsDone = args.Data["isDone"]!.GetValue(); + + ChangeToDoStatus(currentApp, toDoId, newIsDone); + webView.PostMessage("RefreshToDos"); + } + + if (args.Message == "ClearDone") + { + ClearDone(currentApp); + webView.PostMessage("RefreshToDos"); + } + }; + } +``` + +{{% alert color="warning" %}} +{{% snippet file="/static/_includes/apidocs-mxsdk/warning-wwwroot.md" %}} +{{% /alert %}} + +Firstly, you set the default address to `new Uri(_baseUri, "index")`. You will delve a bit deeper into where this index comes from later in the guide. If you want to skip ahead, see [Setting up Communication Between the User Interface and Extension](#set-up-communication) + +Secondly, you add an event handler for the `MessageReceived` event. You will be using this method send and respond to messages from the webview. Within Studio Pro, use a two-way message bus as the primary communication method between your web based user interface and your extension logic. + +Inside the message received event handler, add some code to handle the tasks you need to perform: + +* `AddToDo` will add a Todo Item to the list. +* `ChangeToDoStatus` will change the status of a Todo item. +* `ClearDone` will remove all items flagged as done. + +```csharp + var currentApp = _getCurrentApp(); + if (currentApp == null) return; + + if (args.Message == "AddToDo") + { + var toDoText = args.Data["toDoText"]?.GetValue() ?? "New To Do"; + AddToDo(currentApp, toDoText); + webView.PostMessage("RefreshToDos"); + } + + if (args.Message == "ChangeToDoStatus") + { + var toDoId = args.Data["id"]!.GetValue(); + var newIsDone = args.Data["isDone"]!.GetValue(); + + ChangeToDoStatus(currentApp, toDoId, newIsDone); + webView.PostMessage("RefreshToDos"); + } + + if (args.Message == "ClearDone") + { + ClearDone(currentApp); + webView.PostMessage("RefreshToDos"); + } +``` + +Now, create the methods responsible for performing the logic: + +```csharp + private void AddToDo(IModel currentApp, string toDoText) + { + var toDoStorage = new ToDoStorage(currentApp, _logService); + var toDoList = toDoStorage.LoadToDoList(); + toDoList.ToDos.Add(new ToDoModel(toDoText, false)); + toDoStorage.SaveToDoList(toDoList); + } + + private void ChangeToDoStatus(IModel currentApp, string toDoId, bool newIsDone) + { + var toDoStorage = new ToDoStorage(currentApp, _logService); + var toDoList = toDoStorage.LoadToDoList(); + var toDo = toDoList.ToDos.FirstOrDefault(x => x.Id == toDoId); + if (toDo != null) + { + toDo.IsDone = newIsDone; + toDoStorage.SaveToDoList(toDoList); + } + } + + private void ClearDone(IModel currentApp) + { + var toDoStorage = new ToDoStorage(currentApp, _logService); + var toDoList = toDoStorage.LoadToDoList(); + toDoList.ToDos.RemoveAll(x => x.IsDone); + toDoStorage.SaveToDoList(toDoList); + } +``` + +## Creating a Model to Store the Todo Information + +In order to store the information to disk, add some model classes that will be able to store the Todo information. + +1. Add a new class that will host the To do information itself. Call the file `ToDoModel.cs` +2. Replace the contents of the file with the following code: + + ```csharp + using System.Text.Json.Serialization; + + namespace Mendix.ToDoExtension; + + public record ToDoModel + { + [JsonConstructor] + public ToDoModel(string id, string text, bool isDone) + { + Id = id; + Text = text; + IsDone = isDone; + } + + public ToDoModel(string text, bool isDone) + : this(Guid.NewGuid().ToString(), text, isDone) + { + } + + public string Id { get; set; } + public string Text { get; set; } + public bool IsDone { get; set; } + } + ``` + + You will also need a model class that will store a list of all the todos that you have available. + +3. Add another class and name it `ToDoListModel.cs`. +4. Replace the contents of this file with the following code: + + ```csharp + using System.Text.Json.Serialization; + + namespace Mendix.ToDoExtension; + + public record ToDoListModel + { + [JsonConstructor] + public ToDoListModel(List toDos) + { + ToDos = toDos; + } + + public List ToDos { get; } + } + ``` + +## Creating a Storage Handler to Store the Todo Information + +With the models created, you can now create a storage handler that will manage storing these models to disk. + +1. Add a new class file and call it `ToDoStorage.cs`. +2. Replace the contents of the file with the following code: + + ```csharp + using System.Text; + using System.Text.Json; + using Mendix.StudioPro.ExtensionsAPI.Model; + using Mendix.StudioPro.ExtensionsAPI.Services; + + namespace Mendix.ToDoExtension; + + public class ToDoStorage + { + private readonly ILogService _logService; + private readonly string _toDoFilePath; + + public ToDoStorage(IModel currentApp, ILogService logService) + { + _logService = logService; + _toDoFilePath = Path.Join(currentApp.Root.DirectoryPath, "to-do-list.json"); + } + + public ToDoListModel LoadToDoList() + { + ToDoListModel? toDoList = null; + + try + { + toDoList = JsonSerializer.Deserialize(File.ReadAllText(_toDoFilePath, Encoding.UTF8)); + } + catch (Exception exception) + { + _logService.Error($"Error while loading To Dos from {_toDoFilePath}", exception); + } + + return toDoList ?? new ToDoListModel(new[] + { + new ToDoModel("Buy milk", false), + new ToDoModel("Fix house", false), + new ToDoModel("Shave yak", true) + }.ToList()); + } + + public void SaveToDoList(ToDoListModel toDoList) + { + var jsonText = JsonSerializer.Serialize(toDoList, new JsonSerializerOptions() { WriteIndented = true }); + File.WriteAllText(_toDoFilePath, jsonText, Encoding.UTF8); + } + } + ``` + +### Explanation + +The `ToDoStorage` class will be responsible for storing the todo information to disk. In order to store the file in the correct path, you need to request the path from the `CurrentApp` instance. + +```csharp + public ToDoStorage(IModel currentApp, ILogService logService) + { + _logService = logService; + _toDoFilePath = Path.Join(currentApp.Root.DirectoryPath, "to-do-list.json"); + } +``` + +You also need to handle loading and saving of the todo data. + +```csharp + public ToDoListModel LoadToDoList() + { + ToDoListModel? toDoList = null; + + try + { + toDoList = JsonSerializer.Deserialize(File.ReadAllText(_toDoFilePath, Encoding.UTF8)); + } + catch (Exception exception) + { + _logService.Error($"Error while loading To Dos from {_toDoFilePath}", exception); + } + + return toDoList ?? new ToDoListModel(new[] + { + new ToDoModel("Buy milk", false), + new ToDoModel("Fix house", false), + new ToDoModel("Shave yak", true) + }.ToList()); + } + + public void SaveToDoList(ToDoListModel toDoList) + { + var jsonText = JsonSerializer.Serialize(toDoList, new JsonSerializerOptions() { WriteIndented = true }); + File.WriteAllText(_toDoFilePath, jsonText, Encoding.UTF8); + } +``` + +## Adding a Menu Item to Open the Extension from the Main Menu + +In this section, you will add a menu item to the toolbar that will allow you to select the ToDo list from a menu item. + +1. Create a `MenuExtension`. +2. Add another class and call it `ToDoListMenuExtension.cs`. +3. Replace the contents of the file with the following code: + + ```csharp + using System.Collections.Generic; + + using System.ComponentModel.Composition; + using Mendix.StudioPro.ExtensionsAPI.UI.DockablePane; + using Mendix.StudioPro.ExtensionsAPI.UI.Menu; + using Mendix.StudioPro.ExtensionsAPI.UI.Services; + + namespace Mendix.ToDoExtension; + + [Export(typeof(Mendix.StudioPro.ExtensionsAPI.UI.Menu.MenuExtension))] + public class ToDoListMenuBarExtension : MenuExtension + { + private readonly IDockingWindowService _dockingWindowService; + + [ImportingConstructor] + public ToDoListMenuBarExtension(IDockingWindowService dockingWindowService) + { + _dockingWindowService = dockingWindowService; + } + + public override IEnumerable GetMenus() + { + yield return new MenuViewModel("To Do List", () => _dockingWindowService.OpenPane(ToDoListDockablePaneExtension.PaneId)); + } + } + ``` + +## Adding a Web-based User Interface + +Up to now you have been adding all the logic that will allow your extension to run inside Studio Pro. In this section, you will add a user interface for the extension. In Studio Pro, you need to load your user interface elements as web content. This web content is then rendered from within an isolated web view in Studio Pro. + +{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/extensibility-api-howtos/build-todo-example-extension/add-web-items.png" >}} + +1. Add a new folder to the solution and call it `wwwroot`. +2. In the folder, add two files: + + * A HTML page that contains the layout of the user interface. Call it `index.html` + * A JavaScript file that contains the client side logic for the user interface. Call it `main.js` + +3. Open `index.html`. +4. Replace its contents with the following: + + ```html + + + To Do List + + + + +
+

To Do

+
+

Done

+
+ + + + + ``` + +5. Open `main.js` and add the JavaScript logic by replacing the contents of the file with the following: + + ```js + function postMessage(message, data) { + window.chrome.webview.postMessage({ message, data }); + } + + // Register message handler. + window.chrome.webview.addEventListener("message", handleMessage); + // Indicate that you are ready to receive messages. + postMessage("MessageListenerRegistered"); + + async function handleMessage(event) { + const { message, data } = event.data; + if (message === "RefreshToDos") { + await refreshToDos(); + } + } + + async function refreshToDos() { + let todosResponse = await fetch("./todos"); + let todos = await todosResponse.json(); + + let todoDiv = document.getElementById("todo"); + let doneDiv = document.getElementById("done"); + + let todoItems = []; + let doneItems = []; + + for (const todo of todos.ToDos) { + let item = document.createElement("div"); + + let checkbox = document.createElement("input"); + checkbox.type = "checkbox"; + checkbox.id = `todo-${todo.Id}`; + checkbox.checked = todo.IsDone; + checkbox.addEventListener("click", () => { + postMessage("ChangeToDoStatus", { id: todo.Id, isDone: !todo.IsDone }); + }); + + let label = document.createElement("label"); + label.htmlFor = checkbox.id; + label.innerText = todo.Text; + + item.replaceChildren(checkbox, label); + + if (todo.IsDone) { + doneItems.push(item); + } else { + todoItems.push(item); + } + } + + todoDiv.replaceChildren(...todoItems); + doneDiv.replaceChildren(...doneItems); + } + + async function addToDo(){ + let addToDoInput = document.getElementById("addToDoInput"); + const toDoText = addToDoInput.value; + postMessage("AddToDo", { toDoText }); + addToDoInput.value = ""; + } + + document.getElementById("addToDoButton").addEventListener("click", addToDo); + document.getElementById("clearDoneButton").addEventListener("click", () => { + postMessage("ClearDone"); + }); + + await refreshToDos(); + ``` + +### Explanation + +This HTML page is self-explanatory, as you are providing a very simple interface with some added CSS styling provided by Tailwind CSS. + +Within the JavaScript file, you need to add some logic so that the web view can communicate with your extension logic correctly. + +You add a small helper function to simplify the call to the browser API: + +```javascript +function postMessage(message, data) { + window.chrome.webview.postMessage({ message, data }); +} +``` + +You also need to perform some initialization to ensure that you can respond to messages send to JavaScript and + +```javascript +// Register message handler. +window.chrome.webview.addEventListener("message", handleMessage); +// Indicate that you are ready to receive messages. +postMessage("MessageListenerRegistered"); + +async function handleMessage(event) { + const { message, data } = event.data; + if (message === "RefreshToDos") { + await refreshToDos(); + } +} +``` + +It is important to set these two `index.html` and `main.js` files to *Copy always* or *Copy if newer* in their **Copy to Output Directory** property; otherwise, they will not be present in the build output folder when you are ready to start using the extension. + +## Setting up Communication Between the User Interface and Extension {#set-up-communication} + +So far you have configured the extension to be usable in Studio Pro. You added support for storing the to do items. You also added a user interface that users can interact with. The last step in this process is to link the extension c# logic with the web-based JavaScript logic. + +1. Start with adding a utility class to help simplify the way you interact with web responses. Call the file `HttpListenerResponseUtils.cs`. +2. Replace the contents of the file with the following: + + ```csharp + using System.Net; + using System.Text; + + namespace Mendix.ToDoExtension; + + public static class HttpListenerResponseUtils + { + public static async Task SendFileAndClose(this HttpListenerResponse response, string contentType, string filePath, CancellationToken ct) + { + response.AddDefaultHeaders(200); + + var fileContents = await File.ReadAllBytesAsync(filePath, ct); + + response.ContentType = contentType; + response.ContentLength64 = fileContents.Length; + + await response.OutputStream.WriteAsync(fileContents, ct); + + response.Close(); + } + + public static void SendJsonAndClose(this HttpListenerResponse response, MemoryStream jsonStream) + { + response.AddDefaultHeaders(200); + + response.ContentType = "application/json"; + response.ContentEncoding = Encoding.UTF8; + response.ContentLength64 = jsonStream.Length; + + jsonStream.WriteTo(response.OutputStream); + + response.Close(); + } + + public static void SendNoBodyAndClose(this HttpListenerResponse response, int statusCode) + { + response.AddDefaultHeaders(statusCode); + + response.Close(); + } + + static void AddDefaultHeaders(this HttpListenerResponse response, int statusCode) + { + response.StatusCode = statusCode; + + // Makes sure the web-code can receive responses + response.AddHeader("Access-Control-Allow-Origin", "*"); + } + } + ``` + +### Explanation + +Your web-based user interface is hosted inside Studio Pro in an isolated web container. As such to communicate with it you are adding some utility functionality to help you improve the code. + +The first method you add is `SendFileAndClose`. This function will allow you to send the contents of a file located on your hard drive to the webpage where your user interface is hosted. + +Next you add `SendJsonAndClose`. This method functions similarly to `SendFileAndClose`, but will accept a json stream instead of a file path. + +After that you add `SendNoBodyAndClose`. This sends an empty response with just a status code to the webpage. + +The final method `AddDefaultHeaders` is a utility method that adds some default http headers to the requests. + +## Next Steps + +In this section, you will add a web server extension. This extension type allows you to serve web content easily within extensions. + +1. Add a new file called: `ToDoListWebServerExtension.cs`. +2. Replace the contents of the file with the following code: + + ```csharp + using System.ComponentModel.Composition; + using System.Net; + using System.Text.Json; + using Mendix.StudioPro.ExtensionsAPI.Services; + using Mendix.StudioPro.ExtensionsAPI.UI.WebServer; + + namespace Mendix.ToDoExtension; + + [Export(typeof(WebServerExtension))] + public class ToDoListWebServerExtension : WebServerExtension + { + private readonly IExtensionFileService _extensionFileService; + private readonly ILogService _logService; + + [ImportingConstructor] + public ToDoListWebServerExtension(IExtensionFileService extensionFileService, ILogService logService) + { + _extensionFileService = extensionFileService; + _logService = logService; + } + + public override void InitializeWebServer(IWebServer webServer) + { + webServer.AddRoute("index", ServeIndex); + webServer.AddRoute("main.js", ServeMainJs); + webServer.AddRoute("todos", ServeToDos); + } + + private async Task ServeIndex(HttpListenerRequest request, HttpListenerResponse response, CancellationToken ct) + { + var indexFilePath = _extensionFileService.ResolvePath("wwwroot", "index.html"); + await response.SendFileAndClose("text/html", indexFilePath, ct); + } + + private async Task ServeMainJs(HttpListenerRequest request, HttpListenerResponse response, CancellationToken ct) + { + var indexFilePath = _extensionFileService.ResolvePath("wwwroot", "main.js"); + await response.SendFileAndClose("text/javascript", indexFilePath, ct); + } + + private async Task ServeToDos(HttpListenerRequest request, HttpListenerResponse response, CancellationToken ct) + { + if (CurrentApp == null) + { + response.SendNoBodyAndClose(404); + return; + } + + var toDoList = new ToDoStorage(CurrentApp, _logService).LoadToDoList(); + var jsonStream = new MemoryStream(); + await JsonSerializer.SerializeAsync(jsonStream, toDoList, cancellationToken: ct); + + response.SendJsonAndClose(jsonStream); + } + } + ``` + +This class is the web container that allows Studio Pro to interact with your user interface. Within this class you will serve the web content to your extension logic. + +Note that you inherit from `WebServerExtension`. `WebServerExtension` serves web content to Studio Pro. + +Additionally, you override the `InitializeWebServer` method. Studio Pro will call this method during startup, and you should place all your initialization logic in here. This implementation adds three web routes. These web routes are the locations where your user interface can be accessed from. + +`ServeIndex`, `ServeMainJs`, and `ServeToDos` serve the contents of the 3 routes to your extension logic. + +## Hosting the Extension in Studio Pro + +All the code you need should now be complete. The last step in the process is building your solution and adding your binary output as an extension inside your app. + +To do this you will need to do the following: + +1. Build your solution in Visual Studio by selecting the **Build** > **Build Solution**. +2. Navigate to the Mendix app where your extension will be hosted. +3. Create a new folder and name it `extensions`. +4. Add a subfolder and name it `TodoExtension`. You now have a folder with a path similar to this `[Mendix App]/extensions/MyTodoExtension`/. +5. Copy the files from your Visual Studio extension projects `bin/debug` subfolder into your mendix app extension folder: [`Mendix App]/extensions/MyTodoExtension`. +6. Run Studio Pro. + +While developing extensions, also use command line flag to enable extensions as follows: + +1. Navigate to your Studio Pro Installation folder. +2. From the command line, call the following command: `.\studiopro.exe --enable-extension-development`. + +This will start an instance of Studio Pro and load your extension. You can now view your new extension interface from the **View** > **Todo** menu item. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/create-context-menu.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/create-context-menu.md new file mode 100644 index 00000000000..c528e987457 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/create-context-menu.md @@ -0,0 +1,119 @@ +--- +title: "Create a Context Menu Using C#" +linktitle: "Context Menu" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-context-menu/ +weight: 6 +--- + +## Introduction + +It is possible to add a context menu to an `IEntity` in Studio Pro or to a `IDocument`, such as microflows and pages, etc. Context menus will be placed under a menu named after the extension that contains them (e.g. `MyExtension`) and context menus can modify those items they relate to. You can achieve this simple by specifying the type when creating the extension. + +This how-to describes how you can add a context menu to an entity. Before you start this how-to, it is recommended to [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-menu-extension/) first. + +You can download the example in this how-to in [this GitHub repository](https://github.com/mendix/ExtensionAPI-Samples). + +## Creating an Entity Context Menu Extension Class + +1. Open the project that you previously created when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-menu-extension/). +2. Add a new class to the project and name it `MyEntityContextMenuExtension.cs`. +3. Replace the code in the file with the following code: + + ```csharp + namespace MyCompany.MyProject.MendixExtension; + + [method: ImportingConstructor] + [Export(typeof(Mendix.StudioPro.ExtensionsAPI.UI.Menu_V2.ContextMenuExtension<>))] + class MyEntityContextMenuExtension(IMessageBoxService messageBoxService) : Mendix.StudioPro.ExtensionsAPI.UI.Menu_V2.ContextMenuExtension + { + MenuViewModel? disabledMenu; + + public override IEnumerable GetContextMenus(IEntity entity) + { + var nudgeDownLeft = new MenuViewModel("Left", () => NudgeIt(entity, right: false, down: true)); + var nudgeDownLeftRight = new MenuViewModel("Right", () => NudgeIt(entity, right: true, down: true) ); + var nudgeDown = new MenuViewModel("Down", [nudgeDownLeft, nudgeDownLeftRight]); + + var nudgeUpLeft = new MenuViewModel("Left", () => NudgeIt(entity, right: false, down: false)); + var nudgeUpRight = new MenuViewModel("Right", () => NudgeIt(entity, right: true, down: false)); + var nudgeUp = new MenuViewModel("Up", [nudgeUpLeft, nudgeUpRight]) + { + Separator = MenuSeparator.Before + }; + + yield return new MenuViewModel("Nudge it!", [nudgeDown, nudgeUp]); + + // new group + var randomRenameString = new MenuViewModel("Random string", () => RenameEntity(entity, number: false)); + var randomRenameNumber = new MenuViewModel("Random number", () => RenameEntity(entity, number: true)); + + yield return new MenuViewModel("Randomly rename", [randomRenameString, randomRenameNumber]); + + // show location + yield return new MenuViewModel("Show location", () => ShowLocation(entity, messageBoxService)); + + var showName = new MenuViewModel("Show current name", () => messageBoxService.ShowInformation(entity.Name)); + // "Randomly rename" was added previously, thus it will trigger an exception when collected if uncommented + //yield return new MenuViewModel("Randomly rename", [showName]); + + // Enabling a disabled menu + disabledMenu ??= new MenuViewModel("Disabled menu", () => + { + messageBoxService.ShowInformation("I'm enabled now. But not for long!"); + disabledMenu!.IsEnabled = false; + }){ IsEnabled = false }; + var enablingMenu = new MenuViewModel("Enable disabled menu", () => disabledMenu.IsEnabled = true ); + + yield return new MenuViewModel("Enabling menus", [disabledMenu, enablingMenu]); + + } + + void NudgeIt(IEntity entity, bool right, bool down) + { + var incrementRight = right ? 20 : -20; + var incrementDown = down ? 20 : -20; + using var transaction = CurrentApp!.StartTransaction("nudge with context menu"); + entity.Location = new Location(entity.Location.X + incrementRight, entity.Location.Y + incrementDown); + + transaction.Commit(); + } + + void RenameEntity(IEntity entity, bool number) + { + using var transaction = CurrentApp!.StartTransaction("rename with context menu"); + + entity.Name = number ? $"E_{new Random().Next()}" : $"E_{Guid.NewGuid().ToString().Replace("-", "")}"; + + transaction.Commit(); + } + static void ShowLocation(IEntity entity, IMessageBoxService messageBoxService) => messageBoxService.ShowInformation($"X: {entity.Location.X}, Y: {entity.Location.Y}"); + } + ``` + +The code above creates a series of context menu items for any entity. It is important to note the type `IEntity` is passed in so that the context menu will only apply to entities. It adds menus using the same logic as `MenuExtension.cs`. For more examples, see [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-menu-extension/). + +In the code above, you can see a few context menus that can perform changes on the entity they belong to. In The entity's location on the canvas can be changed, it can be renamed, and some of its information is shown in a message box. + +It is also possible to add menus that are disabled until some action is performed. + +## Adding a Context Menu to a Document + +You can also add a context menu to a document. All you have to do is specify the type `IDocument` in the context menu extension. + +```csharp +namespace MyCompany.MyProject.MendixExtension; + +[method: ImportingConstructor] +[Export(typeof(ContextMenuExtension<>))] +class MyDocumentContextMenuExtension(IMessageBoxService messageBoxService) : ContextMenuExtension +{ + public override IEnumerable GetContextMenus(IDocument document) + { + if (document is IMicroflow microflow) + yield return new MenuViewModel("This document is a microflow", () => messageBoxService.ShowInformation(microflow.Name)); + + else if (document is IPage page) + yield return new MenuViewModel("This document is a page", () => messageBoxService.ShowInformation(page.Name)); + } +} +``` diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/create-dockable-pane.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/create-dockable-pane.md new file mode 100644 index 00000000000..63a3da05b39 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/create-dockable-pane.md @@ -0,0 +1,94 @@ +--- +title: "Create a Dockable Pane Extension Using C#" +linktitle: "Dockable Pane" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-dockable-pane-extension/ +weight: 5 +--- + +## Introduction + +This how-to describes how you can add a custom dockable web pane window to Studio Pro. Before you start this how-to, it is recommended to [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-menu-extension/) first. + +You can download the example in this how-to in [this GitHub repository](https://github.com/mendix/ExtensionAPI-Samples). + +## Creating a Dockable Pane Extension Class + +1. Open the project that you previously created when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-menu-extension/). +2. Add a new class to the project and name it `MyDockablePaneExtension.cs`. +3. Replace the code in the file with the following code: + + ```csharp + using System.ComponentModel.Composition; + using Mendix.StudioPro.ExtensionsAPI.UI.DockablePane; + + namespace MyCompany.MyProject.MendixExtension; + + [Export(typeof(DockablePaneExtension))] + public class MyDockablePaneExtension : DockablePaneExtension + { + public const string ID = "my-dockable-pane"; + public override string Id => ID; + + public override DockablePaneViewModelBase Open() => new MyDockablePaneExtensionWebViewModel("http://mendix.com"); + } + ``` + +## The View Model for the Extension Tab + +The dockable pane will have content, and this content comes in the form of a view model. The view model is an implementation of `WebViewDockablePaneViewModel`. + +You need to override the `InitWebView` method in which you can set up the content of your web view inside the dockable pane. In this example, it will contain the home page of `http://mendix.com`. + +Below is a small example code of the view model: + +```csharp +using Mendix.StudioPro.ExtensionsAPI.UI.DockablePane; +using Mendix.StudioPro.ExtensionsAPI.UI.WebView; + +namespace MyCompany.MyProject.MendixExtension; + +public class MyDockablePaneExtensionWebViewModel(string homePage) : WebViewDockablePaneViewModel +{ + public override void InitWebView(IWebView webView) => webView.Address = new Uri(homePage); +} +``` + +{{% alert color="warning" %}} +{{% snippet file="/static/_includes/apidocs-mxsdk/warning-wwwroot.md" %}} +{{% /alert %}} + +The code above creates a new web-enabled tab view. You still need a way to show the new dockable pane. To do so, you need to modify the menu extension you added when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-menu-extension/). Simple replace the existing content of `MyMenuExtension.cs` with the following code: + +```csharp +using System.ComponentModel.Composition; +using Mendix.StudioPro.ExtensionsAPI.UI.Menu; +using Mendix.StudioPro.ExtensionsAPI.UI.Services; + +namespace MyCompany.MyProject.MendixExtension; + +[Export(typeof(MenuExtension))] +public class MyMenuExtension(IDockingWindowService dockingWindowService, IMessageBoxService messageBoxService) : MenuExtension +{ + public override IEnumerable GetMenus() + { + yield return new MenuViewModel("Say hello", () => messageBoxService.ShowInformation("Hello World!")); + yield return new MenuViewModel("Open My Dockable Pane", () => dockingWindowService.OpenPane(MyDockablePaneExtension.ID)); + } +} +``` + +The code above introduces a few new concepts. + +Firstly, you inject the `IDockingWindowService` so that you can open a new dockable pane. + +Secondly, you add a new menu item with the caption **Open My Dockable Pane** that you will use to open your new dockable pane using the `IDockingWindow` service that you have injected. + +In order for the changes to reflect, you need to build your project. If you have opted to not automatically copy the output to the destination folder, then you will also need to manually copy the bin output from your project to your extension folder you created when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-menu-extension/). + +## Showing a Dockable Pane Without Adding a Custom Menu + +Instead of adding a separate menu to open the docking pane, you can override the `ViewMenuCaption` property in the implementation of the `DockablePaneExtension`. This means that the menu that opens will be placed under the `View` top-level menu in Studio Pro and will have the caption provided. There is no need for a separate `MenuExtension` in this case. + +```csharp +public override string? ViewMenuCaption => "My pane without custom menu"; +``` diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/create-menu-extension.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/create-menu-extension.md new file mode 100644 index 00000000000..34e96abff73 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/create-menu-extension.md @@ -0,0 +1,110 @@ +--- +title: "Create a Menu Extension Using C#" +linktitle: "Create Menu" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-menu-extension/ +weight: 4 +--- + +## Introduction + +This how-to describes how you can create an extension that adds an item to Studio Pro menu from scratch. + +You can download the example in this how-to in [this GitHub repository](https://github.com/mendix/ExtensionAPI-Samples). + +## Creating an Extension Project + +1. Create a new project in Visual Studio based on `C# Class Library` template. +2. Choose a name for the project. Use a format similar to `MyCompany.MyProject.MendixExtension`, but it is not a hard requirement. +3. Choose `.NET 8.0` Framework. +4. Add `Mendix.StudioPro.ExtensionsAPI` NuGet package to the project references. Pick the version that does not exceed the Studio Pro version you installed. To do so, perform the following steps: + + 1. Include a reference to the Extensions API [NuGet package](https://www.nuget.org/packages/Mendix.StudioPro.ExtensionsAPI): + 2. Add new file named `manifest.json` to your project. Put the following content into it: + + ```json + { "mx_extensions": [ ".dll" ] } + ``` + + 3. For the `manifest.json` file, right-click **Solution Explorer** > **Properties** and change the **Copy to Output Directory** property to **Copy always**. + +## Creating a Test Mendix App + +It is handy to have an app where the extension is used for the testing purposes. Perhaps you even want to share this app with your team by committing its code next to the extension project. + +1. Create a new Mendix app to use for testing the extension, based on a starter app of your choice. You can also use an existing app. +2. Go to **App** > **Show App Directory in Explorer** to open the app directory. +3. Create a new folder `extensions` inside the app directory. +4. Create a subfolder named after your extension, for example, `MyCompany`, inside the `extensions` folder. +5. Copy the full path of the subfolder by pressing Shift and right-clicking at the same time, and then selecting **Copy as path**. +6. Add the `Post-build event` script below to your extension project [Build > Events configuration](https://docs.microsoft.com/en-us/visualstudio/ide/how-to-specify-build-events-csharp?view=vs-2022): + `xcopy /y /s /i "$(TargetDir)" ""` + +Now if you build your extension project (usually you can do this by pressing Ctrl + Shift + B) and click [Synchronize App Directory](/refguide/app-menu/#synchronize) in Studio Pro (or press F4), the latest version of your extension will be loaded. + +## Creating Your First Extension + +To introduce a simple extension that adds a menu item to Studio Pro, add the following class: + +```csharp +using System.ComponentModel.Composition; +using Mendix.StudioPro.ExtensionsAPI.UI.Menu; +using Mendix.StudioPro.ExtensionsAPI.UI.Services; + +namespace MyCompany.MyProject.MendixExtension; + +[method: ImportingConstructor] +[Export(typeof(MenuExtension))] +public class MyMenuExtension(IMessageBoxService messageBoxService) : MenuExtension +{ + public override IEnumerable GetMenus() + { + yield return new MenuViewModel("Say hello", () => messageBoxService.ShowInformation("Hello World!")); + } +} +``` + +Build your extension and press F4 in Studio Pro. Extension menu items are placed under a corresponding menu with the extensions name. If your extension is named "My Extension", then your menu items will be located under the **Extensions** > **My Company** submenu. + +The Extensibility API provides you with several services you can use and they are injected into your extension classes by using the `ImportingConstructor` attribute. + +It is also possible to get notified when your extension has been successfully loaded into Studio Pro and also just before it gets unloaded. It is as simple as subscribing to the `ExtensionLoaded` and `ExtensionUnloading` events. + +```csharp +using Mendix.StudioPro.ExtensionsAPI.UI.Events; + +namespace MyCompany.MyProject.MendixExtension; + +[method: ImportingConstructor] +[Export(typeof(MenuExtension))] +public class MyMenuExtension() : MenuExtension +{ + public MyMenuExtension() + { + Subscribe(onEvent: () => { MyActionOnLoaded() }); + Subscribe(onEvent: () => { MyActionOnUnloading() }); + } +} +``` + +## Debugging Your Extension + +1. Make sure that the current version of the extension code is loaded in Studio Pro. +2. Attach to Studio Pro process in Visual Studio debugger as follows: + 1. On the **Debug** menu, open the **Attach to Process** dialog box (or press Ctrl + Alt + P). + 2. Search for `studiopro.exe` among the processes. + 3. Select the only found process (or the one with correct title, if you have many) and select **Attach**. + +3. Add a Breakpoint inside `Action` delegate in `MyMenuExtension.GetMenus()`. It will be hit when you click **Extensions** > **MyCompany** > **Say hello** menu item. + +## Adding a NuGet Dependency + +You can freely use [NuGet packages](https://www.nuget.org/) from extensions to access reusable .NET libraries. The following one-time additional setup is required: + +1. Open your extension project `.csproj` file by right-clicking **Solution Explorer** > **Edit Project File**. +2. Add the following line into the first ``: + + ```xml + true + ``` + +Then you can add a NuGet dependency, for example, using the **Manage NuGet Packages** menu. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/create-microflow-service.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/create-microflow-service.md new file mode 100644 index 00000000000..5c95c238a8c --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/create-microflow-service.md @@ -0,0 +1,145 @@ +--- +title: "Create a Microflow and Add Activities Using C#" +linktitle: "Microflows and Activities" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-microflow-add-activities/ +weight: 14 +--- + +## Introduction + +The `IMicroflowService` is the service used to perform actions related to microflows. This how-to describes how you can create a new microflow and add some activities to it. + +## `Initialize` + +The `Initialize` method initializes a microflow that was previously created. It is part of a series of steps required for adding a microflow to the model: + +1. Start a transaction (`IModel.StartTransaction`). +2. Create a microflow and add it to the module (`IModel.Create`). +3. Call `IMicroflowService.Initialize`. + +Internally, the `Initialize` method sets up the start and end flows, and adds any parameters that might be passed in (in the example below, you are passing a single parameter `boolParameter` of `DataType.Boolean`). + +In the example below, you also add activities to the microflow, with `IMicroflowService.TryInsertAfterStart` (adding an activity as the first) or `IMicroflowService.TryInsertBeforeActivity` (adding an activity before another). + +```csharp +public void Initialize(IModel currentApp, params IActionActivity[] actionActivities) +{ + var module = currentApp.Root.GetModules().Single(m => m.Name == "MyFirstModule"); + + using var transaction = currentApp.StartTransaction("Create and initialize microflow"); + + var microflow = currentApp.Create(); + microflow.Name = "Microflow"; + module!.AddDocument(microflow); + + _microflowService.Initialize(microflow, ("boolParameter", DataType.Boolean)); + + for (int i = 0; i < activities.Length; i++) + { + var activity = activities[i]; + if (i == 0) + _microflowService.TryInsertAfterStart(microflow, activity); + else + _microflowService.TryInsertBeforeActivity(activity, activities[i-1]); + } + + transaction.Commit(); +} +``` + +As you can see, this `IMicroflowService.Initialize` method can be cumbersome to use, since it is only part of the whole process of creating a new microflow. To have an easier method of creating microflows, use the `MicroflowService.CreateMicroflow` method. This method is described in the next section. + +## `CreateMicroflow` + +The `CreateMicroflow` method is the more advanced and comprehensive method to create microflows. It is a good alternative to the `IMicroflowService.Initialize` method. +The `CreateMicroflow` takes care of initialization and adding everything to the model in one single step. It requires the current `IModel`, the `IFolderBase` (module or folder) in which to save the microflow, a name, an optional `MicroflowReturnValue`, and an optional list of parameters. See the code below for a few examples. + +### Creating a Simple Microflow + +As shown in the code below, all that is required to create a microflow and add it to the model is the `IModel`, the `IFolderBase` in which to add the microflow, and its name. + +```csharp +public void CreateMicroflow(IModel currentApp) +{ + var module = currentApp.Root.GetModules().Single(m => m.Name == "MyFirstModule"); + + using var transaction = currentApp.StartTransaction("Create microflows"); + + var microflow = _microflowService.CreateMicroflow(currentApp, module, "Microflow"); + + transaction.Commit(); +} +``` + +### Creating Microflow with Return Type and Parameters + +In this more advanced example, you will see the `IMicroflowExpressionService.CreateFromString` method, which allows you to create expressions that can be then used as the `MicroflowReturnValue` of the microflow. Here, the expression is a simple addition of two values, and the return type is of `DataType.Integer`. + +```csharp + void CreateMicroflow(IModel currentApp) + { + var module = currentApp.Root.GetModules().Single(m => m.Name == "MyFirstModule"); + string returnValueExpression = "1 + 2"; + + var microflow = microflowService.CreateMicroflow(currentApp, module, "Microflow", + new MicroflowReturnValue(DataType.Integer, microflowExpressionService.CreateFromString(returnValueExpression))); + + transaction.Commit(); + } +``` + +The `IMicroflowService.CreateMicroflow` method is a bit easier to use than the `IMicroflowService.Initialize` method because it doesn't require manually creating the microflow with `IModel.Create` and then manually adding it to the `IFolderBase` container. It can do everything behind the scenes as long as everything is supplied to it. For a comprehensive example on how to create microflows, see [Create Microflows for Calculations Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-microflows-for-calculations/). + +## `TryInsertAfterStart` and `TryInsertBeforeActivity` + +The `TryInsertAfterStart` and `TryInsertBeforeActivity` methods enable inserting an activity in the flow. It can be added right after the start event of the microflow, or be inserted before another specific activity. + +```csharp +microflowService.TryInsertAfterStart(microflow, newActivity); +microflowService.TryInsertBeforeActivity(newAactivity, existingActivity); +``` + +## `GetParameters` + +The `GetParameters` method enables retrieving all the parameters that are inputs into a microflow. It returns a list of `IMicroflowParameterObject`, which is composed of its name, its `IQualifiedName` identifier, a description, and its `DataType`. Any parameters passed into the microflow will be returned here together with their type. + +```csharp +IReadOnlyList parameters = _microflowService.GetParameters(microflow); +``` + +## `GetAllMicroflowActivities` + +The `GetAllMicroflowActivities` method enables retrieving all the activities in the flow of a microflow. It returns a list of `IActivity`. + +```csharp +IReadOnlyList activities = _microflowService.GetAllMicroflowActivities(microflow); +``` + +## `IsVariableNameInUse` + +The `IsVariableNameInUse` method can check if the microflow already contains a variable with the name provided. This can be called before adding a new activity to the flow whose output variable name can overlap with existing variables. An example is as follows: + +```csharp +public void AddNewActivity(IModel currentApp, IMicroflow microflow, string activityName) +{ + using var transaction = currentApp.StartTransaction("Create microflows"); + + var microflowCallActivity = currentApp.Create(); + var microflowCallAction = currentApp.Create(); + microflowCallAction.MicroflowCall = currentApp.Create(); + microflowCallAction.MicroflowCall.Microflow = microflow.QualifiedName; + microflowCallActivity!.Action = microflowCallAction; + + if (!_microflowService.IsVariableNameInUse(microflow, activityName)) + microflowCallAction.OutputVariableName = activityName; + else + { + _messageBoxService.ShowError("That variable name is already in use."); + return; + } + + _microflowService.TryInsertAfterStart(microflow, microflowCallActivity); + + transaction.Commit(); +} +``` diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/create-microflows-calculations.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/create-microflows-calculations.md new file mode 100644 index 00000000000..41a9f29f02b --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/create-microflows-calculations.md @@ -0,0 +1,296 @@ +--- +title: "Create Microflows for Calculations Using C#" +linktitle: "Calculation Microflows" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-microflows-for-calculations/ +weight: 8 +--- + +## Introduction + +This how-to describes how you can create microflows which perform some calculations and return the result. + +You can download the example in this how-to in [this GitHub repository](https://github.com/mendix/ExtensionAPI-Samples). + +## Creating an Extension Class that Creates Microflows + +1. Open the project that you previously created when you [created a menu extension](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-menu-extension/). +2. Add a new folder *MicroflowTutorial* to your solution. +3. Create a `MenuExtension` class. +4. Add a new class to the project and call it `CreateMicroflowsMenu.cs`. +5. Replace the code in the file with the code below. + + ```csharp + using Mendix.StudioPro.ExtensionsAPI.UI.Menu; + using System.ComponentModel.Composition; + + namespace MicroflowTutorial; + + [Export(typeof(MenuExtension))] + [method: ImportingConstructor] + class CreateMicroflowsMenu(CalculationsMicroflowCreator microflowCreator) : MenuExtension + { + public override IEnumerable GetMenus() + { + yield return new MenuViewModel("Create microflows", () => + { + if (CurrentApp == null) + return; + + microflowCreator.CreateMicroflows(CurrentApp); + } + ); + } + } + ``` + + As you can see, the `GetMenus` method is overridden to add your own menus to Studio Pro. The class `CalculationsMicroflowCreator` (which you will add shortly) will be called from the action of your menu. You can see that this class has been injected in the constructor of your menu extension. + +## Microflow Creator + +In this section, you will add the class `CalculationsMicroflowCreator.cs`, which will be injected into your menu extension so that it can be called from your menu action. Make sure to add the `Export` attribute to the class; otherwise, it will not be injected into your menu extension. The `ImportingConstructor` attribute on the constructor is also very important, as this allows the class to receive any required services via dependency injection. + +```csharp +using Mendix.StudioPro.ExtensionsAPI.Model; +using Mendix.StudioPro.ExtensionsAPI.Model.DataTypes; +using Mendix.StudioPro.ExtensionsAPI.Model.Microflows; +using Mendix.StudioPro.ExtensionsAPI.Model.Projects; +using Mendix.StudioPro.ExtensionsAPI.Services; +using System.ComponentModel.Composition; + +namespace MicroflowTutorial; + +[Export(typeof(CalculationsMicroflowCreator))] +[method: ImportingConstructor] +class CalculationsMicroflowCreator(IMicroflowService microflowService, IMicroflowExpressionService microflowExpressionService) +{ + +} +``` + +This class contains one public method, which is the one called by your menu. The method `CreateMicroflows` requires the current app as the parameter. The `CreateMicroflowsMenu` extension has access to the `CurrentApp` property, so it will pass it to the method when calling it from the menu action. The `CurrentApp` property is the `IModel` for the app that is currently open in Studio Pro. Every extension that inherits the type `UIExtensionBase` (such as a `MenuBarExtension`) has access to the `CurrentApp` property and can interact and modify the model. + +Add the method as follows: + +```csharp +public void CreateMicroflows(IModel currentApp) +{ + var module = currentApp.Root.GetModules().Single(m => m.Name == "MyFirstModule"); + + using var transaction = currentApp.StartTransaction("Create microflows"); + + CreateMicroflowsInFolder(currentApp, module); + + transaction.Commit(); +} +``` + +As you can see, the `CreateMicroflows` method starts a new transaction, by calling `currentApp.StartTransaction`, which is the only way an extension can modify the model of the app. If your class tried to create microflows outside of a transaction, an error will be thrown. For more information, see [Interact with the Model API Using C#](/apidocs-mxsdk/apidocs/interact-with-model-api/). + + `IMicroflowService` enables creating microflows. For details, see [Create a Microflow and Add Activities Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-microflow-add-activities/). It requires the current model (`IModel`), the containing module or folder inside a module (`IFolderBase`), a name, and an optional `MicroflowReturnValue`. The return value is actually used in the example, so you will see how to create one as well. + +A microflow returns a value with `IMicroflowExpression`. This can be achieved by using your `IMicroflowExpressionService`, which returns an expression from a String input, and set that expression as the microflow's return value. + +A very simple `MicroflowReturnValue` can be created as follows: + +```csharp +new MicroflowReturnValue(DataType.Boolean, microflowExpressionService.CreateFromString("true or false")); +``` + +However, the example will have more complicated expressions, which use parameter names. These parameter names match the return values from called microflows. +This simple extension will create three microflows. Two of them will perform mathematical calculations (multiplication and addition) and each of them will return their result. The other microflow will call these two microflows in sequence, compute their two results (subtract the addition result from the multiplication result), and return true or false if the value is larger than 0. + +The method `CreateMicroflowsInFolder` will create the two microflows and the return values. Add the method. + +```csharp +void CreateMicroflowsInFolder(IModel currentApp, IFolderBase folder) +{ + string multiplicationResult = "multiplicationResult"; + string additionResult = "additionResult"; + + string returnValueExpression = $"(${multiplicationResult} - round(${additionResult}) > 0)"; + + var callingMicroflow = microflowService.CreateMicroflow(currentApp, folder, "Microflow", + new MicroflowReturnValue(DataType.Boolean, microflowExpressionService.CreateFromString(returnValueExpression))); + + CreateMultiplicationMicroflow(currentApp, folder, callingMicroflow, multiplicationResult); + CreateAdditionMicroflow(currentApp, folder, callingMicroflow, additionResult); +} +``` + +To create a microflow which performs a multiplication between two input parameters (two numbers in this case), you can use the code below. As you can see, the String `multiplication1` and the String `multiplication2` match the parameters used in the expression for the return value. Note that for an expression, the dollar sign `$` must be put in front of the parameter name in order to be recognized as a variable input. + +You can also see that the `DataType` of both parameters is integer. + +```csharp +void CreateMultiplicationMicroflow(IModel currentApp, IFolderBase folder, IMicroflow callingMicroflow, string outputVariableName) +{ + var multiplication1Param = "multiplication1"; + var multiplication2Param = "multiplication2"; + + var returnExpression = microflowExpressionService.CreateFromString($"${multiplication1Param} * ${multiplication2Param}"); + var returnValue = new MicroflowReturnValue(DataType.Integer, returnExpression); + + var multiplicationMicroflow = microflowService.CreateMicroflow(currentApp, folder, "MultiplicationMicroflow", + returnValue, + (multiplication1Param, DataType.Integer), + (multiplication2Param, DataType.Integer)); + + CreateMicroflowCallActivity(currentApp, callingMicroflow, multiplicationMicroflow, + outputVariableName, + (multiplication1Param, "10"), + (multiplication2Param, "100")); +} +``` + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/multiplication-microflow.png" >}} + +To create a microflow that performs an addition between two decimal values, you can use the code below. Just like the multiplication microflow example above, you can see that the String `addition1` and the String `addition2` match the parameters used in the expression for the return value. You can also see that their `DataType` is decimal. + +```csharp +void CreateAdditionMicroflow(IModel currentApp, IFolderBase folder, IMicroflow callingMicroflow, string outputVariableName) +{ + var addition1Param = "addition1"; + var addition2Param = "addition2"; + + var returnExpression = microflowExpressionService.CreateFromString($"${addition1Param} + ${addition2Param}"); + var returnValue = new MicroflowReturnValue(DataType.Decimal, returnExpression); + + var additionMicroflow = microflowService.CreateMicroflow(currentApp, folder, "AdditionMicroflow", + returnValue, + (addition1Param, DataType.Decimal), + (addition2Param, DataType.Decimal)); + + CreateMicroflowCallActivity(currentApp, callingMicroflow, additionMicroflow, + outputVariableName, + (addition1Param, "1.2"), + (addition2Param, "2.2")); +} +``` + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/addition-microflow.png" >}} + +Once a microflow is created, in order to enable this microflow to be called by other microflows, you need to add a call activity (`IActionActivity`). In the example, you have a method called `CreatMicroflowCallActivity` that can be used by both your multiplication and addition microflows. + +There are a few prerequisites that you must complete before a microflow can be called by another microflow. This method can be broken down into parts: + +```csharp +var microflowCallActivity = currentApp.Create(); +var microflowCallAction = currentApp.Create(); +microflowCallAction.MicroflowCall = currentApp.Create(); +microflowCallAction.MicroflowCall.Microflow = calledMicroflow.QualifiedName; +microflowCallActivity.Action = microflowCallAction; + +microflowCallAction.OutputVariableName = outputVariableName; +``` + +In order to create `IActionActivity`, `IMicroflowCallAction` must also be created, and set as the `Action` property of the `IActionActivity`. + +Then, for `IMicroflowCallAction`, `IMicroflowCall` must also be created and set as the `MicroflowCall` property of the `IMicroflowCallAction`. + +Next, `QualifiedName` of the microflow, which is to be called by this activity, must be set as the `Microflow` property of the `MicroflowCall` object. + +Finally, you can set `OutputVariableName` on `IActionActivity`, which is what the calling microflow will read from the called microflow. + +## Passing Parameters + +It is also possible to pass a set of parameters to the action activity, which will be the inputs for the called microflow. This set of parameters is a simple `Tuple` of a name and an expression. In the example, these parameters are the two integers for the multiplication microflow and the two decimals for the addition microflow. + +```csharp +foreach (var (parameterName, expression) in parameters) +{ + var parameterInCalledMicroflow = microflowService.GetParameters(calledMicroflow).Single(p => p.Name == parameterName); + var parameterMapping = currentApp.Create(); + parameterMapping.Argument = microflowExpressionService.CreateFromString(expression); + parameterMapping.Parameter = parameterInCalledMicroflow.QualifiedName; + microflowCallAction.MicroflowCall.AddParameterMapping(parameterMapping); +} +``` + +The method in its entirety is below and can be pasted into your `CalculationsMicroflowCreator` class. + +```csharp +void CreateMicroflowCallActivity(IModel currentApp, + IMicroflow microflowThatCalls, + IMicroflow calledMicroflow, + string outputVariableName, + params (string parameterName, string expression)[] parameters) +{ + var microflowCallActivity = currentApp.Create(); + var microflowCallAction = currentApp.Create(); + microflowCallAction.MicroflowCall = currentApp.Create(); + microflowCallAction.MicroflowCall.Microflow = calledMicroflow.QualifiedName; + microflowCallActivity.Action = microflowCallAction; + microflowCallAction.OutputVariableName = outputVariableName; + + foreach (var (parameterName, expression) in parameters) + { + var parameterInCalledMicroflow = microflowService.GetParameters(calledMicroflow).Single(p => p.Name == parameterName); + var parameterMapping = currentApp.Create(); + parameterMapping.Argument = microflowExpressionService.CreateFromString(expression); + parameterMapping.Parameter = parameterInCalledMicroflow.QualifiedName; + microflowCallAction.MicroflowCall.AddParameterMapping(parameterMapping); + } + + microflowService.TryInsertAfterStart(microflowThatCalls, microflowCallActivity); +} +``` + +To create a call activity for your multiplication and addition microflows, you can use something like the code below. As you can see, the parameter names for the activity match the parameter name from the microflow and their values are also passed in for integers and decimals. + +```csharp +CreateMicroflowCallActivity(currentApp, callingMicroflow, mathMicroflow, + outputVariableName, + ("multiplication1", "10"), + ("multiplication2", "100")); + +CreateMicroflowCallActivity(currentApp, callingMicroflow, additionMicroflow, + outputVariableName, + ("addition1", "1.2"), + ("addition2", "2.2")); +``` + +The calling microflow looks as follows: + +{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/main-microflow.png" >}} + +## Java Actions + +Outside of this calculation examples, you might want to create a microflow activity that calls a Java action file. See below for how to add an activity, and action and a call to the microflow to achieve that. Same as in the previous examples, you have to do this inside a transaction (`IModel.StartTransaction`). + +First, create an `IActionActivity`, just like the calculation example above, but then, its `Action` property will have the type `IJavaActionCallAction` instead of `IMicroflowCallAction`. This `IJavaActionCallAction` will need to know which `IJavaAction` is linked to. You can achieve this by setting the property `JavaAction` on the `IJavaActionCallAction` object to the `IQualifiedName` of the `IJavaAction`. If you are creating a brand new `IJavaAction`, it is important to add it to the module before accessing its `IQualifiedName`. If you have `IJavaAction` already, and you want to set up a call for that one, find it in the app and pass along its `IQualifiedName`. See below for an example. + +```csharp +public void CreateMicroflowAndJavaAction(IModule module, IModel currentApp) +{ + using var transaction = currentApp.StartTransaction("Create microflows"); + + var microflow = microflowService.CreateMicroflow(currentApp, module, "Microflow"); + + var javaActionActivity = currentApp.Create(); + var javaCallAction = currentApp.Create(); + var javaAction = currentApp.Create(); + javaAction.Name = "java_action"; + + // must add Java action file to module before using its qualified name + module.AddDocument(javaAction); + + javaCallAction.JavaAction = javaAction.QualifiedName; + javaActionActivity.Action = javaCallAction; + + microflowService.TryInsertAfterStart(microflow, javaActionActivity); + + transaction.Commit(); +} +``` + +If you already have a Java action file that you previously created, simply pass its `IQualifiedName` to the Java action. You will need to query the model in order to retrieve the actual object. You can do so as follows: + +```csharp +IQualifiedName FindJavaAction(string name, IModule module) +{ + var javaAction = module.GetDocuments().OfType().Single(ja => ja.Name == name); + return javaAction.QualifiedName; +} +``` + +Download the [whole code](https://github.com/mendix/ExtensionAPI-Samples) to see the way it works in its entirety. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/create-modal-web-view.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/create-modal-web-view.md new file mode 100644 index 00000000000..15abcb9f205 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/create-modal-web-view.md @@ -0,0 +1,171 @@ +--- +title: "Create a Web View Hosted Inside a Modal Dialog Using C#" +linktitle: "Modal Web View" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-11/create-modal-web-view/ +weight: 7 +--- + +## Introduction + +This how-to describes how you can create a new web view hosted inside a modal dialog. You will then open the modal from a new menu item. + +You can download the example in this how-to in [this GitHub repository](https://github.com/mendix/ExtensionAPI-Samples) + +## Adding a View Model for Your New Modal + +Add a view model for your new model. The view model takes care of the messages and the overall lifecycle of the modal web view. + +```csharp +namespace MyCompany.MyProject.MendixExtension; + +class MyModalWebViewViewModel( + string title, + IModel currentApp, + IDialogService dialogService, + IMessageBoxService messageBoxService, + Uri webServerBaseUrl) : WebViewModalDialogViewModel(title) +{ + public override void InitWebView(IWebView webView) + { + webView.MessageReceived += Browser_MessageReceived; + OnClosing = HandleOnClosed; + webView.Address = new Uri(webServerBaseUrl + "index"); + } + + void HandleOnClosed(CancelEventArgs cancelEventArgs) => messageBoxService.ShowInformation("Entity was created."); + + void Browser_MessageReceived(object? sender, MessageReceivedEventArgs e) + { + using var transaction = currentApp.StartTransaction("create entity from modal"); + + var entity = currentApp.Create(); + entity.Name = e.Message.Replace("\\", "").Replace("\"", ""); + currentApp.Root.GetModules().First(m => m.Name == "MyFirstModule").DomainModel.AddEntity(entity); + + transaction.Commit(); + + dialogService.CloseDialog(this); + } +} +``` + +{{% alert color="warning" %}} +{{% snippet file="/static/_includes/apidocs-mxsdk/warning-wwwroot.md" %}} +{{% /alert %}} + +## Adding a Controller Class + +You are creating a controller to delegate specific tasks, allowing you to keep the menu item free of unrelated business logic, for instance, conducting basic setup and displaying the web view. This approach is recommended, but not mandatory. + +{{% alert color="info" %}} +Specify both `Height` and `Width` properties; otherwise, they will default to 0,0. +{{% /alert %}} + +```csharp +namespace MyCompany.MyProject.MendixExtension; + +[method: ImportingConstructor] +[Export(typeof(MyModalWebViewController))] +class MyModalWebViewController(IDialogService dialogService, IMessageBoxService messageBoxService) +{ + public void ShowDialog(IModel currentApp, Uri webServerBaseUrl) + { + var modalViewModel = new MyModalWebViewViewModel("Modal WebView", currentApp, dialogService, messageBoxService, webServerBaseUrl) + { + Height = 160, Width = 400, + }; + + dialogService.ShowDialog(modalViewModel); + } +} +``` + +## Adding a Content Server + +To open static pages, you need to source them from either a file system path or via the `WebServerExtension` route. This document covers the latter, as this is the preferred way to provide static web content to an extension. + +```csharp +namespace MyCompany.MyProject.MendixExtension; + +[Export(typeof(WebServerExtension))] +class ContentServer : WebServerExtension +{ + private const string Content = """ + + + + + +
+

Entity Name

+ +

+
+ + +"""; + + public override void InitializeWebServer(IWebServer webServer) + { + webServer.AddRoute("index", async (_, response, _) => + { + response.ContentType = "text/html"; + response.StatusCode = 200; + var content = Encoding.ASCII.GetBytes(Content); + response.ContentLength64 = content.Length; + await response.OutputStream.WriteAsync(content, CancellationToken.None); + }); + } +} +``` + +## Adding a Menu Item That Opens the Modal Dialog + +Finally, you need to add a menu item to open the dialog. Replace the contents of `MyMenuExtension.cs` with the code below: + +```csharp +namespace MyCompany.MyProject.MendixExtension; + +[Export(typeof(MenuExtension))] +[method: ImportingConstructor] +class MyMenuExtension(MyModalWebViewController myModalWebViewController) : MenuExtension +{ + public override IEnumerable GetMenus() + { + yield return new MenuViewModel("Create Entity From Dialog", () => myModalWebViewController.ShowDialog(CurrentApp!, WebServerBaseUrl)); + } +} +``` + +These changes inject your new controller class into the `MyMenuExtension` class. Then you add a new menu item called `Create Entity From Dialog` and call the controller's `ShowDialog` method. + +Note that in this example, the parameter `currentApp` will be necessary, if the dialog needs to interact with the model. Additionally, `WebServerBaseUrl` is crucial, because, without the base path, navigating to the route you defined in the web server extension would not be possible. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/export-an-extension.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/export-an-extension.md new file mode 100644 index 00000000000..ef4f06ef863 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/export-an-extension.md @@ -0,0 +1,33 @@ +--- +title: "Export a C# Extension" +linktitle: "Export Extension" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-11/export-an-extension/ +weight: 99 +--- + +## Introduction + +This how-to describes how you can export an extension so that you can publish it in the Marketplace or directly share it with other Mendix developers. + +## Prerequisites + +To be able to export extension add-on modules, you need to have the feature flag `--enable-extension-development` enabled. For more information, see [Get Started with the Extensibility API](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/get-started/). + +## Procedure + +1. In Studio Pro, open the app that contains your development extension. +2. Create a new module with the same name as your extension folder. For example, if your extension folder is called 'MyFirstExtension', your module must also be called 'MyFirstExtension'. +3. In **App Explorer**, go to **Settings** of this module and then go to the **Export** tab. + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/extensibility-api-howtos/export-an-extension/export-tab.png" max-width=80% >}} + +4. Set **Module type** to *Add-on module*. +5. In the **Module version** field, enter the version number of the extension. +6. In the **Extension Name** field, select the name of your extension. This must match with your module name. + + {{% alert color="info" %}} If you do not set the **Extension name** field, your feature flag is not configured correctly. For more information, see [Get Started with the Extensibility API](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/get-started/). {{% /alert %}} + +7. Click **OK** to save the settings. +8. In **App Explorer**, right-click the module and click **Export** to export the extension. + +Now you can [publish the extension in the Marketplace](/appstore/submit-content/#adding) or directly share it with other Mendix developers. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/interact-with-model-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/interact-with-model-api.md new file mode 100644 index 00000000000..b0c2e240cc1 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/interact-with-model-api.md @@ -0,0 +1,61 @@ +--- +title: "Interact with the Model API Using C#" +linktitle: "Model Interaction" +url: /apidocs-mxsdk/apidocs/interact-with-model-api-11/ +weight: 11 +--- + +## Introduction + +After you create some basic extensions, you want to interact with the Studio Pro model in order to make changes to your app. The Model API allows you to do just that. The model representation is exposed via the `Mendix.StudioPro.ExtensionsAPI.Model` namespace. + +## Gaining Access to the Mendix Model SDK + +The easiest way to gain access to the model is by using the `CurrentApp` property of Extension class. All extension classes implement the [`Mendix.StudioPro.ExtensionsAPI.UI.UIExtensionBase`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.UI/UIExtensionBase.md) base class which provides this access. + +The `CurrentApp` property exposes an implementation of [`IModel`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Model/IModel.md). With a `IModel` reference you can gain access to any other model elements. However, any changes that you introduce must be contained within a model transaction. + +## Interacting with Model Elements + +Any modification to the model must be done within a transaction; otherwise, a `System.InvalidOperationException` is thrown. There can be only a single active (i.e. not committed or rolled back) `ITransaction` for the whole app. + +Transactions do not provide a way to isolate changes, but to group them. That is, any change to a model is immediately visible to all code working with the model. When transaction is rolled back programmatically or is undone by a user, all changes included in it are reverted. + +To create transaction you can call [`IModel.StartTransaction`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Model/IModel/StartTransaction.md). This method will return a transaction object which implements [`ITransaction`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Model/ITransaction.md). + +For your changes to reflect within the model, you must first commit the transaction by calling [`ITransaction.Commit`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Model/ITransaction/Commit.md). + +Alternatively, if you wish to abort or revert your changes, you can call [`ITransaction.Rollback`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Model/ITransaction/Rollback.md). + +## Examples + +The most common use case for changing an app is to change one or few properties synchronously. + +In the following example, an existing folder is renamed: + +```csharp +using (var transaction = model.StartTransaction("rename folder")) +{ + folder.Name = "New_Name"; + transaction.Commit(); +} +``` + +Here is another simple example. It adds an entity named `testEntity` and adds an attribute called `testAttribute` to it. + +```csharp +using (var transaction = model.StartTransaction("add entity")) +{ + var entity = CurrentApp.Create(); + var attr = CurrentApp.Create(); + entity.Name = "testEntity"; + attr.Name = "testAttribute"; + entity.AddAttribute(attr); + var copyEntity = CurrentApp.Copy(entity); + transaction.Commit(); +} +``` + +## Read More + +* [Understanding the Mendix Metamodel](/apidocs-mxsdk/mxsdk/mendix-metamodel/) diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/untyped-model-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/untyped-model-api.md new file mode 100644 index 00000000000..dec1f960e1b --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-howtos/untyped-model-api.md @@ -0,0 +1,111 @@ +--- +title: "Use the Untyped Model Access API Using C#" +linktitle: "Untyped Model API" +url: /apidocs-mxsdk/apidocs/untyped-model-access-api-11/ +weight: 24 +--- + +## Introduction + +The Untyped Model Access API caters to the advanced users who are familiar with the internals of the Mendix platform. With the Untyped Model API, you can access the rich data of model elements. + +When you need to use model elements, units, and property names in the Untyped Model API, you can reference the [Mendix Model SDK](https://apidocs.rnd.mendix.com/modelsdk/latest/index.html). Moreover, you can find the type names used by the Untyped Model Access API under the [structureTypeName](https://apidocs.rnd.mendix.com/modelsdk/latest/classes/Structure.html#structureTypeName) property of any model element. + +{{% alert color="info" %}} +All methods provided by the Untyped Model API are recursive to reduce the amount of API calls necessary to get to the content you are looking for. +{{% /alert %}} + +## Prerequisites + +To see the Untyped Model API in action, as described in the examples in this document, you must create a microflow having the default name `MyFirstLogic` with an action, and add an entity to the domain model. + +## Getting Started + +To start using the Untyped Model API, import it: + +```csharp +class Sample(IUntypedModelAccessService untypedModelAccessService) +{ +} +``` + +## Gaining Access to the Model Root + +To gain access to the model `Root`, request it from the newly added API service: + +```csharp +class Sample2(IUntypedModelAccessService untypedModelAccessService, IModel currentApp) +{ + public IModelRoot GetUntypedModelRoot() => untypedModelAccessService.GetUntypedModel(currentApp); +} + +``` + +## Requesting Top-level Model Elements + +To start going through the model elements, such as `Apps` and `Modules`, choose a starting point first: + +```csharp +class Sample3(IUntypedModelAccessService untypedModelAccessService, IModel currentApp) +{ + public IModelUnit GetProjectData() => + untypedModelAccessService.GetUntypedModel(currentApp) + .GetUnitsOfType("Projects$Project") + .Single(); + + public IModelUnit GetMyModuleData() => + untypedModelAccessService.GetUntypedModel(currentApp) + .GetUnitsOfType("Projects$Module") + .Single(unit => unit.Name == "MyFirstModule"); +} +``` + +## Accessing Child Elements + +It is possible to access the child elements of a model element, such as the actions of a microflow or entities of a domain model. + +Using either `GetElements` or `GetElementsOfType`, can help you, for example, analyze these elements' properties to implement custom validation rules. See an example below: + +```csharp +class Sample4(IUntypedModelAccessService untypedModelAccessService, IModel currentApp) +{ + public IReadOnlyList GetMicroflowActionActivities() => + untypedModelAccessService.GetUntypedModel(currentApp) + .GetUnitsOfType("Projects$Module") + .Single(unit => unit.Name == "MyFirstModule") + .GetUnitsOfType("Microflows$Microflow") + .Single(unit => unit.Name == "MyFirstLogic") + .GetElementsOfType("Microflows$ActionActivity"); + + public IReadOnlyList GetDomainModelEntities() => + untypedModelAccessService.GetUntypedModel(currentApp) + .GetUnitsOfType("Projects$Module") + .Single(unit => unit.Name == "MyFirstModule") + .GetUnitsOfType("DomainModels$DomainModel").Single() + .GetElementsOfType("DomainModels$Entity"); +} +``` + +## Getting Model Unit's Properties + +In case you would like to extract data out of a model element or unit, you need to access its properties. See an example below: + +```csharp +class Sample5(IUntypedModelAccessService untypedModelAccessService, IModel currentApp) +{ + private IReadOnlyList GetProjectRuntimeSettingsProperties() => + untypedModelAccessService.GetUntypedModel(currentApp) + .GetUnitsOfType("Settings$ProjectSettings") + .Single() + .GetElements() + .Single(element => element.Type == "Settings$RuntimeSettings") + .GetProperties(); + + + public string? GetAfterStartupMicroflowId() => + GetProjectRuntimeSettingsProperties() + .Single(property => property.Name == "AfterStartupMicroflowId") + .Value? + .ToString(); +} +``` diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-reference-guide/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-reference-guide/_index.md new file mode 100644 index 00000000000..94dd511ab8b --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-reference-guide/_index.md @@ -0,0 +1,16 @@ +--- +title: "Reference Guide for the C# Extensibility API" +linktitle: "Reference Guide" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-11/refguide/ +weight: 2 +no_list: false +description_list: true +--- + +## Introduction + +In the **Reference Guide**, you can explore a variety of topics related to the Extensibility API in detail. + +### Documentation in This Category + +The **Reference Guide** category contains the following documents: diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-reference-guide/extension-points.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-reference-guide/extension-points.md new file mode 100644 index 00000000000..59cdfd37d51 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-reference-guide/extension-points.md @@ -0,0 +1,40 @@ +--- +title: "Extensibility Extension Points in C#" +linktitle: "Extension Points" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-11/extension-points/ +weight: 9 +--- + +## Introduction + +Extension points allow you to hook functionality into various areas of the Studio Pro IDE. Extension point is a base class that you as an extension developer can inherit from. Your functionality will then be loaded by Studio Pro. These classes all have the `*Extension` suffix, inherit from [`ExtensionBase`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI/ExtensionBase.md) base class, and contain a few virtual or abstract members. + +Extension point is the only way you can add a custom behavior to Studio Pro. The rest of the APIs are there only to aid with implementing or expressing these behaviors. + +To be injected, your class must be decorated with [ExportAttribute](https://docs.microsoft.com/en-us/dotnet/api/system.composition.exportattribute?view=dotnet-plat-ext-6.0) +like in the example above. This attribute is part of [Managed Extensibility Framework](https://docs.microsoft.com/en-us/dotnet/framework/mef/) +that is employed by Studio Pro. + +## List of Available Extension Points + +### Studio Pro UI Extensions + +* [ContextMenuExtension](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.UI.Menu/ContextMenuExtension-1.md) – This allows injecting new context menu items into model elements. + +* [MenuExtension](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.UI.Menu/MenuExtension.md) – This allows injecting new menu items into Studio Pro menu bar. + +* [DockablePaneExtension](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.UI.DockablePane/DockablePaneExtension.md) – allows introducing new + [dockable pane](/refguide/studio-pro-overview/#panes) like Connector or Documentation. Panes integrate with Studio Pro + [layout system](/refguide/view-menu/#layout-of-panes) automatically. + + {{% alert color="info" %}}It is advised to introduce a new **View** menu item for each pane, so that Studio Pro users have a way to open it.{{% /alert %}} + +Additionally, there are additional features that provide access to the following: + +* [Studio Pro configuration](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI/ExtensionBase/Configuration.md) +* [The currently opened app](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.UI/UIExtensionBase/CurrentApp.md), as well as event subscription mechanism to that app +* Events can be subscribed to by using the subscribe and unsubscribe methods exposed in [UIExtensionBase](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.UI/UIExtensionBase.md). + +### Studio Pro and MxBuild Extensions + +* [ConsistencyCheckExtension](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.ConsistencyCheck/ConsistencyCheckExtension-1.md) – This allows injecting custom logic into the [Consistency check](/refguide/consistency-errors/) process. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-reference-guide/services.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-reference-guide/services.md new file mode 100644 index 00000000000..0d8c28b9b14 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-reference-guide/services.md @@ -0,0 +1,26 @@ +--- +title: "Studio Pro Extensibility Services in C#" +linktitle: "Studio Pro Services" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-11/services/ +weight: 10 +--- + +## Introduction + +A Studio Pro service is an interface that exposes some core Studio Pro functionality to extensions. These interfaces are named `I*Service` and can be found in `Mendix.StudioPro.ExtensionsAPI.Services` or `Mendix.StudioPro.ExtensionsAPI.UI.Services` namespaces. It can be injected using the Microsoft Extensions Framework, also referred to as MEF. For more information about MEF and how to use it please refer to the official [Microsoft documentation](https://learn.microsoft.com/en-us/dotnet/framework/mef/) + +{{% alert color="info" %}}You should not implement these interfaces in your production code, although it is possible to make sense to do so for unit testing purposes.{{% /alert %}} + +## List of Available Services + +* Helpers to operate on the app model or parts of it: + * [`IMicroflowService`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Services/IMicroflowService.md) + * [`IMicroflowExpressionService`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Services/IMicroflowExpressionService.md) +* Services giving access to interactive operations: + * [`IAppService`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.UI.Services/IAppService.md) + * [`ISelectorDialogService`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.UI.Services/ISelectorDialogService.md) + * [`IDockingWindowService`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.UI.Services/IDockingWindowService.md) +* Utility services: + * [`IConfigurationService`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Services/IConfigurationService.md) + * [`ILogService`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Services/ILogService.md) + * [`IExtensionFileService`](https://github.com/mendix/ExtensionAPI-Samples/blob/main/API%20Reference/Mendix.StudioPro.ExtensionsAPI.Services/IExtensionFileService.md) diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-reference-guide/web-view.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-reference-guide/web-view.md new file mode 100644 index 00000000000..312d2a23e63 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/extensibility-api-reference-guide/web-view.md @@ -0,0 +1,46 @@ +--- +title: "Extensibility Web Views in C#" +linktitle: "Web Views" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-11/web-views/ +weight: 12 +--- + +## Introduction + +In all places where the Studio Pro Extensibility API allows you to add custom UI, you can use web technology to implement the UI. + +Studio Pro contains a built-in web view that you can leverage to show your web-based UI. +Studio Pro also contains a built-in web server that can be used to serve the web UI, as well as to serve data to the web UI. + +In addition, there is a two-way message passing mechanism for direct communication between the web content and the C# part of your extension. + +## Showing a Web View in the UI + +There are a number of places where the Studio Pro Extensibility API allows you to add custom UI. + +Typically, the Extensibility API requires you to return a view model for your UI, and for every view model type, there is a corresponding base class for showing the UI in a web view. + +The following table shows the APIs that allow you to add custom UI, and the corresponding view model base class: + +| UI element | API for adding UI | Base class for view model | +|--------------------------------|------------------------------------------|--------------------------------| +| Tab (in working area) | `IDockingWindowManager.OpenTab(...)` | `WebViewTabViewModel` | +| Document tab (in working area) | `AppExplorerExtension.EditDocument(...)` | `WebViewDocumentTabViewModel` | +| Dockable pane | `DockablePaneExtension.Open(...)` | `WebViewDockablePaneViewModel` | +| Modal dialog | `IDialogService.ShowDialog(...)` | `WebViewModalDialogViewModel` | + +The view model base classes are `abstract`, so you are required to create your own view model class that derives from the base class. + +Each view model class has a method called `InitWebView` that you must override to initialize the web view. + +In this method, you can tell the web view to navigate to the (local) URL that contains your web content. + +In addition, the view model class can be used to house the logic for communicating with the web view. + +## Serving Content to the Web View + +For serving content to the web view and communicating both ways with it, see [Build a Todo Example Extension](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/build-todo-example-extension/). + +{{% alert color="warning" %}} +{{% snippet file="/static/_includes/apidocs-mxsdk/warning-wwwroot.md" %}} +{{% /alert %}} diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/get-started.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/get-started.md new file mode 100644 index 00000000000..0082aea4e6b --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/csharp/get-started.md @@ -0,0 +1,69 @@ +--- +title: "Get Started with the C# Extensibility API" +linktitle: "Get Started" +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-11/get-started/ +weight: 2 +--- + +## Introduction + +This document helps you set up a basic development environment for building extensions. Additionally, you can find links here to additional tutorials that help familiarize you to the extensibility API topics. + +## Development Setup + +The recommended development environment is [Visual Studio 2022](https://visualstudio.microsoft.com/) running on Windows. This documentation is centered on this setup. (You can also use other IDEs and other .NET compatible programming languages like [F#](https://fsharp.org/)). + +Install the latest Studio Pro version from the Mendix [Marketplace](https://marketplace.mendix.com/link/studiopro/). + +{{% alert color="info" %}} +Make sure to keep this Studio Pro installation up-to-date to benefit from new features and fixes. +{{% /alert %}} + +{{% alert color="info" %}} +Extensions can be built on any operating system as the underlying framework is cross-platform. +{{% /alert %}} + +## Using Extensibility API via a Hosted NuGet Package + +You can start extension development by simply including the `Mendix.StudioPro.ExtensionsAPI` NuGet package to your project by searching for *Mendix.Studio.ExtensionsAPI* in NuGet. + +{{% alert color="warning" %}}The initial released package version compatible with Mendix 10.12.0 was 10.12.38909. This has been updated to 10.12.0+38909 to match the Studio Pro version. +{{% /alert %}} + +Depending on your local environment setup, you possibly need to manually add a NuGet package to your solution. You can add a package source to Visual Studio via the menu: **Tools** > **Options** > **NuGet Package Manager** > **Package Sources** + +## Importing Extensibility API via a NuGet Package Hosted on a Local Repository + +Another option to start extension development is to import a locally hosted `Mendix.StudioPro.ExtensionsAPI` NuGet package into your project. + +To create a local NuGet repository that will let you host and use NuGet packages shared with you, follow the procedure below: + +1. Go to **Tools** > **Options** > **NuGet Package Manager** > **Package Sources**. +2. Press the green plus sign and specify a local folder, instead of a network location. +3. Drop the package into the local folder. +4. Refresh the NuGet manager to see the package alongside the other packages. + +{{% alert color="info" %}} +To make the search process easier, you can specifically select a certain package source inside the NuGet manager window. +{{% /alert %}} + +{{% alert color="info" %}} +For more information on local NuGet repositories, see the official [Local Feeds](https://learn.microsoft.com/en-us/nuget/hosting-packages/local-feeds) in *Microsoft Documentation*. +{{% /alert %}} + +## Extensions Development Setup + +Extensions load from within your mendix application and will only be loaded while the app is open. + +If you specify the `--enable-extension-development` flag you can debug your extension by placing it into a new subfolder within your app directory. The format of the folder must be `\extensions\` where all extensions that you want to load must exist inside the <`Mendix app folder>\extensions` subfolder. + +Studio Pro will load your extension, only if it complies with the following rules: + +* Your extension needs to provide a manifest.json file. +* This manifest file must contain a list of entry points for your extension. For example: + + ``` + { + "mx_extensions": [ "MyExtension.dll" ] + } + ``` diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/packaging-your-extension.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/packaging-your-extension.md new file mode 100644 index 00000000000..af9a8bd2a5f --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/packaging-your-extension.md @@ -0,0 +1,29 @@ +--- +title: "Packaging Your Extension" +url: /apidocs-mxsdk/apidocs/extensibility-api-11/packaging-your-extension +weight: 30 +--- + +# Packaging your extension + +Once you have finished development on your extension, you might want to package it into an add-on module so that others can start using it. Once you have created the add-on module, it can then be published to the Mendix Marketplace for your extension users to download into their Studio Pro app. + +To package your extension, you will still need the `--enable-extension-development` command line option turned on. Create a new module in your Studio Pro app containing your dev extension, give it an appropriate name. Open the module's settings form and set it to be an Add-on module. In the `Extension name` dropdown, select the extension you want to package into it. + +![Extension Add-on Module](/attachments/apidocs-mxsdk/apidocs/extensibility-api-11/extensionAddOnModule.png) + +After you've created your add-on module with its extension, you can now export it, by right-clicking the module in the App Explorer and choosing `Export add-on module package`, as shown below. + +![Export Module](/attachments/apidocs-mxsdk/apidocs/extensibility-api-11/exportAddOnModule.png) + +You can now save the add-on module to a location of your choice. + +# Importing the extension add-on module + +Once the add-on module is available to a Studio Pro user, they are now able to add it in their application. They can so so by right-clicking the app in the App Explorer and choosing `Import module package`, as shown below. + +![Import Module](/attachments/apidocs-mxsdk/apidocs/extensibility-api-11/importAddOnModule.png) + +Once an add-on module containing an extension is imported in the app, Studio Pro will show a warning to the user, asking to trust the extension contained in it. If the user does not choose to trust, the module will still be imported but the extension inside it won't be loaded. + +![Trust Extension](/attachments/apidocs-mxsdk/apidocs/extensibility-api-11/trustExtension.png) \ No newline at end of file diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/_index.md new file mode 100644 index 00000000000..b95391e760f --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/_index.md @@ -0,0 +1,37 @@ +--- +title: "Extensibility API for Web Developers" +linktitle: "Web Extensibility API" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-11/ +description: "The web extensibility API allows your custom Studio Pro extensions developed using JavaScript to interact with some internal services of Studio Pro." +weight: 20 +--- + +{{% alert color="warning" %}} This feature is in beta. For more information, see [Beta Releases](/releasenotes/beta-features/). {{% /alert %}} + +{{% alert color="info" %}}For information on new releases of the Extensibility API, see [Extensibility: Web API Release Notes](/releasenotes/studio-pro/web-extensibility-api/). +{{% /alert %}} + +## Introduction + +Extensions can be written in Typescript or other web languages, described here, or using a C# API which is documented separately in [Extensibility API for C# Developers](/apidocs-mxsdk/apidocs/csharp-extensibility-api-11/). + +## Prerequisites + +* You need at least a basic understanding of the Mendix platform. +* You need some understanding of the Mendix Model. +* You need to have some TypeScript development experience. + +## Getting Started + +For detailed explanation on how to get started with extensions, check out [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/getting-started/). + +## How-tos + +Here is a list of how-tos for you to begin with: + +* [How to Create a Dockable Pane Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/dockable-pane-api/) +* [How to Interact With Local App Files Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/local-app-files-api/) +* [How to Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/menu-api/) +* [How to Show a Message Box Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/messagebox-api/) +* [How to Access a Mendix Model Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/model-api/) +* [How to Open a Tab Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/tab-api/) diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/get-started.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/get-started.md new file mode 100644 index 00000000000..d814edfaa32 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/get-started.md @@ -0,0 +1,124 @@ +--- +title: "Get Started with the Web Extensibility API" +linktitle: "Get Started" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-11/getting-started/ +weight: 2 +--- + +## Introduction + +Studio Pro extensions can be developed using typescript and use standard web development technologies to extend the Studio Pro development environment. This guide shows you how to set up a basic development environment for building an extension using the web extensibility API. + +### Prerequisites + +You will need the following prerequisites: + +* Mendix Studio Pro version 10.21.0 or higher [Mendix Studio Pro](https://marketplace.mendix.com/link/studiopro). +* Install the latest Studio Pro version from the Mendix [Marketplace](https://marketplace.mendix.com/link/studiopro/). +* A development IDE to develop your extensions. We recommend using [Visual Studio Code](https://code.visualstudio.com/). +* Install the latest version 22.x.x of Node: https://nodejs.org/en/download. + +{{% alert color="info" %}} +Extensions can be built on any operating system as the underlying framework is cross-platform. +{{% /alert %}} + +## Creating Your First Extension + +This section will show you how to build and test an extension. + +### Create a Test App + +1. Create a new app using the **Blank Web App** template. +1. Install the [Studio Pro Web Extension Template](https://github.com/mendix/web-extension-template) from GitHub using the instructions in the repository. + +### Building the Extension + +From within Visual Studio Code: + +1. Select **File** -> **Open Folder** +1. Navigate to the folder you just extracted your extension source code to. +1. Click **Select Folder**. +1. Select **Yes** if you are asked whether you trust this folder. +1. Now open a Terminal by selecting **Terminal** -> **New Terminal** from the top menu. +1. From the Terminal type `npm install`. This installs all dependencies for the extension +1. Build your extension using the command `npm run build` in the terminal. + +Once completed you should now have a build artifact which we can deploy to your Mendix app. + +You can explore the extension a bit more to understand what it will do when it is installed. Do the following: + +1. From the Explorer window navigate to `src/main/index.ts` select it to open the file. + + Reading through the source code you should see the following: + +1. Line 7 adds a menu + + ```typescript + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "MyExtension Menu", + subMenus: [{ menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }], + }); + ``` + +1. Line 14 opens a tab + + ```typescript + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener("menuItemActivated", (args) => { + if (args.menuId === "myextension.ShowTabMenuItem") { + studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + } + }); + ``` + +When you install the extension you will see a new menu item within Studio Pro. + +### Testing the Extension + +To test the extension, do the following in File Explorer. + +1. Navigate to the folder where you extracted the extension source code. +1. Open the `dist` folder. +1. Copy the `myextension` folder. +1. Navigate to the folder where you created your app. +1. Create a new folder called `webextensions`. +1. Paste the `myextension` folder into the `webextensions` folder you just created. + + The extension files have now been added to the app. + +1. Start Studio Pro with the following command line parameters to tell it to use the extensions in the folder. + + `--enable-extension-development --enable-webview-debugging` + + These flags instruct Studio Pro to do the following: + + * Load extensions from the webextensions folder + * Enable web debugging tools which will be useful when developing your extension + +1. In Studio Pro, open the new app. + + You will see a new `Extensions` item in the top menu. + +## Conclusion + +Using this guide we have: + +* Created a new app +* Downloaded a new extension from GitHub +* Built the extension and installed it in our app +* Tested our extension from within Studio Pro. + +## Extensibility Feedback + +If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) + +Any feedback is much appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/_index.md new file mode 100644 index 00000000000..33fd0790a45 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/_index.md @@ -0,0 +1,12 @@ +--- +title: "Web Extensibility API How-tos" +linktitle: "How-tos" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-11/how-tos/ +weight: 3 +no_list: false +description_list: true +--- + +## Introduction + +The following how-tos teach you how to use the Extensibility API for Web Developers in different use cases. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/dockable-pane-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/dockable-pane-api.md new file mode 100644 index 00000000000..19d704867e2 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/dockable-pane-api.md @@ -0,0 +1,349 @@ +--- +title: "Create a Dockable Pane Using Web API" +linktitle: "Dockable Pane" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-11/dockable-pane-api/ +weight: 10 +--- + +## Introduction + +This guide explains how to create and manage a dockable pane using the web extensions API. A Dockable pane allows you to create a web view that can be docked and moved within the Studio Pro user interface. Examples of dockable panes in Studio Pro are: + +* Marketplace +* Errors +* Stories +* Toolbox + +## Prerequisites + +This guide uses the app created in [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/getting-started/). Please complete that how-to before starting this one. + +## Creating a Dockable Pane + +To open a dockable pane you must first register the dockable pane handle with the API. To do this, add a call to register the pane to the extension loaded method in the `src/main/index.ts`. + +```typescript + const paneHandle = await studioPro.ui.panes.register( + { + title: "My Extension Pane", + initialPosition: "right", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "dockablepane", + }); +``` + +Use the 'paneHandle' you registered to interact with the dockable pane. + +After adding this call the `loaded()` method looks like this: + +```typescript {hl_lines=["11-19"]} + async loaded() { + // Add a menu item to the Extensions menu + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "MyExtension Menu", + subMenus: [ + { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, + ], + }); + + const paneHandle = await studioPro.ui.panes.register( + { + title: "My Extension Pane", + initialPosition: "right", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "dockablepane", + }); + + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + (args) => { + if (args.menuId === "myextension.ShowTabMenuItem") { + studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + } + } + ); + } +``` + +## Adding a Menu To Open the Dockable Pane + +You will now add a menu that will open the pane when it is selected. + +1. Add a new submenu to the existing `extensionsMenu.add()` method on line 10. + + ```typescript {linenos=table linenostart=10} + // Add a menu item to the Extensions menu + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "MyExtension Menu", + subMenus: [ + { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, + { menuId: "myextension.ShowDockMenuItem", caption: "Show dock pane" }, + ], + }); + ``` + +1. Add lines to the `addEventListener()` call to handle opening the dockable pane once the menu has been selected, as follows: + + ```typescript + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + (args) => { + if (args.menuId === "myextension.ShowTabMenuItem") { + studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + } + else if (args.menuId === "myextension.ShowDockMenuItem") { + studioPro.ui.panes.open(paneHandle); + } + } + ); + ``` + +Your `loaded()` method should now look like this: + +```typescript {hl_lines=["3-10","22-41"]} + async loaded() { + // Add a menu item to the Extensions menu + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "MyExtension Menu", + subMenus: [ + { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, + { menuId: "myextension.ShowDockMenuItem", caption: "Show dock pane" }, + ], + }); + + const paneHandle = await studioPro.ui.panes.register( + { + title: "My Extension Pane", + initialPosition: "right", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "dockablepane", + }); + + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + (args) => { + if (args.menuId === "myextension.ShowTabMenuItem") { + studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + } + else if (args.menuId === "myextension.ShowDockMenuItem") { + studioPro.ui.panes.open(paneHandle); + } + } + ); + } +``` + +## Specifying a Web View Endpoint + +### Adding New Endpoint Handlers + +You must now create a new web view endpoint where the user interface to be rendered within the pane is defined. You can use the existing endpoint and rename it to something more appropriate. + +1. Rename `ui/index.tsx` to `ui/tab.tsx` +1. Add the new endpoint file, `ui/dockablepane.tsx` by copying `ui/tab.tsx`. + +You must also alter the `vite.config.ts` and `manifest.json` files to bind to the correct endpoint, as described in the following sections: + +### Altering `vite.config.js` + +Replace the entry section of `vite.config.js` with the following: + +```typescript + entry: { + main: "src/main/index.ts", + tab: "src/ui/tab.tsx", + dockablepane: "src/ui/dockablepane.tsx", + } +``` + +This instructs vite that the tab endpoint is connected to `src/ui/tab.tsx` and the dockable pane endpoint is connected to `src/ui/dockablepane.tsx`. + +`vite.config.js` should now look like this: + +```typescript {hl_lines=["7-11"]} +import { defineConfig, ResolvedConfig, UserConfig } from "vite"; + +export default defineConfig({ + build: { + lib: { + formats: ["es"], + entry: { + main: "src/main/index.ts", + tab: "src/ui/tab.tsx", + dockablepane: "src/ui/dockablepane.tsx", + }, + }, + rollupOptions: { + external: ["@mendix/component-framework", "@mendix/model-access-sdk"], + }, + outDir: "./dist/myextension", + }, +} satisfies UserConfig); +``` + +### Altering `public/manifest.json` + +You also need to instruct Studio Pro to load the endpoint that you just created. To do this, modify the manifest file `public/manifest.json`. + +Alter the "ui" section by changing the `tab` endpoint and adding the `dockablepane` endpoint. + +```typescript + "ui": { + "tab": "tab.js", + "dockablepane": "dockablepane.js" + } +``` + +The `manifest.json file` should now look like this: + +```typescript {hl_lines=["5-8"]} +{ + "mendixComponent": { + "entryPoints": { + "main": "main.js", + "ui": { + "tab": "tab.js", + "dockablepane": "dockablepane.js" + } + } + } +} +``` + +## Closing the Dockable Pane + +Now that you have registered a pane and can open, it would also be a good idea to close it. + +You will close your pane using a new menu item. + +First add a new sub menu item to the menu on line 11. + +```typescript {linenos=table linenostart=11} + { menuId: "myextension.HideDockMenuItem", caption: "Hide dock pane" }, +``` + +You must also alter the event handler for the new menu at the end of the loaded method: + +```typescript + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + (args) => { + if (args.menuId === "myextension.ShowTabMenuItem") { + studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + } + else if (args.menuId === "myextension.ShowDockMenuItem") { + studioPro.ui.panes.open(paneHandle); + } + else if (args.menuId === "myextension.HideDockMenuItem") { + studioPro.ui.panes.close(paneHandle); + } + } + ); +``` + +The loaded method should now look like this: + +```typescript {hl_lines=["9","24-45"]} + async loaded() { + // Add a menu item to the Extensions menu + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "MyExtension Menu", + subMenus: [ + { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, + { menuId: "myextension.ShowDockMenuItem", caption: "Show dock pane" }, + { menuId: "myextension.HideDockMenuItem", caption: "Hide dock pane" }, + ], + }); + + const paneHandle = await studioPro.ui.panes.register( + { + title: "My Extension Pane", + initialPosition: "right", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "dockablepane", + }); + + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + (args) => { + if (args.menuId === "myextension.ShowTabMenuItem") { + studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + } + else if (args.menuId === "myextension.ShowDockMenuItem") { + studioPro.ui.panes.open(paneHandle); + } + else if (args.menuId === "myextension.HideDockMenuItem") { + studioPro.ui.panes.close(paneHandle); + } + } + ); + } +``` + +## Conclusion + +You now have a new dockable pane with its own user interface which you can modify as you like. +You can also open and close the dockable pane from a menu. + +## Extensibility Feedback + +If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) + +Any feedback is much appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/local-app-files-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/local-app-files-api.md new file mode 100644 index 00000000000..bb03c444cd7 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/local-app-files-api.md @@ -0,0 +1,138 @@ +--- +title: "Interact With Local App Files Using Web API" +linktitle: "Local Files" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-11/local-app-files-api/ +weight: 20 +--- + +## Introduction + +This how-to shows you how to interact with local application files from within an extension. It adds three new buttons to the tab and three new event handlers for saving, loading, and deleting a file called `HelloWorld.txt`. + +## Prerequisites + +This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/getting-started/). Please complete that how-to before starting this one. + +## Adding Some Interactivity + +First, you will add all the code. The changes will then be explained so that you can understand what the code does. + +1. Open `src/ui/index.tsx`. +1. Replace the contents of the file with the following code: + +```typescript +import { studioPro } from "@mendix/extensions-api"; +import { StrictMode, useCallback } from "react"; +import { createRoot } from "react-dom/client"; + +const saveFile = async () => { + await studioPro.app.files.putFile( + "HelloWorld.txt", + "Hello world from a file!" + ); + studioPro.ui.messageBoxes.show("info", "Saving HelloWorld.txt"); +}; + +const loadFile = async () => { + const message = await studioPro.app.files.getFile("HelloWorld.txt"); + studioPro.ui.messageBoxes.show( + "info", + `Loaded HelloWorld.txt it contained: ${message}` + ); +}; + +const deleteFile = async () => { + await studioPro.app.files.deleteFile("HelloWorld.txt"); + studioPro.ui.messageBoxes.show("info", "Deleted HelloWorld.txt"); +}; + +createRoot(document.getElementById("root")!).render( + +

Mendix Studio Pro Extension

+

Hello from an extension!

+

+ + + +

+
+); +``` + +## What Does the Code Do? + +The following sections explain the various parts of the code. + +### saveFile + +The `saveFile` callback calls the `putFile` API setting the filename to `HelloWorld.txt` and the content to `Hello world from a file!`. + +```typescript +const saveFile = async () => { + await studioPro.app.files.putFile( + "HelloWorld.txt", + "Hello world from a file!" + ); + studioPro.ui.messageBoxes.show("info", "Saving HelloWorld.txt"); +}; +``` + +### loadFile + +The loadFile callback calls the getFile API requesting to load `HelloWorld.txt`. It then shows message box displaying the content of the file. + +```typescript +const loadFile = async () => { + const message = await studioPro.app.files.getFile("HelloWorld.txt"); + studioPro.ui.messageBoxes.show( + "info", + `Loaded HelloWorld.txt it contained: ${message}` + ); +}; +``` + +### deleteFile + +The deleteFile callback calls the deleteFile API requesting to delete `HelloWorld.txt` + +```typescript +const deleteFile = async () => { + await studioPro.app.files.deleteFile("HelloWorld.txt"); + studioPro.ui.messageBoxes.show("info", "Deleted HelloWorld.txt"); +}; +``` + +### Adding the Buttons + +The final part of the code adds three new buttons which, when clicked, call the callbacks described above. + +```typescript +createRoot(document.getElementById("root")!).render( + +

Mendix Studio Pro Extension

+

Hello from an extension!

+

+ + + +

+
+); +``` + +## Some Restrictions + +The App files API allows you to modify files within your application's folder. It will not: + +* serve restricted files such as the `.mpr` file or the contents of some folders such as the `.git` folder. +* allow access to files outside of the app folder. + +## Conclusion + +You can now create and extension which save, load, and delete files within the app folder. + +## Extensibility Feedback + +If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) + +Any feedback is much appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/menu-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/menu-api.md new file mode 100644 index 00000000000..8fb51618cec --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/menu-api.md @@ -0,0 +1,209 @@ +--- +title: "Create a Menu Using Web API" +linktitle: "Create Menu" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-11/menu-api/ +weight: 30 +--- + +## Introduction + +This how-to shows you how to create both a simple menu item and a menu item with subsidiary items beneath it using the web extension API. + +## Prerequisites + +This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/getting-started/). Please complete that how-to before starting this one. + +## Creating a Simple Menu + +In this section, you will add a simple menu to your extension. + +The code below will: + +* create a menu item under the menu that was added in [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/getting-started/) +* show a dialog when the menu is clicked + +Replace your `src/main/index.ts` file with the following: + +```typescript +import { IComponent, Menu, studioPro } from "@mendix/extensions-api"; + +const menuApi = studioPro.ui.extensionsMenu; + +const messageBoxApi = studioPro.ui.messageBoxes; +const menuId = "my-menu-unique-id"; +const caption = "My First Menu"; + +// Open a message box when the menu item is clicked +studioPro.ui.extensionsMenu.addEventListener("menuItemActivated", (args) => { + if (args.menuId === "my-menu-unique-id") { + messageBoxApi.show("info", `My menu '${args.menuId}' was clicked`); + } +}); +class Main implements IComponent { + async loaded() { + const menu: Menu = { + caption: caption, + menuId: menuId, + subMenus: [], + hasSeparatorBefore: false, + hasSeparatorAfter: true, + enabled: true, + }; + + await menuApi.add(menu); + } +} + +export const component: IComponent = new Main(); +``` + +The code imports the following: + +* `menuApi` from `studioPro.ui.extensionsMenu` to allow you to use the menu API +* `messageBoxApi` from `studioPro.ui.messageBoxes` to show a dialog. + +It starts listening to the `menuItemActivated` endpoint which will notify the extension when **My First Menu** is clicked. + +The `menuItemActivated` listener event handles the actual function of the menu. The arguments `args` contain the data returned by Studio Pro to the extension, notifying it which menu item was activated (clicked). This is passed as the `menuId` that was used when creating the menu item and the `menuId` in the `if` statement is used to identify this. In this example it is `"my-menu-unique-id"` and the handler calls the `messageBoxApi` to show an information dialog. + +Your extensions should now appear like this: + +{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/menus/my_first_menu.png" >}} + +## Menu Properties + +The menu has the following properties: + +* `caption` – the text of the menu item +* `menuId` – a unique id for the menu item +* `subMenus` – a list of menus subsidiary to this menu item +* `hasSeparatorBefore` (default: `false`) – shows a visual separator before this menu item +* `hasSeparatorAfter` (default: `false`) – shows a visual separator after this menu item +* `enabled` (default: `true`) – indicates that this menu item notifies the listener when clicked + +{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/menus/grouped_menus.png" >}} + + +## Creating a Menu with Submenus + +You can also have a number of submenus that branch out your menu. + +To do so, add additional menu items to your code and add these to the `subMenus` array for the relevant menu item. These child menus can in turn have their own submenus, and so on. Only parent menus (menus that are not sub menus to any others) should be added through the `await menuApi.add()` call, as shown in the code sample below. + +{{% alert color="info" %}} +Parent menus (with `subMenus`) do not create `menuItemActivated` events. These only get sent when a leaf menu (a menu that does not have any submenus) is clicked. +{{% /alert %}} + +The following `src/main/index.ts` generates one menu item with sub menus and one menu item without sub menus. + +```typescript +import { IComponent, Menu, studioPro } from "@mendix/extensions-api"; + +const menuApi = studioPro.ui.extensionsMenu; +const messageBoxApi = studioPro.ui.messageBoxes; + +// Open a message box when the menu item is clicked +studioPro.ui.extensionsMenu.addEventListener("menuItemActivated", (args) => { + messageBoxApi.show("info", `Child menu '${args.menuId}' was clicked`); +}); +class Main implements IComponent { + async loaded() { + const grandChild: Menu = { + caption: "Grandchild Menu", + menuId: "grandChild", + }; + + const childMenu1: Menu = { + caption: "Child Menu 1", + menuId: "child_1", + subMenus: [grandChild], + }; + + const childMenu2: Menu = { + caption: "Child Menu 2", + menuId: "child_2", + }; + + const menu1: Menu = { + caption: "Menu 1", + menuId: "menu1", + subMenus: [childMenu1, childMenu2], + }; + + const menu2: Menu = { + caption: "Menu 2", + menuId: "menu2", + subMenus: [], + }; + + await menuApi.add(menu1); + await menuApi.add(menu2); + } +} + +export const component: IComponent = new Main(); +``` + +The menu hierarchy will then be displayed like this: + +{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/menus/child_menus.png" >}} + +## Updating a menu + +Sometimes you might want to disable a menu or update its caption depending on a condition. You can do so by calling the menu API's `updateMenu` method. + +An example is shown in the code below. If you click on the menu item, it will be disabled and its caption will be updated. + +{{% alert color="info" %}} +Only `caption` and `enabled` can be updated. +{{% /alert %}} + +You can test it by the following code as the contents of `src/main/index.ts`. + +```typescript +import { IComponent, Menu, studioPro } from "@mendix/extensions-api"; + +const menuApi = studioPro.ui.extensionsMenu; + +const menuId = "my-menu-unique-id"; +const caption = "My First Menu"; + +menuApi.addEventListener("menuItemActivated", (args) => { + if (args.menuId !== menuId) return; + menuApi.update(menuId, { + caption: `${caption} (Disabled)`, + enabled: false, + }); +}); +class Main implements IComponent { + async loaded() { + const menu: Menu = { + caption: caption, + menuId: menuId, + subMenus: [], + hasSeparatorBefore: false, + hasSeparatorAfter: true, + enabled: true, + }; + + await menuApi.add(menu); + } +} + +export const component: IComponent = new Main(); +``` + +The disabled state is shown in the image below. + +{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/menus/disabled_menu.png" >}} + +## Conclusion + +You have seen how to create simple menu items and menu items with sub menus. +You can also dynamically change the enabled status and caption of a menu item. + +## Extensibility Feedback + +If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) + +Any feedback is much appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/messagebox-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/messagebox-api.md new file mode 100644 index 00000000000..16585622234 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/messagebox-api.md @@ -0,0 +1,100 @@ +--- +title: "Show a Message Box Using Web API" +linktitle: "Message Box" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-11/messagebox-api/ +weight: 50 +--- + +## Introduction + +This how-to shows you how to show a message box to the Studio Pro user. + +## Prerequisites + +This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/getting-started/). Please complete that how-to before starting this one. + +## Showing a Message Box + +Firstly, you need to create a menu which will display a dialog with text. This is done in the `loaded` event in `Main`. + +You can learn how to do that in [Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/menu-api/). + +In this example you create three menu items: + +* Show Info +* Show Error +* Show Warning + +You then need to add event listeners to receive and act upon the notifications generated by the menu items. Here the listener events show a different message box, depending on which menu item is selected. The message has the following format: + +`messageBoxApi.show(, , )` + +where + +* `` is the type of message, indicated in the pane title and indicated by an icon. Values are "information" {{% icon name="info-circle" color="blue" %}}, "warning" {{% icon name="alert-triangle" color="yellow" %}}, and "error" {{% icon name="remove-circle" color="red" %}}. +* `` is the message to display. +* `` is an optional extended message which is displayed in an expandable area which is initially collapsed. + +The full typescript file to implement these three menu items and message boxes is as follows. + +```typescript +import { IComponent, Menu, studioPro } from "@mendix/extensions-api"; +const messageBoxApi = studioPro.ui.messageBoxes; +const menuApi = studioPro.ui.extensionsMenu; + +const show_info_menu_id = "show-info-id"; +const show_error_menu_id = "show-error-id"; +const show_warning_menu_id = "show-warning-id"; + +menuApi.addEventListener("menuItemActivated", (args) => { + if (args.menuId === show_info_menu_id) + messageBoxApi.show("info", "This is information.", "Extra info"); + if (args.menuId === show_error_menu_id) + messageBoxApi.show("error", "This is an error.", "Extra error details"); + if (args.menuId === show_warning_menu_id) + messageBoxApi.show( + "warning", + "This is a warning.", + "Extra warning details" + ); +}); + +class Main implements IComponent { + async loaded() { + const infoMenu: Menu = { + caption: "Show Info", + menuId: show_info_menu_id, + }; + + const errorMenu: Menu = { + caption: "Show Error", + menuId: show_error_menu_id, + }; + + const warningMenu: Menu = { + caption: "Show Warning", + menuId: show_warning_menu_id, + }; + + await menuApi.add(infoMenu); + await menuApi.add(errorMenu); + await menuApi.add(warningMenu); + } +} + +export const component: IComponent = new Main(); +``` + +For example, the **Show Info** menu item will display the following message box. + +{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/messageBoxes/info.png" >}} + +## Conclusion + +You have seen how to implement message boxes triggered by menu items. + +## Extensibility Feedback + +If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) + +Any feedback is much appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/model-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/model-api.md new file mode 100644 index 00000000000..14c7003a1cd --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/model-api.md @@ -0,0 +1,108 @@ +--- +title: "Access a Mendix Model Using Web API" +linktitle: "Mendix Model" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-11/model-api/ +weight: 40 +--- + +## Introduction + +The Model Access API allow access to the Mendix model. This how-to provides guidance on using the Model Access API. It is split into the following sections: + +* [Using the Model Access API](#using-api) +* [Reading the Units Info and Loading Units](#units-info-load) +* [Reading the Unit Content](#read) +* [Modifying the Unit Content](#modify) + +## Using the Model Access API {#using-api} + +The model is split in several components exposed via `studioPro.app.model` object. Currently supported components are: + +* buildingBlocks +* domainModels +* enumerations +* pages +* snippets + +You can include these components using syntax as shown below, which includes pages and domain models. + +```ts +const { pages, domainModels } = studioPro.app.model; +``` + +## Reading the Units Info and Loading Units {#units-info-load} + +A unit is a Mendix document (for example, a page or a domain model) containing elements. Each element is within a container element and may contain other elements. An element is part of a Mendix model and all elements together form the logic of the model. For more information see [Mendix Metamodel](/apidocs-mxsdk/mxsdk/mendix-metamodel/). + +Each component, for example pages (`studioPro.app.model.pages`) exposes the units it is responsible for. You can only access all the content of a unit once you have loaded the unit info for that unit. + +The unit info, described by the `UnitInfo` interface, contains the the following fields: + +| Name | Description | Example value | +| --- | --- | --- | +| `$ID` | The unique id of the unit | `077d1338-a548-49a9-baee-c291e93d19af` | +| `$Type` | The type of the unit | `Pages$Page` | +| `moduleName` | (Optional) The name of the module containing the unit | `MyFirstModule` | +| `name` | (Optional) The name of the unit | `ExamplePage` | + +For example, you can retrieve all the units managed by the `domainModels` component using the following code: + +```ts +const unitsInfo: Primitives.UnitInfo[] = await domainModels.getUnitsInfo() +``` + +A unit can be loaded by supplying a function, `fn` to `component.loadAll(fn)`. The function `fn` should return `true` to load a specified unit. + +{{% alert color="warning" %}} +Loading units is a resource intensive process. Only load units when you need them. +{{% /alert %}} + +For example, the following snippet loads the `domainModel` for the module named `MyFirstModule`: + +```ts +const [domainModel] = await domainModels.loadAll((info: Primitives.UnitInfo) => info.moduleName === 'MyFirstModule'); +``` + +And this example snippet loads the page named `Home_Web` in the module named `MyFirstModule`: + +```ts +const [page] = await pages.loadAll((info: Primitives.UnitInfo) => info.moduleName === 'MyFirstModule' && info.name === 'Home_Web') +``` + +## Reading the Unit Content {#read} + +Elements within units can be accessed using the `get` helper methods. + +For example, the following snippet will get the entity named `MyEntity` from the previously loaded `DomainModels` unit: + +```ts +const entity: DomainModels.Entity = domainModel.getEntity("MyEntity"); +``` + +## Modifying the Unit Content {#modify} + +You can modify a Mendix model by leveraging the `add` helper methods. + +{{% alert color="warning" %}} +Always invoke the `component.save(unit)` method after making changes to your unit. This method must be invoked for each modified unit, so changes to multiple units need to be saved separately. +{{% /alert %}} + +The following snippet creates a new entity inside the previously loaded `DomainModels` unit: + +```ts +const newEntity: DomainModels.Entity = await domainModel.addEntity({ name: "NewEntity", attributes: [{ name: "MyAttribute", type: "AutoNumber" }]}); + +newEntity.documentation = "New documentation"; + +await domainModels.save(domainModel); +``` + +## Conclusion + +You now know how to interact with units and elements in the Mendix model. + +## Extensibility Feedback + +If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) + +Any feedback is much appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/tab-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/tab-api.md new file mode 100644 index 00000000000..4b77a972c3f --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/extensibility-api/web/web-extensions-howtos/tab-api.md @@ -0,0 +1,233 @@ +--- +title: "Open a Tab Using Web API" +linktitle: "Open a Tab" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-11/tab-api/ +weight: 60 +--- + +## Introduction + +This how-to shows you how to open a tab in Studio Pro from an extension. This tab will contain your web content. + +## Prerequisites + +This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/getting-started/). Please complete that how-to before starting this one. You should also be familiar with creating menus as described in [Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/menu-api/). + +## Opening a Tab + +Firstly, create a menu item to open the tab. This is done inside the `loaded` event in `Main`. For more information see [Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/menu-api/). + +In a listener event called `menuItemActivated` the `studioPro.ui.tabs.open(, )` call opens a new tab where: + +* `` is an object containing the `title` of the tab, which will be shown in the title bar of your tab in Studio Pro. +* `` is an object containing two required properties: + + * `componentName` which is the name of the extension prefixed with "extension/". For example "extension/myextension" in the following example. + * `uiEntryPoint` which is the name mapped from the `manifest.json` file. See below for examples with multiple tabs. + +{{% alert color="info" %}} +Whenever the tabs API `open` method is called, the `TabHandle` returned must be tracked by the extension so that it can be closed later by calling the `close` method. +{{% /alert %}} + +An example of the class `Main` to open a tab called **My Extension Tab** looks similar to the following: + +```typescript +import { IComponent, studioPro, TabHandle } from "@mendix/extensions-api"; + +class Main implements IComponent { + tabs: { [menuId: string]: Promise } = {}; + async loaded() { + // Add menu items to the Extensions menu to open and close our tab + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "MyExtension Menu", + subMenus: [ + { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, + { + menuId: "myextension.CloseTabMenuItem", + caption: "Close tab", + }, + ], + }); + + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + async (args) => { + // Open a tab when the menu item is clicked + if (args.menuId === "myextension.ShowTabMenuItem") { + const handle = studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + + // Track the open tab + this.tabs["myextension.MainMenu"] = handle; + } + + // Close the tab opened previously + if (args.menuId === "myextension.CloseTabMenuItem") { + studioPro.ui.tabs.close(await this.tabs["myextension.MainMenu"]); + } + } + ); + } +} + +export const component: IComponent = new Main(); +``` + +{{% alert color="info" %}} + + In this example, there is a dictionary that uses the parent menu id as the key to track the open `TabHandle`. +{{% /alert %}} + +## Filling the Tabs With Content + +In the previous example, the `uiEntryPoint` property of the `` object had the value "tab". This value must match the one from the manifest. + +If you want to have multiple tabs in your extension, you need to structure the folders and set up the manifest file correctly. + +To do this, follow these steps: + +1. Add a new method `createTabSpec` in your `Main` class. + + ```typescript + createTabSpec(tab: string, title: string): { info: TabInfo, ui: UISpec} { + const info: TabInfo = { title }; + const ui: UISpec = { + componentName: "extension/myextension", + uiEntrypoint: tab, + }; + + return {info, ui}; + } + ``` + +1. Add three folders inside the `ui` folder, one for each tab you want to display contents for. +1. Create an `index.tsx` file in each folder. +1. Put the following code in each `index.tsx` file (this example is for **tab3**): + + ```typescript + import { StrictMode } from "react"; + import { createRoot } from "react-dom/client"; + + createRoot(document.getElementById("root")!).render( + +

tab3

+
+ ); + ``` + + In this example, we'll add 3 tabs: **tab1**, **tab2**, and **tab3**. + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/tabs/ui_folder_structure.png" >}} + +1. Create listener events in the `Main` class to open each of the three tabs. The `Main` class will then look like this: + + ```typescript + import { IComponent, studioPro, TabInfo, UISpec } from "@mendix/extensions-api"; + + class Main implements IComponent { + async loaded() { + // Add a menu item to the Extensions menu + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "Show Tabs", + subMenus: [ + { menuId: "myextension.ShowTab1", caption: "Show tab 1" }, + { menuId: "myextension.ShowTab2", caption: "Show tab 2" }, + { menuId: "myextension.ShowTab3", caption: "Show tab 3" }, + ], + }); + + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + async (args) => { + if (args.menuId === "myextension.ShowTab1") { + const tab1Spec = this.createTabSpec("tab1", "Tab 1 Title"); + studioPro.ui.tabs.open(tab1Spec.info, tab1Spec.ui); + } + if (args.menuId === "myextension.ShowTab2") { + const tab2Spec = this.createTabSpec("tab2", "Tab 2 Title"); + studioPro.ui.tabs.open(tab2Spec.info, tab2Spec.ui); + } + if (args.menuId === "myextension.ShowTab3") { + const tab3Spec = this.createTabSpec("tab3", "Tab 3 Title"); + studioPro.ui.tabs.open(tab3Spec.info, tab3Spec.ui); + } + } + ); + } + + createTabSpec(tab: string, title: string): { info: TabInfo; ui: UISpec } { + const info: TabInfo = { title }; + const ui: UISpec = { + componentName: "extension/myextension", + uiEntrypoint: tab, + }; + + return { info, ui }; + } + } + + export const component: IComponent = new Main(); + ``` + +1. Ensure the tabs are added to the `manifest.json` file. Here is an example of three tabs under the `ui` property. + + ```json + { + "mendixComponent": { + "entryPoints": { + "main": "main.js", + "ui": { + "tab1": "tab1.js", + "tab2": "tab2.js", + "tab3": "tab3.js" + } + } + } + } + ``` + +1. Update `vite.config` to match the manifest with an entry for each tab. For example: + + ```typescript + import { defineConfig, ResolvedConfig, UserConfig } from "vite"; + + export default defineConfig({ + build: { + lib: { + formats: ["es"], + entry: { + main: "src/main/index.ts", + tab1: "src/ui/tab1/index.tsx", + tab2: "src/ui/tab2/index.tsx", + tab3: "src/ui/tab3/index.tsx", + }, + }, + rollupOptions: { + external: ["@mendix/component-framework", "@mendix/model-access-sdk"], + }, + outDir: "./dist/myextension", + }, + } satisfies UserConfig); + ``` + +After building and installing the extension in our Studio Pro app, each tab will display the content specified in the related `index.tsx` file. + +## Conclusion + +You now know how to create tabs and populate them with content. + +## Extensibility Feedback + +If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) + +Any feedback is much appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/runtime-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/runtime-api.md new file mode 100644 index 00000000000..7698e5a0084 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/runtime-api.md @@ -0,0 +1,18 @@ +--- +title: "Mendix Runtime API" +url: /apidocs-mxsdk/apidocs/runtime-api-11/ +description: "The Mendix 11 Runtime API provides all the functionality and information from both the application model and Mendix Runtime." +weight: 75 +--- + +## Introduction + +Extend your application model using Java. All functionality and information from both the application model and Mendix Runtime is accessible via the Mendix 11 Runtime API. + +## Mendix Runtime API + +* [Mendix 11 Runtime API](https://apidocs.rnd.mendix.com/11/runtime/index.html) + +## Read More + +* [Using the Mendix Runtime Java API](/refguide/java-api-tutorial/) diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/_index.md new file mode 100644 index 00000000000..535dbe90d43 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/_index.md @@ -0,0 +1,11 @@ +--- +title: "APIs for Studio Pro 9" +url: /apidocs-mxsdk/apidocs/apis-for-studio-pro-9/ +no_list: false +description_list: true +description: "Provides the documentation of APIs for Studio Pro 9, including Extensibility API and Mendix Runtime API." +weight: 7 +linktitle: "Studio Pro 9" +--- + +Mendix offers the following APIs for Studio Pro 9: diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/_index.md new file mode 100644 index 00000000000..3021fc32d75 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/_index.md @@ -0,0 +1,25 @@ +--- +title: "Extensibility API" +url: /apidocs-mxsdk/apidocs/extensibility-api-9/ +description: "The Extensibility API allows you to extend Studio Pro 9 by adding custom functionality." +weight: 57 +no_list: false +description_list: true +cascade: + - beta: true +--- + +{{% alert color="warning" %}} This feature is in beta. For more information, see [Beta Releases](/releasenotes/beta-features/). {{% /alert %}} + +{{% alert color="info" %}} +For information on new releases of the Extensibility API see: + +* [Extensibility: C# API Release Notes](/releasenotes/studio-pro/csharp-extensibility-api/). +* [Extensibility: Web API Release Notes](/releasenotes/studio-pro/web-extensibility-api/). +{{% /alert %}} + +## Introduction + +Extensions are self-contained modules which users can add to Studio Pro. This means that with extensibility you can add new features and functionality to Studio Pro. The Extensibility API is an API that allows developers to interact with a curated list of internal systems of Studio Pro. This documentation provides guides and reference documentation for the Extensibility API. + +The API is provided in two flavors, depending which language you are developing in. C# and web based (via Typescript): diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/_index.md similarity index 79% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/_index.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/_index.md index 4c8e97113f1..939b59310c7 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/_index.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/_index.md @@ -1,7 +1,7 @@ --- title: "Extensibility API for C# Developers" linktitle: "C# Extensibility API" -url: /apidocs-mxsdk/apidocs/csharp-extensibility-api/ +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-9/ description: "The C# extensibility API allows your custom Studio Pro extensions developed in C# to interact with some internal services of Studio Pro." weight: 10 no_list: false @@ -20,7 +20,7 @@ For information on new releases of the Extensibility API, see [Extensibility: C# ## Introduction -Extensions can be written in C#, described here, or using a web API which is documented separately in [Extensibility API for Web Developers](/apidocs-mxsdk/apidocs/web-extensibility-api/). +Extensions can be written in C#, described here, or using a web API which is documented separately in [Extensibility API for Web Developers](/apidocs-mxsdk/apidocs/web-extensibility-api-9/). If you need to add your own custom UI to Studio Pro, you can achieve this using web technology. Your web-based UI will be rendered in Studio Pro using a hosted web view, the API provides communication functionality between your web UI and the C# extension logic. @@ -32,7 +32,7 @@ If you need to add your own custom UI to Studio Pro, you can achieve this using ## Getting Started -For detailed explanation on how to get started with extensions, check out [Get Started with the Extensibility API](/apidocs-mxsdk/apidocs/csharp-extensibility-api/get-started/). +For detailed explanation on how to get started with extensions, check out [Get Started with the Extensibility API](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/get-started/). You can also check out our examples and [API reference documentation](https://github.com/mendix/ExtensionAPI-Samples). @@ -40,11 +40,11 @@ You can also check out our examples and [API reference documentation](https://gi Here is a list of how-tos for you to begin with: -* [How to Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api/create-menu-extension/) -* [How to Create a Dockable Pane Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api/create-dockable-pane-extension/) -* [How to Create a Context Menu Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api/create-context-menu/) -* [How to Create a Web View Hosted Inside a Modal Dialog Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api/create-modal-web-view/) -* [How to Create Microflows for Calculations Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api/create-microflows-for-calculations/) +* [How to Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-menu-extension/) +* [How to Create a Dockable Pane Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-dockable-pane-extension/) +* [How to Create a Context Menu Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-context-menu/) +* [How to Create a Web View Hosted Inside a Modal Dialog Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-modal-web-view/) +* [How to Create Microflows for Calculations Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-microflows-for-calculations/) ## Advanced APIs @@ -56,10 +56,10 @@ APIs for the Mendix platform's advanced users: You can dive into the following topics in depth: -* [What are extension points](/apidocs-mxsdk/apidocs/csharp-extensibility-api/extension-points/) -* [What are the Extensibility API services](/apidocs-mxsdk/apidocs/csharp-extensibility-api/services/) +* [What are extension points](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/extension-points/) +* [What are the Extensibility API services](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/services/) * [How to Interact with the Model API Using C#](/apidocs-mxsdk/apidocs/interact-with-model-api/) -* [How to host web content via a web view wrapper](/apidocs-mxsdk/apidocs/csharp-extensibility-api/web-views/) -* [How to Build a Todo Example Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api/build-todo-example-extension/) +* [How to host web content via a web view wrapper](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/web-views/) +* [How to Build a Todo Example Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/build-todo-example-extension/) ## Documentation in This Category diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/_index.md similarity index 78% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/_index.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/_index.md index 6f020ff1bda..1c563b022a4 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/_index.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/_index.md @@ -1,7 +1,7 @@ --- title: "C# Extensibility API How-tos" linktitle: "How-tos" -url: /apidocs-mxsdk/apidocs/csharp-extensibility-api/how-tos/ +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-9/how-tos/ weight: 3 no_list: false description_list: true diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/add-menu.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/add-menu.md similarity index 94% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/add-menu.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/add-menu.md index d79297666e4..53e80e634be 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/add-menu.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/add-menu.md @@ -1,19 +1,19 @@ --- title: "Add Menus and Submenus to Studio Pro Using C#" linktitle: "Structured Menus" -url: /apidocs-mxsdk/apidocs/csharp-extensibility-api/add-menu/ +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-9/add-menu/ weight: 15 --- ## Introduction -This how-to describes how you can add a menu that contains submenus, some of which also contain submenus of their own. Before you start this how-to, it is recommended to [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api/create-menu-extension/) first. +This how-to describes how you can add a menu that contains submenus, some of which also contain submenus of their own. Before you start this how-to, it is recommended to [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-menu-extension/) first. You can download the example in this how-to in [this GitHub repository](https://github.com/mendix/ExtensionAPI-Samples). ## Creating Menu Extension Class -1. Open the project that you previously created when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api/create-menu-extension/). +1. Open the project that you previously created when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-menu-extension/). 2. Add a new class to the project and name it `MyMenuExtension.cs`. 3. Replace the code in the file with the following code: diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/build-todo-example-extension.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/build-todo-example-extension.md similarity index 99% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/build-todo-example-extension.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/build-todo-example-extension.md index 2afd38dc10d..9310fe047a3 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/build-todo-example-extension.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/build-todo-example-extension.md @@ -1,7 +1,7 @@ --- title: "Build a Todo Example Extension Using C#" linktitle: "ToDo Example" -url: /apidocs-mxsdk/apidocs/csharp-extensibility-api/build-todo-example-extension/ +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-9/build-todo-example-extension/ weight: 20 --- diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/create-context-menu.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/create-context-menu.md similarity index 96% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/create-context-menu.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/create-context-menu.md index e8aa0ce6716..2c46d599f88 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/create-context-menu.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/create-context-menu.md @@ -1,7 +1,7 @@ --- title: "Create a Context Menu Using C#" linktitle: "Context Menu" -url: /apidocs-mxsdk/apidocs/csharp-extensibility-api/create-context-menu/ +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-context-menu/ weight: 6 --- @@ -9,13 +9,13 @@ weight: 6 It is possible to add a context menu to an `IEntity` in Studio Pro or to a `IDocument`, such as microflows and pages, etc. Context menus will be placed under a menu named after the extension that contains them (e.g. `MyExtension`) and context menus can modify those items they relate to. You can achieve this simple by specifying the type when creating the extension. -This how-to describes how you can add a context menu to an entity. Before you start this how-to, it is recommended to [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api/create-menu-extension/) first. +This how-to describes how you can add a context menu to an entity. Before you start this how-to, it is recommended to [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-menu-extension/) first. You can download the example in this how-to in [this GitHub repository](https://github.com/mendix/ExtensionAPI-Samples). ## Creating an Entity Context Menu Extension Class -1. Open the project that you previously created when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api/create-menu-extension/). +1. Open the project that you previously created when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-menu-extension/). 2. Add a new class to the project and name it `MyEntityContextMenuExtension.cs`. 3. Replace the code in the file with the following code: @@ -90,7 +90,7 @@ You can download the example in this how-to in [this GitHub repository](https:// } ``` -The code above creates a series of context menu items for any entity. It is important to note the type `IEntity` is passed in so that the context menu will only apply to entities. It adds menus using the same logic as `MenuExtension.cs`. For more examples, see [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api/create-menu-extension/). +The code above creates a series of context menu items for any entity. It is important to note the type `IEntity` is passed in so that the context menu will only apply to entities. It adds menus using the same logic as `MenuExtension.cs`. For more examples, see [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-menu-extension/). In the code above, you can see a few context menus that can perform changes on the entity they belong to. In The entity's location on the canvas can be changed, it can be renamed, and some of its information is shown in a message box. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/create-dockable-pane.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/create-dockable-pane.md similarity index 91% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/create-dockable-pane.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/create-dockable-pane.md index a244ab7ce8c..320cbd5e8fa 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/create-dockable-pane.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/create-dockable-pane.md @@ -1,19 +1,19 @@ --- title: "Create a Dockable Pane Extension Using C#" linktitle: "Dockable Pane" -url: /apidocs-mxsdk/apidocs/csharp-extensibility-api/create-dockable-pane-extension/ +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-dockable-pane-extension/ weight: 5 --- ## Introduction -This how-to describes how you can add a custom dockable web pane window to Studio Pro. Before you start this how-to, it is recommended to [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api/create-menu-extension/) first. +This how-to describes how you can add a custom dockable web pane window to Studio Pro. Before you start this how-to, it is recommended to [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-menu-extension/) first. You can download the example in this how-to in [this GitHub repository](https://github.com/mendix/ExtensionAPI-Samples). ## Creating a Dockable Pane Extension Class -1. Open the project that you previously created when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api/create-menu-extension/). +1. Open the project that you previously created when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-menu-extension/). 2. Add a new class to the project and name it `MyDockablePaneExtension.cs`. 3. Replace the code in the file with the following code: @@ -57,7 +57,7 @@ public class MyDockablePaneExtensionWebViewModel(string homePage) : WebViewDocka {{% snippet file="/static/_includes/apidocs-mxsdk/warning-wwwroot.md" %}} {{% /alert %}} -The code above creates a new web-enabled tab view. You still need a way to show the new dockable pane. To do so, you need to modify the menu extension you added when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api/create-menu-extension/). Simple replace the existing content of `MyMenuExtension.cs` with the following code: +The code above creates a new web-enabled tab view. You still need a way to show the new dockable pane. To do so, you need to modify the menu extension you added when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-menu-extension/). Simple replace the existing content of `MyMenuExtension.cs` with the following code: ```csharp using System.ComponentModel.Composition; @@ -83,7 +83,7 @@ Firstly, you inject the `IDockingWindowService` so that you can open a new docka Secondly, you add a new menu item with the caption **Open My Dockable Pane** that you will use to open your new dockable pane using the `IDockingWindow` service that you have injected. -In order for the changes to reflect, you need to build your project. If you have opted to not automatically copy the output to the destination folder, then you will also need to manually copy the bin output from your project to your extension folder you created when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api/create-menu-extension/). +In order for the changes to reflect, you need to build your project. If you have opted to not automatically copy the output to the destination folder, then you will also need to manually copy the bin output from your project to your extension folder you created when you followed [Create a Menu Extension Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-menu-extension/). ## Showing a Dockable Pane Without Adding a Custom Menu diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/create-menu-extension.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/create-menu-extension.md similarity index 98% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/create-menu-extension.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/create-menu-extension.md index b0958316122..a6cbe3f521e 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/create-menu-extension.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/create-menu-extension.md @@ -1,7 +1,7 @@ --- title: "Create a Menu Extension Using C#" linktitle: "Create Menu" -url: /apidocs-mxsdk/apidocs/csharp-extensibility-api/create-menu-extension/ +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-menu-extension/ weight: 4 --- diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/create-microflow-service.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/create-microflow-service.md similarity index 97% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/create-microflow-service.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/create-microflow-service.md index 3bccd67b8ea..1dc641e5486 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/create-microflow-service.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/create-microflow-service.md @@ -1,7 +1,7 @@ --- title: "Create a Microflow and Add Activities Using C#" linktitle: "Microflows and Activities" -url: /apidocs-mxsdk/apidocs/csharp-extensibility-api/create-microflow-add-activities/ +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-microflow-add-activities/ weight: 14 --- @@ -88,7 +88,7 @@ In this more advanced example, you will see the `IMicroflowExpressionService.Cre } ``` -The `IMicroflowService.CreateMicroflow` method is a bit easier to use than the `IMicroflowService.Initialize` method because it doesn't require manually creating the microflow with `IModel.Create` and then manually adding it to the `IFolderBase` container. It can do everything behind the scenes as long as everything is supplied to it. For a comprehensive example on how to create microflows, see [Create Microflows for Calculations Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api/create-microflows-for-calculations/). +The `IMicroflowService.CreateMicroflow` method is a bit easier to use than the `IMicroflowService.Initialize` method because it doesn't require manually creating the microflow with `IModel.Create` and then manually adding it to the `IFolderBase` container. It can do everything behind the scenes as long as everything is supplied to it. For a comprehensive example on how to create microflows, see [Create Microflows for Calculations Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-microflows-for-calculations/). ## `TryInsertAfterStart` and `TryInsertBeforeActivity` diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/create-microflows-calculations.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/create-microflows-calculations.md similarity index 97% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/create-microflows-calculations.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/create-microflows-calculations.md index d89ffcb5590..a4b8a1407d4 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/create-microflows-calculations.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/create-microflows-calculations.md @@ -1,7 +1,7 @@ --- title: "Create Microflows for Calculations Using C#" linktitle: "Calculation Microflows" -url: /apidocs-mxsdk/apidocs/csharp-extensibility-api/create-microflows-for-calculations/ +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-microflows-for-calculations/ weight: 8 --- @@ -13,7 +13,7 @@ You can download the example in this how-to in [this GitHub repository](https:// ## Creating an Extension Class that Creates Microflows -1. Open the project that you previously created when you [created a menu extension](/apidocs-mxsdk/apidocs/csharp-extensibility-api/create-menu-extension/). +1. Open the project that you previously created when you [created a menu extension](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-menu-extension/). 2. Add a new folder *MicroflowTutorial* to your solution. 3. Create a `MenuExtension` class. 4. Add a new class to the project and call it `CreateMicroflowsMenu.cs`. @@ -86,7 +86,7 @@ public void CreateMicroflows(IModel currentApp) As you can see, the `CreateMicroflows` method starts a new transaction, by calling `currentApp.StartTransaction`, which is the only way an extension can modify the model of the app. If your class tried to create microflows outside of a transaction, an error will be thrown. For more information, see [Interact with the Model API Using C#](/apidocs-mxsdk/apidocs/interact-with-model-api/). - `IMicroflowService` enables creating microflows. For details, see [Create a Microflow and Add Activities Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api/create-microflow-add-activities/). It requires the current model (`IModel`), the containing module or folder inside a module (`IFolderBase`), a name, and an optional `MicroflowReturnValue`. The return value is actually used in the example, so you will see how to create one as well. + `IMicroflowService` enables creating microflows. For details, see [Create a Microflow and Add Activities Using C#](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-microflow-add-activities/). It requires the current model (`IModel`), the containing module or folder inside a module (`IFolderBase`), a name, and an optional `MicroflowReturnValue`. The return value is actually used in the example, so you will see how to create one as well. A microflow returns a value with `IMicroflowExpression`. This can be achieved by using your `IMicroflowExpressionService`, which returns an expression from a String input, and set that expression as the microflow's return value. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/create-modal-web-view.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/create-modal-web-view.md similarity index 98% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/create-modal-web-view.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/create-modal-web-view.md index 3159b33cbf3..c5a8b7cbf8a 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/create-modal-web-view.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/create-modal-web-view.md @@ -1,7 +1,7 @@ --- title: "Create a Web View Hosted Inside a Modal Dialog Using C#" linktitle: "Modal Web View" -url: /apidocs-mxsdk/apidocs/csharp-extensibility-api/create-modal-web-view/ +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-9/create-modal-web-view/ weight: 7 --- diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/export-an-extension.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/export-an-extension.md similarity index 88% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/export-an-extension.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/export-an-extension.md index 9958b3f8958..d4812f01ab5 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/export-an-extension.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/export-an-extension.md @@ -1,7 +1,7 @@ --- title: "Export a C# Extension" linktitle: "Export Extension" -url: /apidocs-mxsdk/apidocs/csharp-extensibility-api/export-an-extension/ +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-9/export-an-extension/ weight: 99 --- @@ -11,7 +11,7 @@ This how-to describes how you can export an extension so that you can publish it ## Prerequisites -To be able to export extension add-on modules, you need to have the feature flag `--enable-extension-development` enabled. For more information, see [Get Started with the Extensibility API](/apidocs-mxsdk/apidocs/csharp-extensibility-api/get-started/). +To be able to export extension add-on modules, you need to have the feature flag `--enable-extension-development` enabled. For more information, see [Get Started with the Extensibility API](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/get-started/). ## Procedure @@ -25,7 +25,7 @@ To be able to export extension add-on modules, you need to have the feature flag 5. In the **Module version** field, enter the version number of the extension. 6. In the **Extension Name** field, select the name of your extension. This must match with your module name. - {{% alert color="info" %}} If you do not set the **Extension name** field, your feature flag is not configured correctly. For more information, see [Get Started with the Extensibility API](/apidocs-mxsdk/apidocs/csharp-extensibility-api/get-started/). {{% /alert %}} + {{% alert color="info" %}} If you do not set the **Extension name** field, your feature flag is not configured correctly. For more information, see [Get Started with the Extensibility API](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/get-started/). {{% /alert %}} 7. Click **OK** to save the settings. 8. In **App Explorer**, right-click the module and click **Export** to export the extension. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/interact-with-model-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/interact-with-model-api.md similarity index 98% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/interact-with-model-api.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/interact-with-model-api.md index c87f0a2e2e2..e4f2f9f610f 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/interact-with-model-api.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/interact-with-model-api.md @@ -1,7 +1,7 @@ --- title: "Interact with the Model API Using C#" linktitle: "Model Interaction" -url: /apidocs-mxsdk/apidocs/interact-with-model-api/ +url: /apidocs-mxsdk/apidocs/interact-with-model-api-9/ weight: 11 --- diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/untyped-model-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/untyped-model-api.md similarity index 98% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/untyped-model-api.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/untyped-model-api.md index 392c71af426..e8640c83958 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-howtos/untyped-model-api.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-howtos/untyped-model-api.md @@ -1,7 +1,7 @@ --- title: "Use the Untyped Model Access API Using C#" linktitle: "Untyped Model API" -url: /apidocs-mxsdk/apidocs/untyped-model-access-api/ +url: /apidocs-mxsdk/apidocs/untyped-model-access-api-9/ weight: 24 --- diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-reference-guide/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-reference-guide/_index.md similarity index 85% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-reference-guide/_index.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-reference-guide/_index.md index 2653758d686..b1d9ff22813 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-reference-guide/_index.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-reference-guide/_index.md @@ -1,7 +1,7 @@ --- title: "Reference Guide for the C# Extensibility API" linktitle: "Reference Guide" -url: /apidocs-mxsdk/apidocs/csharp-extensibility-api/refguide/ +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-9/refguide/ weight: 2 no_list: false description_list: true diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-reference-guide/extension-points.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-reference-guide/extension-points.md similarity index 97% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-reference-guide/extension-points.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-reference-guide/extension-points.md index 65292c28990..b6bdda464bd 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-reference-guide/extension-points.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-reference-guide/extension-points.md @@ -1,7 +1,7 @@ --- title: "Extensibility Extension Points in C#" linktitle: "Extension Points" -url: /apidocs-mxsdk/apidocs/csharp-extensibility-api/extension-points/ +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-9/extension-points/ weight: 9 --- diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-reference-guide/services.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-reference-guide/services.md similarity index 97% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-reference-guide/services.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-reference-guide/services.md index 8145e3bf794..5ca39360716 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-reference-guide/services.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-reference-guide/services.md @@ -1,7 +1,7 @@ --- title: "Studio Pro Extensibility Services in C#" linktitle: "Studio Pro Services" -url: /apidocs-mxsdk/apidocs/csharp-extensibility-api/services/ +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-9/services/ weight: 10 --- diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-reference-guide/web-view.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-reference-guide/web-view.md similarity index 94% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-reference-guide/web-view.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-reference-guide/web-view.md index f0d7fe24b67..8f98892272a 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/extensibility-api-reference-guide/web-view.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/extensibility-api-reference-guide/web-view.md @@ -1,7 +1,7 @@ --- title: "Extensibility Web Views in C#" linktitle: "Web Views" -url: /apidocs-mxsdk/apidocs/csharp-extensibility-api/web-views/ +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-9/web-views/ weight: 12 --- @@ -39,7 +39,7 @@ In addition, the view model class can be used to house the logic for communicati ## Serving Content to the Web View -For serving content to the web view and communicating both ways with it, see [Build a Todo Example Extension](/apidocs-mxsdk/apidocs/csharp-extensibility-api/build-todo-example-extension/). +For serving content to the web view and communicating both ways with it, see [Build a Todo Example Extension](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/build-todo-example-extension/). {{% alert color="warning" %}} {{% snippet file="/static/_includes/apidocs-mxsdk/warning-wwwroot.md" %}} diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/get-started.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/get-started.md similarity index 98% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/get-started.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/get-started.md index 2e04d511cf6..43ad9931274 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/csharp/get-started.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/csharp/get-started.md @@ -1,7 +1,7 @@ --- title: "Get Started with the C# Extensibility API" linktitle: "Get Started" -url: /apidocs-mxsdk/apidocs/csharp-extensibility-api/get-started/ +url: /apidocs-mxsdk/apidocs/csharp-extensibility-api-9/get-started/ weight: 2 --- diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/packaging-your-extension.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/packaging-your-extension.md new file mode 100644 index 00000000000..0743e1889d3 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/packaging-your-extension.md @@ -0,0 +1,29 @@ +--- +title: "Packaging Your Extension" +url: /apidocs-mxsdk/apidocs/extensibility-api-9/packaging-your-extension +weight: 30 +--- + +# Packaging your extension + +Once you have finished development on your extension, you might want to package it into an add-on module so that others can start using it. Once you have created the add-on module, it can then be published to the Mendix Marketplace for your extension users to download into their Studio Pro app. + +To package your extension, you will still need the `--enable-extension-development` command line option turned on. Create a new module in your Studio Pro app containing your dev extension, give it an appropriate name. Open the module's settings form and set it to be an Add-on module. In the `Extension name` dropdown, select the extension you want to package into it. + +![Extension Add-on Module](/attachments/apidocs-mxsdk/apidocs/extensibility-api/extensionAddOnModule.png) + +After you've created your add-on module with its extension, you can now export it, by right-clicking the module in the App Explorer and choosing `Export add-on module package`, as shown below. + +![Export Module](/attachments/apidocs-mxsdk/apidocs/extensibility-api/exportAddOnModule.png) + +You can now save the add-on module to a location of your choice. + +# Importing the extension add-on module + +Once the add-on module is available to a Studio Pro user, they are now able to add it in their application. They can so so by right-clicking the app in the App Explorer and choosing `Import module package`, as shown below. + +![Import Module](/attachments/apidocs-mxsdk/apidocs/extensibility-api/importAddOnModule.png) + +Once an add-on module containing an extension is imported in the app, Studio Pro will show a warning to the user, asking to trust the extension contained in it. If the user does not choose to trust, the module will still be imported but the extension inside it won't be loaded. + +![Trust Extension](/attachments/apidocs-mxsdk/apidocs/extensibility-api/trustExtension.png) \ No newline at end of file diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/_index.md similarity index 80% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/_index.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/_index.md index ab7cc52a4e5..3f1ad00e30b 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/_index.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/_index.md @@ -1,7 +1,7 @@ --- title: "Extensibility API for Web Developers" linktitle: "Web Extensibility API" -url: /apidocs-mxsdk/apidocs/web-extensibility-api +url: /apidocs-mxsdk/apidocs/web-extensibility-api-9/ description: "The web extensibility API allows your custom Studio Pro extensions developed using JavaScript to interact with some internal services of Studio Pro." weight: 20 --- @@ -13,7 +13,7 @@ weight: 20 ## Introduction -Extensions can be written in Typescript or other web languages, described here, or using a C# API which is documented separately in [Extensibility API for C# Developers](/apidocs-mxsdk/apidocs/csharp-extensibility-api/). +Extensions can be written in Typescript or other web languages, described here, or using a C# API which is documented separately in [Extensibility API for C# Developers](/apidocs-mxsdk/apidocs/csharp-extensibility-api-9/). ## Prerequisites @@ -23,15 +23,15 @@ Extensions can be written in Typescript or other web languages, described here, ## Getting Started -For detailed explanation on how to get started with extensions, check out [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api/getting-started/). +For detailed explanation on how to get started with extensions, check out [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-9/getting-started/). ## How-tos Here is a list of how-tos for you to begin with: -* [How to Create a Dockable Pane Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api/dockable-pane-api/) -* [How to Interact With Local App Files Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api/local-app-files-api/) -* [How to Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api/menu-api/) -* [How to Show a Message Box Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api/messagebox-api/) -* [How to Access a Mendix Model Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api/model-api/) -* [How to Open a Tab Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api/tab-api/) +* [How to Create a Dockable Pane Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-9/dockable-pane-api/) +* [How to Interact With Local App Files Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-9/local-app-files-api/) +* [How to Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-9/menu-api/) +* [How to Show a Message Box Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-9/messagebox-api/) +* [How to Access a Mendix Model Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-9/model-api/) +* [How to Open a Tab Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-9/tab-api/) diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/get-started.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/get-started.md similarity index 98% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/get-started.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/get-started.md index dfec6a9b667..cf5e9f6a4d0 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/get-started.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/get-started.md @@ -1,7 +1,7 @@ --- title: "Get Started with the Web Extensibility API" linktitle: "Get Started" -url: /apidocs-mxsdk/apidocs/web-extensibility-api/getting-started/ +url: /apidocs-mxsdk/apidocs/web-extensibility-api-9/getting-started/ weight: 2 --- diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/_index.md similarity index 79% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/_index.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/_index.md index 43ff66b3129..fb007dd3c1d 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/_index.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/_index.md @@ -1,7 +1,7 @@ --- title: "Web Extensibility API How-tos" linktitle: "How-tos" -url: /apidocs-mxsdk/apidocs/web-extensibility-api/how-tos/ +url: /apidocs-mxsdk/apidocs/web-extensibility-api-9/how-tos/ weight: 3 no_list: false description_list: true diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/dockable-pane-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/dockable-pane-api.md new file mode 100644 index 00000000000..d8bfba1d6be --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/dockable-pane-api.md @@ -0,0 +1,349 @@ +--- +title: "Create a Dockable Pane Using Web API" +linktitle: "Dockable Pane" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-9/dockable-pane-api/ +weight: 10 +--- + +## Introduction + +This guide explains how to create and manage a dockable pane using the web extensions API. A Dockable pane allows you to create a web view that can be docked and moved within the Studio Pro user interface. Examples of dockable panes in Studio Pro are: + +* Marketplace +* Errors +* Stories +* Toolbox + +## Prerequisites + +This guide uses the app created in [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-9/getting-started/). Please complete that how-to before starting this one. + +## Creating a Dockable Pane + +To open a dockable pane you must first register the dockable pane handle with the API. To do this, add a call to register the pane to the extension loaded method in the `src/main/index.ts`. + +```typescript + const paneHandle = await studioPro.ui.panes.register( + { + title: "My Extension Pane", + initialPosition: "right", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "dockablepane", + }); +``` + +Use the 'paneHandle' you registered to interact with the dockable pane. + +After adding this call the `loaded()` method looks like this: + +```typescript {hl_lines=["11-19"]} + async loaded() { + // Add a menu item to the Extensions menu + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "MyExtension Menu", + subMenus: [ + { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, + ], + }); + + const paneHandle = await studioPro.ui.panes.register( + { + title: "My Extension Pane", + initialPosition: "right", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "dockablepane", + }); + + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + (args) => { + if (args.menuId === "myextension.ShowTabMenuItem") { + studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + } + } + ); + } +``` + +## Adding a Menu To Open the Dockable Pane + +You will now add a menu that will open the pane when it is selected. + +1. Add a new submenu to the existing `extensionsMenu.add()` method on line 10. + + ```typescript {linenos=table linenostart=10} + // Add a menu item to the Extensions menu + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "MyExtension Menu", + subMenus: [ + { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, + { menuId: "myextension.ShowDockMenuItem", caption: "Show dock pane" }, + ], + }); + ``` + +1. Add lines to the `addEventListener()` call to handle opening the dockable pane once the menu has been selected, as follows: + + ```typescript + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + (args) => { + if (args.menuId === "myextension.ShowTabMenuItem") { + studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + } + else if (args.menuId === "myextension.ShowDockMenuItem") { + studioPro.ui.panes.open(paneHandle); + } + } + ); + ``` + +Your `loaded()` method should now look like this: + +```typescript {hl_lines=["3-10","22-41"]} + async loaded() { + // Add a menu item to the Extensions menu + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "MyExtension Menu", + subMenus: [ + { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, + { menuId: "myextension.ShowDockMenuItem", caption: "Show dock pane" }, + ], + }); + + const paneHandle = await studioPro.ui.panes.register( + { + title: "My Extension Pane", + initialPosition: "right", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "dockablepane", + }); + + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + (args) => { + if (args.menuId === "myextension.ShowTabMenuItem") { + studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + } + else if (args.menuId === "myextension.ShowDockMenuItem") { + studioPro.ui.panes.open(paneHandle); + } + } + ); + } +``` + +## Specifying a Web View Endpoint + +### Adding New Endpoint Handlers + +You must now create a new web view endpoint where the user interface to be rendered within the pane is defined. You can use the existing endpoint and rename it to something more appropriate. + +1. Rename `ui/index.tsx` to `ui/tab.tsx` +1. Add the new endpoint file, `ui/dockablepane.tsx` by copying `ui/tab.tsx`. + +You must also alter the `vite.config.ts` and `manifest.json` files to bind to the correct endpoint, as described in the following sections: + +### Altering `vite.config.js` + +Replace the entry section of `vite.config.js` with the following: + +```typescript + entry: { + main: "src/main/index.ts", + tab: "src/ui/tab.tsx", + dockablepane: "src/ui/dockablepane.tsx", + } +``` + +This instructs vite that the tab endpoint is connected to `src/ui/tab.tsx` and the dockable pane endpoint is connected to `src/ui/dockablepane.tsx`. + +`vite.config.js` should now look like this: + +```typescript {hl_lines=["7-11"]} +import { defineConfig, ResolvedConfig, UserConfig } from "vite"; + +export default defineConfig({ + build: { + lib: { + formats: ["es"], + entry: { + main: "src/main/index.ts", + tab: "src/ui/tab.tsx", + dockablepane: "src/ui/dockablepane.tsx", + }, + }, + rollupOptions: { + external: ["@mendix/component-framework", "@mendix/model-access-sdk"], + }, + outDir: "./dist/myextension", + }, +} satisfies UserConfig); +``` + +### Altering `public/manifest.json` + +You also need to instruct Studio Pro to load the endpoint that you just created. To do this, modify the manifest file `public/manifest.json`. + +Alter the "ui" section by changing the `tab` endpoint and adding the `dockablepane` endpoint. + +```typescript + "ui": { + "tab": "tab.js", + "dockablepane": "dockablepane.js" + } +``` + +The `manifest.json file` should now look like this: + +```typescript {hl_lines=["5-8"]} +{ + "mendixComponent": { + "entryPoints": { + "main": "main.js", + "ui": { + "tab": "tab.js", + "dockablepane": "dockablepane.js" + } + } + } +} +``` + +## Closing the Dockable Pane + +Now that you have registered a pane and can open, it would also be a good idea to close it. + +You will close your pane using a new menu item. + +First add a new sub menu item to the menu on line 11. + +```typescript {linenos=table linenostart=11} + { menuId: "myextension.HideDockMenuItem", caption: "Hide dock pane" }, +``` + +You must also alter the event handler for the new menu at the end of the loaded method: + +```typescript + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + (args) => { + if (args.menuId === "myextension.ShowTabMenuItem") { + studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + } + else if (args.menuId === "myextension.ShowDockMenuItem") { + studioPro.ui.panes.open(paneHandle); + } + else if (args.menuId === "myextension.HideDockMenuItem") { + studioPro.ui.panes.close(paneHandle); + } + } + ); +``` + +The loaded method should now look like this: + +```typescript {hl_lines=["9","24-45"]} + async loaded() { + // Add a menu item to the Extensions menu + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "MyExtension Menu", + subMenus: [ + { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, + { menuId: "myextension.ShowDockMenuItem", caption: "Show dock pane" }, + { menuId: "myextension.HideDockMenuItem", caption: "Hide dock pane" }, + ], + }); + + const paneHandle = await studioPro.ui.panes.register( + { + title: "My Extension Pane", + initialPosition: "right", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "dockablepane", + }); + + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + (args) => { + if (args.menuId === "myextension.ShowTabMenuItem") { + studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + } + else if (args.menuId === "myextension.ShowDockMenuItem") { + studioPro.ui.panes.open(paneHandle); + } + else if (args.menuId === "myextension.HideDockMenuItem") { + studioPro.ui.panes.close(paneHandle); + } + } + ); + } +``` + +## Conclusion + +You now have a new dockable pane with its own user interface which you can modify as you like. +You can also open and close the dockable pane from a menu. + +## Extensibility Feedback + +If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) + +Any feedback is much appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/local-app-files-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/local-app-files-api.md new file mode 100644 index 00000000000..412825fcad7 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/local-app-files-api.md @@ -0,0 +1,138 @@ +--- +title: "Interact With Local App Files Using Web API" +linktitle: "Local Files" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-9/local-app-files-api/ +weight: 20 +--- + +## Introduction + +This how-to shows you how to interact with local application files from within an extension. It adds three new buttons to the tab and three new event handlers for saving, loading, and deleting a file called `HelloWorld.txt`. + +## Prerequisites + +This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-9/getting-started/). Please complete that how-to before starting this one. + +## Adding Some Interactivity + +First, you will add all the code. The changes will then be explained so that you can understand what the code does. + +1. Open `src/ui/index.tsx`. +1. Replace the contents of the file with the following code: + +```typescript +import { studioPro } from "@mendix/extensions-api"; +import { StrictMode, useCallback } from "react"; +import { createRoot } from "react-dom/client"; + +const saveFile = async () => { + await studioPro.app.files.putFile( + "HelloWorld.txt", + "Hello world from a file!" + ); + studioPro.ui.messageBoxes.show("info", "Saving HelloWorld.txt"); +}; + +const loadFile = async () => { + const message = await studioPro.app.files.getFile("HelloWorld.txt"); + studioPro.ui.messageBoxes.show( + "info", + `Loaded HelloWorld.txt it contained: ${message}` + ); +}; + +const deleteFile = async () => { + await studioPro.app.files.deleteFile("HelloWorld.txt"); + studioPro.ui.messageBoxes.show("info", "Deleted HelloWorld.txt"); +}; + +createRoot(document.getElementById("root")!).render( + +

Mendix Studio Pro Extension

+

Hello from an extension!

+

+ + + +

+
+); +``` + +## What Does the Code Do? + +The following sections explain the various parts of the code. + +### saveFile + +The `saveFile` callback calls the `putFile` API setting the filename to `HelloWorld.txt` and the content to `Hello world from a file!`. + +```typescript +const saveFile = async () => { + await studioPro.app.files.putFile( + "HelloWorld.txt", + "Hello world from a file!" + ); + studioPro.ui.messageBoxes.show("info", "Saving HelloWorld.txt"); +}; +``` + +### loadFile + +The loadFile callback calls the getFile API requesting to load `HelloWorld.txt`. It then shows message box displaying the content of the file. + +```typescript +const loadFile = async () => { + const message = await studioPro.app.files.getFile("HelloWorld.txt"); + studioPro.ui.messageBoxes.show( + "info", + `Loaded HelloWorld.txt it contained: ${message}` + ); +}; +``` + +### deleteFile + +The deleteFile callback calls the deleteFile API requesting to delete `HelloWorld.txt` + +```typescript +const deleteFile = async () => { + await studioPro.app.files.deleteFile("HelloWorld.txt"); + studioPro.ui.messageBoxes.show("info", "Deleted HelloWorld.txt"); +}; +``` + +### Adding the Buttons + +The final part of the code adds three new buttons which, when clicked, call the callbacks described above. + +```typescript +createRoot(document.getElementById("root")!).render( + +

Mendix Studio Pro Extension

+

Hello from an extension!

+

+ + + +

+
+); +``` + +## Some Restrictions + +The App files API allows you to modify files within your application's folder. It will not: + +* serve restricted files such as the `.mpr` file or the contents of some folders such as the `.git` folder. +* allow access to files outside of the app folder. + +## Conclusion + +You can now create and extension which save, load, and delete files within the app folder. + +## Extensibility Feedback + +If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) + +Any feedback is much appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/menu-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/menu-api.md new file mode 100644 index 00000000000..041137022be --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/menu-api.md @@ -0,0 +1,209 @@ +--- +title: "Create a Menu Using Web API" +linktitle: "Create Menu" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-9/menu-api/ +weight: 30 +--- + +## Introduction + +This how-to shows you how to create both a simple menu item and a menu item with subsidiary items beneath it using the web extension API. + +## Prerequisites + +This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-9/getting-started/). Please complete that how-to before starting this one. + +## Creating a Simple Menu + +In this section, you will add a simple menu to your extension. + +The code below will: + +* create a menu item under the menu that was added in [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-9/getting-started/) +* show a dialog when the menu is clicked + +Replace your `src/main/index.ts` file with the following: + +```typescript +import { IComponent, Menu, studioPro } from "@mendix/extensions-api"; + +const menuApi = studioPro.ui.extensionsMenu; + +const messageBoxApi = studioPro.ui.messageBoxes; +const menuId = "my-menu-unique-id"; +const caption = "My First Menu"; + +// Open a message box when the menu item is clicked +studioPro.ui.extensionsMenu.addEventListener("menuItemActivated", (args) => { + if (args.menuId === "my-menu-unique-id") { + messageBoxApi.show("info", `My menu '${args.menuId}' was clicked`); + } +}); +class Main implements IComponent { + async loaded() { + const menu: Menu = { + caption: caption, + menuId: menuId, + subMenus: [], + hasSeparatorBefore: false, + hasSeparatorAfter: true, + enabled: true, + }; + + await menuApi.add(menu); + } +} + +export const component: IComponent = new Main(); +``` + +The code imports the following: + +* `menuApi` from `studioPro.ui.extensionsMenu` to allow you to use the menu API +* `messageBoxApi` from `studioPro.ui.messageBoxes` to show a dialog. + +It starts listening to the `menuItemActivated` endpoint which will notify the extension when **My First Menu** is clicked. + +The `menuItemActivated` listener event handles the actual function of the menu. The arguments `args` contain the data returned by Studio Pro to the extension, notifying it which menu item was activated (clicked). This is passed as the `menuId` that was used when creating the menu item and the `menuId` in the `if` statement is used to identify this. In this example it is `"my-menu-unique-id"` and the handler calls the `messageBoxApi` to show an information dialog. + +Your extensions should now appear like this: + +{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/menus/my_first_menu.png" >}} + +## Menu Properties + +The menu has the following properties: + +* `caption` – the text of the menu item +* `menuId` – a unique id for the menu item +* `subMenus` – a list of menus subsidiary to this menu item +* `hasSeparatorBefore` (default: `false`) – shows a visual separator before this menu item +* `hasSeparatorAfter` (default: `false`) – shows a visual separator after this menu item +* `enabled` (default: `true`) – indicates that this menu item notifies the listener when clicked + +{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/menus/grouped_menus.png" >}} + + +## Creating a Menu with Submenus + +You can also have a number of submenus that branch out your menu. + +To do so, add additional menu items to your code and add these to the `subMenus` array for the relevant menu item. These child menus can in turn have their own submenus, and so on. Only parent menus (menus that are not sub menus to any others) should be added through the `await menuApi.add()` call, as shown in the code sample below. + +{{% alert color="info" %}} +Parent menus (with `subMenus`) do not create `menuItemActivated` events. These only get sent when a leaf menu (a menu that does not have any submenus) is clicked. +{{% /alert %}} + +The following `src/main/index.ts` generates one menu item with sub menus and one menu item without sub menus. + +```typescript +import { IComponent, Menu, studioPro } from "@mendix/extensions-api"; + +const menuApi = studioPro.ui.extensionsMenu; +const messageBoxApi = studioPro.ui.messageBoxes; + +// Open a message box when the menu item is clicked +studioPro.ui.extensionsMenu.addEventListener("menuItemActivated", (args) => { + messageBoxApi.show("info", `Child menu '${args.menuId}' was clicked`); +}); +class Main implements IComponent { + async loaded() { + const grandChild: Menu = { + caption: "Grandchild Menu", + menuId: "grandChild", + }; + + const childMenu1: Menu = { + caption: "Child Menu 1", + menuId: "child_1", + subMenus: [grandChild], + }; + + const childMenu2: Menu = { + caption: "Child Menu 2", + menuId: "child_2", + }; + + const menu1: Menu = { + caption: "Menu 1", + menuId: "menu1", + subMenus: [childMenu1, childMenu2], + }; + + const menu2: Menu = { + caption: "Menu 2", + menuId: "menu2", + subMenus: [], + }; + + await menuApi.add(menu1); + await menuApi.add(menu2); + } +} + +export const component: IComponent = new Main(); +``` + +The menu hierarchy will then be displayed like this: + +{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/menus/child_menus.png" >}} + +## Updating a menu + +Sometimes you might want to disable a menu or update its caption depending on a condition. You can do so by calling the menu API's `updateMenu` method. + +An example is shown in the code below. If you click on the menu item, it will be disabled and its caption will be updated. + +{{% alert color="info" %}} +Only `caption` and `enabled` can be updated. +{{% /alert %}} + +You can test it by the following code as the contents of `src/main/index.ts`. + +```typescript +import { IComponent, Menu, studioPro } from "@mendix/extensions-api"; + +const menuApi = studioPro.ui.extensionsMenu; + +const menuId = "my-menu-unique-id"; +const caption = "My First Menu"; + +menuApi.addEventListener("menuItemActivated", (args) => { + if (args.menuId !== menuId) return; + menuApi.update(menuId, { + caption: `${caption} (Disabled)`, + enabled: false, + }); +}); +class Main implements IComponent { + async loaded() { + const menu: Menu = { + caption: caption, + menuId: menuId, + subMenus: [], + hasSeparatorBefore: false, + hasSeparatorAfter: true, + enabled: true, + }; + + await menuApi.add(menu); + } +} + +export const component: IComponent = new Main(); +``` + +The disabled state is shown in the image below. + +{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/menus/disabled_menu.png" >}} + +## Conclusion + +You have seen how to create simple menu items and menu items with sub menus. +You can also dynamically change the enabled status and caption of a menu item. + +## Extensibility Feedback + +If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) + +Any feedback is much appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/messagebox-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/messagebox-api.md new file mode 100644 index 00000000000..2143b9f00d4 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/messagebox-api.md @@ -0,0 +1,100 @@ +--- +title: "Show a Message Box Using Web API" +linktitle: "Message Box" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-9/messagebox-api/ +weight: 50 +--- + +## Introduction + +This how-to shows you how to show a message box to the Studio Pro user. + +## Prerequisites + +This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-9/getting-started/). Please complete that how-to before starting this one. + +## Showing a Message Box + +Firstly, you need to create a menu which will display a dialog with text. This is done in the `loaded` event in `Main`. + +You can learn how to do that in [Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-9/menu-api/). + +In this example you create three menu items: + +* Show Info +* Show Error +* Show Warning + +You then need to add event listeners to receive and act upon the notifications generated by the menu items. Here the listener events show a different message box, depending on which menu item is selected. The message has the following format: + +`messageBoxApi.show(, , )` + +where + +* `` is the type of message, indicated in the pane title and indicated by an icon. Values are "information" {{% icon name="info-circle" color="blue" %}}, "warning" {{% icon name="alert-triangle" color="yellow" %}}, and "error" {{% icon name="remove-circle" color="red" %}}. +* `` is the message to display. +* `` is an optional extended message which is displayed in an expandable area which is initially collapsed. + +The full typescript file to implement these three menu items and message boxes is as follows. + +```typescript +import { IComponent, Menu, studioPro } from "@mendix/extensions-api"; +const messageBoxApi = studioPro.ui.messageBoxes; +const menuApi = studioPro.ui.extensionsMenu; + +const show_info_menu_id = "show-info-id"; +const show_error_menu_id = "show-error-id"; +const show_warning_menu_id = "show-warning-id"; + +menuApi.addEventListener("menuItemActivated", (args) => { + if (args.menuId === show_info_menu_id) + messageBoxApi.show("info", "This is information.", "Extra info"); + if (args.menuId === show_error_menu_id) + messageBoxApi.show("error", "This is an error.", "Extra error details"); + if (args.menuId === show_warning_menu_id) + messageBoxApi.show( + "warning", + "This is a warning.", + "Extra warning details" + ); +}); + +class Main implements IComponent { + async loaded() { + const infoMenu: Menu = { + caption: "Show Info", + menuId: show_info_menu_id, + }; + + const errorMenu: Menu = { + caption: "Show Error", + menuId: show_error_menu_id, + }; + + const warningMenu: Menu = { + caption: "Show Warning", + menuId: show_warning_menu_id, + }; + + await menuApi.add(infoMenu); + await menuApi.add(errorMenu); + await menuApi.add(warningMenu); + } +} + +export const component: IComponent = new Main(); +``` + +For example, the **Show Info** menu item will display the following message box. + +{{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/messageBoxes/info.png" >}} + +## Conclusion + +You have seen how to implement message boxes triggered by menu items. + +## Extensibility Feedback + +If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) + +Any feedback is much appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/model-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/model-api.md similarity index 98% rename from content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/model-api.md rename to content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/model-api.md index c73ab6c8ac5..d74ca17f3c7 100644 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/extensibility-api/web/web-extensions-howtos/model-api.md +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/model-api.md @@ -1,7 +1,7 @@ --- title: "Access a Mendix Model Using Web API" linktitle: "Mendix Model" -url: /apidocs-mxsdk/apidocs/web-extensibility-api/model-api/ +url: /apidocs-mxsdk/apidocs/web-extensibility-api-9/model-api/ weight: 40 --- diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/tab-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/tab-api.md new file mode 100644 index 00000000000..c9e9c16aca0 --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/extensibility-api/web/web-extensions-howtos/tab-api.md @@ -0,0 +1,233 @@ +--- +title: "Open a Tab Using Web API" +linktitle: "Open a Tab" +url: /apidocs-mxsdk/apidocs/web-extensibility-api-9/tab-api/ +weight: 60 +--- + +## Introduction + +This how-to shows you how to open a tab in Studio Pro from an extension. This tab will contain your web content. + +## Prerequisites + +This how-to uses the results of [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-9/getting-started/). Please complete that how-to before starting this one. You should also be familiar with creating menus as described in [Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-9/menu-api/). + +## Opening a Tab + +Firstly, create a menu item to open the tab. This is done inside the `loaded` event in `Main`. For more information see [Create a Menu Using Web API](/apidocs-mxsdk/apidocs/web-extensibility-api-9/menu-api/). + +In a listener event called `menuItemActivated` the `studioPro.ui.tabs.open(, )` call opens a new tab where: + +* `` is an object containing the `title` of the tab, which will be shown in the title bar of your tab in Studio Pro. +* `` is an object containing two required properties: + + * `componentName` which is the name of the extension prefixed with "extension/". For example "extension/myextension" in the following example. + * `uiEntryPoint` which is the name mapped from the `manifest.json` file. See below for examples with multiple tabs. + +{{% alert color="info" %}} +Whenever the tabs API `open` method is called, the `TabHandle` returned must be tracked by the extension so that it can be closed later by calling the `close` method. +{{% /alert %}} + +An example of the class `Main` to open a tab called **My Extension Tab** looks similar to the following: + +```typescript +import { IComponent, studioPro, TabHandle } from "@mendix/extensions-api"; + +class Main implements IComponent { + tabs: { [menuId: string]: Promise } = {}; + async loaded() { + // Add menu items to the Extensions menu to open and close our tab + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "MyExtension Menu", + subMenus: [ + { menuId: "myextension.ShowTabMenuItem", caption: "Show tab" }, + { + menuId: "myextension.CloseTabMenuItem", + caption: "Close tab", + }, + ], + }); + + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + async (args) => { + // Open a tab when the menu item is clicked + if (args.menuId === "myextension.ShowTabMenuItem") { + const handle = studioPro.ui.tabs.open( + { + title: "My Extension Tab", + }, + { + componentName: "extension/myextension", + uiEntrypoint: "tab", + } + ); + + // Track the open tab + this.tabs["myextension.MainMenu"] = handle; + } + + // Close the tab opened previously + if (args.menuId === "myextension.CloseTabMenuItem") { + studioPro.ui.tabs.close(await this.tabs["myextension.MainMenu"]); + } + } + ); + } +} + +export const component: IComponent = new Main(); +``` + +{{% alert color="info" %}} + + In this example, there is a dictionary that uses the parent menu id as the key to track the open `TabHandle`. +{{% /alert %}} + +## Filling the Tabs With Content + +In the previous example, the `uiEntryPoint` property of the `` object had the value "tab". This value must match the one from the manifest. + +If you want to have multiple tabs in your extension, you need to structure the folders and set up the manifest file correctly. + +To do this, follow these steps: + +1. Add a new method `createTabSpec` in your `Main` class. + + ```typescript + createTabSpec(tab: string, title: string): { info: TabInfo, ui: UISpec} { + const info: TabInfo = { title }; + const ui: UISpec = { + componentName: "extension/myextension", + uiEntrypoint: tab, + }; + + return {info, ui}; + } + ``` + +1. Add three folders inside the `ui` folder, one for each tab you want to display contents for. +1. Create an `index.tsx` file in each folder. +1. Put the following code in each `index.tsx` file (this example is for **tab3**): + + ```typescript + import { StrictMode } from "react"; + import { createRoot } from "react-dom/client"; + + createRoot(document.getElementById("root")!).render( + +

tab3

+
+ ); + ``` + + In this example, we'll add 3 tabs: **tab1**, **tab2**, and **tab3**. + + {{< figure src="/attachments/apidocs-mxsdk/apidocs/extensibility-api/web/tabs/ui_folder_structure.png" >}} + +1. Create listener events in the `Main` class to open each of the three tabs. The `Main` class will then look like this: + + ```typescript + import { IComponent, studioPro, TabInfo, UISpec } from "@mendix/extensions-api"; + + class Main implements IComponent { + async loaded() { + // Add a menu item to the Extensions menu + await studioPro.ui.extensionsMenu.add({ + menuId: "myextension.MainMenu", + caption: "Show Tabs", + subMenus: [ + { menuId: "myextension.ShowTab1", caption: "Show tab 1" }, + { menuId: "myextension.ShowTab2", caption: "Show tab 2" }, + { menuId: "myextension.ShowTab3", caption: "Show tab 3" }, + ], + }); + + // Open a tab when the menu item is clicked + studioPro.ui.extensionsMenu.addEventListener( + "menuItemActivated", + async (args) => { + if (args.menuId === "myextension.ShowTab1") { + const tab1Spec = this.createTabSpec("tab1", "Tab 1 Title"); + studioPro.ui.tabs.open(tab1Spec.info, tab1Spec.ui); + } + if (args.menuId === "myextension.ShowTab2") { + const tab2Spec = this.createTabSpec("tab2", "Tab 2 Title"); + studioPro.ui.tabs.open(tab2Spec.info, tab2Spec.ui); + } + if (args.menuId === "myextension.ShowTab3") { + const tab3Spec = this.createTabSpec("tab3", "Tab 3 Title"); + studioPro.ui.tabs.open(tab3Spec.info, tab3Spec.ui); + } + } + ); + } + + createTabSpec(tab: string, title: string): { info: TabInfo; ui: UISpec } { + const info: TabInfo = { title }; + const ui: UISpec = { + componentName: "extension/myextension", + uiEntrypoint: tab, + }; + + return { info, ui }; + } + } + + export const component: IComponent = new Main(); + ``` + +1. Ensure the tabs are added to the `manifest.json` file. Here is an example of three tabs under the `ui` property. + + ```json + { + "mendixComponent": { + "entryPoints": { + "main": "main.js", + "ui": { + "tab1": "tab1.js", + "tab2": "tab2.js", + "tab3": "tab3.js" + } + } + } + } + ``` + +1. Update `vite.config` to match the manifest with an entry for each tab. For example: + + ```typescript + import { defineConfig, ResolvedConfig, UserConfig } from "vite"; + + export default defineConfig({ + build: { + lib: { + formats: ["es"], + entry: { + main: "src/main/index.ts", + tab1: "src/ui/tab1/index.tsx", + tab2: "src/ui/tab2/index.tsx", + tab3: "src/ui/tab3/index.tsx", + }, + }, + rollupOptions: { + external: ["@mendix/component-framework", "@mendix/model-access-sdk"], + }, + outDir: "./dist/myextension", + }, + } satisfies UserConfig); + ``` + +After building and installing the extension in our Studio Pro app, each tab will display the content specified in the related `index.tsx` file. + +## Conclusion + +You now know how to create tabs and populate them with content. + +## Extensibility Feedback + +If you would like to provide us with some additional feedback you can complete a small [Survey](https://survey.alchemer.eu/s3/90801191/Extensibility-Feedback) + +Any feedback is much appreciated. diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/runtime-api.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/runtime-api.md new file mode 100644 index 00000000000..6525af2a2ca --- /dev/null +++ b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro-9/runtime-api.md @@ -0,0 +1,19 @@ +--- +title: "Mendix Runtime API" +url: /apidocs-mxsdk/apidocs/runtime-api-9/ +description: "The Mendix Runtime API provides all the functionality and information from both the application model and Mendix Runtime." +weight: 75 +--- + +## Introduction + +Extend your application model using Java. All functionality and information from both the application model and Mendix Runtime is accessible via the Mendix Runtime API. + +## Mendix Runtime API + +* [Mendix 9 Runtime API](https://apidocs.rnd.mendix.com/9/runtime/index.html) +* [Mendix 8 Runtime API](https://apidocs.rnd.mendix.com/8/runtime/index.html) + +## Read More + +* [Using the Mendix Runtime Java API](/refguide/java-api-tutorial/) diff --git a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/_index.md b/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/_index.md deleted file mode 100644 index 39eb168192d..00000000000 --- a/content/en/docs/apidocs-mxsdk/apidocs/studio-pro/_index.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: "APIs for Studio Pro" -url: /apidocs-mxsdk/apidocs/apis-for-studio-pro/ -no_list: false -description_list: true -description: "Provides the documentation of APIs for Studio Pro, including Extensibility API and Mendix Runtime API." -weight: 5 -linktitle: "Studio Pro" ---- - -Mendix offers the following APIs for Studio Pro: diff --git a/content/en/docs/appstore/create-content/create-connectors/connector-guide-best-practices.md b/content/en/docs/appstore/create-content/create-connectors/connector-guide-best-practices.md index e7ea41dbfdf..0f083556b1e 100644 --- a/content/en/docs/appstore/create-content/create-connectors/connector-guide-best-practices.md +++ b/content/en/docs/appstore/create-content/create-connectors/connector-guide-best-practices.md @@ -409,7 +409,7 @@ Gradle can handle managing Java dependencies and running [JUnit](https://junit.o #### Using the Java Unit Test Reference -Mendix apps need `Core` classes, and the **Class Core** [Runtime API](/apidocs-mxsdk/apidocs/runtime-api/) allows you to do things that you usually do in a microflow, like committing an object. `CoreProxy` serves as a mockable layer between your Java logic and the **Core** API. +Mendix apps need `Core` classes, and the **Class Core** [Runtime API](/apidocs-mxsdk/apidocs/) allows you to do things that you usually do in a microflow, like committing an object. `CoreProxy` serves as a mockable layer between your Java logic and the **Core** API. Our [Java unit test reference](https://Github.com/mendixlabs/javaunittestreference) is available to help you through this process. `MendixUnitTestBase.java` is extendable and reusable for your own purposes. When extended, it enables Mockito on your test classes and mocks `Core` API behavior. This does the following: diff --git a/content/en/docs/howto/extensibility/_index.md b/content/en/docs/howto/extensibility/_index.md index b4b205f9932..1e5dbe1a644 100644 --- a/content/en/docs/howto/extensibility/_index.md +++ b/content/en/docs/howto/extensibility/_index.md @@ -11,6 +11,6 @@ description_list: true One of the strengths of Mendix is that it is easy to connect your Mendix application to other systems. The [Mendix Marketplace](https://marketplace.mendix.com/) is stocked with all kinds of connectors and adapters. You are also able to extend your app by using and creating widgets. -In a related matter, one of Studio Pro's strengths is is customizability. For more information on adding your first custom extension to Studio Pro, see [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api/getting-started/) or [Get Started with the C# Extensibility API](/apidocs-mxsdk/apidocs/csharp-extensibility-api/get-started/) in our API documentation. +In a related matter, one of Studio Pro's strengths is is customizability. For more information on adding your first custom extension to Studio Pro, see [Get Started with the Web Extensibility API](/apidocs-mxsdk/apidocs/web-extensibility-api-11/getting-started/) or [Get Started with the C# Extensibility API](/apidocs-mxsdk/apidocs/csharp-extensibility-api/get-started/) in our API documentation. ## Documents in This Category diff --git a/content/en/docs/howto/extensibility/howto-datastorage-api.md b/content/en/docs/howto/extensibility/howto-datastorage-api.md index 0f1cbdc1b4a..d7c92944c93 100644 --- a/content/en/docs/howto/extensibility/howto-datastorage-api.md +++ b/content/en/docs/howto/extensibility/howto-datastorage-api.md @@ -13,7 +13,7 @@ Mendix Studio Pro supports two query languages to retrieve data: * XPath as an easy to use query language to retrieve objects * OQL is a SQL based language, more focused on powerful reporting facilities -You can use these query languages in Mendix Studio Pro, but both languages are also available through the [Mendix Runtime Java API](/apidocs-mxsdk/apidocs/runtime-api/). You can use this API to implement powerful reusable microflow actions. In addition to XPath and OQL, the Mendix API also enables you to use standard SQL on your Mendix database. +You can use these query languages in Mendix Studio Pro, but both languages are also available through the [Mendix Runtime Java API](/apidocs-mxsdk/apidocs/runtime-api-11/). You can use this API to implement powerful reusable microflow actions. In addition to XPath and OQL, the Mendix API also enables you to use standard SQL on your Mendix database. This how to describes how you can build the following microflow actions: diff --git a/content/en/docs/howto10/extensibility/howto-datastorage-api.md b/content/en/docs/howto10/extensibility/howto-datastorage-api.md index 0002831503b..5e17bc8ce88 100644 --- a/content/en/docs/howto10/extensibility/howto-datastorage-api.md +++ b/content/en/docs/howto10/extensibility/howto-datastorage-api.md @@ -13,7 +13,7 @@ Mendix Studio Pro supports two query languages to retrieve data: * XPath as an easy to use query language to retrieve objects * OQL is a SQL based language, more focused on powerful reporting facilities -You can use these query languages in Mendix Studio Pro, but both languages are also available through the [Mendix Runtime Java API](/apidocs-mxsdk/apidocs/runtime-api/). You can use this API to implement powerful reusable microflow actions. In addition to XPath and OQL, the Mendix API also enables you to use standard SQL on your Mendix database. +You can use these query languages in Mendix Studio Pro, but both languages are also available through the [Mendix Runtime Java API](/apidocs-mxsdk/apidocs/runtime-api-10/). You can use this API to implement powerful reusable microflow actions. In addition to XPath and OQL, the Mendix API also enables you to use standard SQL on your Mendix database. This how to describes how you can build the following microflow actions: diff --git a/content/en/docs/refguide/java-programming/_index.md b/content/en/docs/refguide/java-programming/_index.md index 80412ec8e26..9ebd2e290f0 100644 --- a/content/en/docs/refguide/java-programming/_index.md +++ b/content/en/docs/refguide/java-programming/_index.md @@ -68,7 +68,7 @@ public class JavaAction_1 extends CustomJavaAction You can use the Mendix Java library in the Java code that you write for your Java actions. {{% alert color="info" %}} -You can find the Javadoc at [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/) or in the directory Studio Pro is installed in (for example, *C:\Program Files\Mendix\9.0.0\runtime\javadoc*). +You can find the Javadoc at [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-11/) or in the directory Studio Pro is installed in (for example, *C:\Program Files\Mendix\9.0.0\runtime\javadoc*). {{% /alert %}} This library is automatically added to your libraries when you import your app into Eclipse, it is called *mxruntime.jar*. diff --git a/content/en/docs/refguide/java-programming/extending-your-application-with-custom-java.md b/content/en/docs/refguide/java-programming/extending-your-application-with-custom-java.md index 52aab32786e..0b0e7402d10 100644 --- a/content/en/docs/refguide/java-programming/extending-your-application-with-custom-java.md +++ b/content/en/docs/refguide/java-programming/extending-your-application-with-custom-java.md @@ -14,7 +14,7 @@ Most application logic can be developed using [microflows](/refguide/microflows/ This document teaches you how to do the following: * Extend your application with custom Java code you can call from a microflow -* Use the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/) to retrieve data from the database using an XPath in a Java action +* Use the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-11/) to retrieve data from the database using an XPath in a Java action ## Prerequisites {#prerequisites} @@ -174,7 +174,7 @@ Now you will add a button to the Product_NewEdit page which uses a microflow to ## XPath Retrieval in Java {#xpath} -Using the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/), your Java actions can interact with many parts of your app. One thing that many developers want to do is to retrieve a list of objects using an [XPath Constraint](/refguide/xpath-constraints/). This section describes how to implement an XPath retrieval in a Java action. +Using the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-11/), your Java actions can interact with many parts of your app. One thing that many developers want to do is to retrieve a list of objects using an [XPath Constraint](/refguide/xpath-constraints/). This section describes how to implement an XPath retrieval in a Java action. Using the Domain Model you set up in [Setting Up a Simple App](#simple-app), above, you want to retrieve a list of Products which meet the following criteria: diff --git a/content/en/docs/refguide/java-programming/java-api-tutorial.md b/content/en/docs/refguide/java-programming/java-api-tutorial.md index 964aa39dd1b..9aa381d8699 100644 --- a/content/en/docs/refguide/java-programming/java-api-tutorial.md +++ b/content/en/docs/refguide/java-programming/java-api-tutorial.md @@ -7,7 +7,7 @@ description: "Describes how to add a Java action, edit it in Eclipse, and call i ## Introduction -This tutorial contains some examples of how to use the [Mendix Runtime Java API](/apidocs-mxsdk/apidocs/runtime-api/). The tutorial assumes that you have some basic Java and Mendix modeling knowledge. +This tutorial contains some examples of how to use the [Mendix Runtime Java API](/apidocs-mxsdk/apidocs/runtime-api-11/). The tutorial assumes that you have some basic Java and Mendix modeling knowledge. To avoid cluttering the tutorial, only the relevant code is shown, not the entire code generated by Studio Pro itself. Recreating the cases yourself and then trying to execute the actions is recommended. diff --git a/content/en/docs/refguide/modeling/domain-model/oql/oql-expressions.md b/content/en/docs/refguide/modeling/domain-model/oql/oql-expressions.md index 786252d8602..dd5aa80c474 100644 --- a/content/en/docs/refguide/modeling/domain-model/oql/oql-expressions.md +++ b/content/en/docs/refguide/modeling/domain-model/oql/oql-expressions.md @@ -221,7 +221,7 @@ Parameters are external variables that are referenced to by name in an OQL query If you use undefined in `IN` and `LIKE` comparison expressions, the condition always returns `true`. In other cases, undefined parameters cause an exception. -{{% alert color="warning" %}} Parameters are only supported within OQL queries defined in [data sets](/refguide/data-sets/) or inside Java actions using the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/). They can not be used in [view entities](/refguide/view-entities/).{{% /alert %}} +{{% alert color="warning" %}} Parameters are only supported within OQL queries defined in [data sets](/refguide/data-sets/) or inside Java actions using the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-11/). They can not be used in [view entities](/refguide/view-entities/).{{% /alert %}} ### Examples diff --git a/content/en/docs/refguide/runtime/_index.md b/content/en/docs/refguide/runtime/_index.md index 48ad87659c8..942560b9d3c 100644 --- a/content/en/docs/refguide/runtime/_index.md +++ b/content/en/docs/refguide/runtime/_index.md @@ -82,7 +82,7 @@ You need a license to run an application in production mode. Without a license, ## APIs -You can extend the functionality of the Runtime Server by writing Java actions. For more information, see the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/). +You can extend the functionality of the Runtime Server by writing Java actions. For more information, see the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-11/). {{% alert color="info" %}} If the app contains published services, links to available API documentation such as [OpenAPI documentation](/refguide/open-api/) for [published REST services](/refguide/published-rest-services/), links to [published OData/GraphQL services](/refguide/published-odata-services/), and WSDLs for [published web services](/refguide/published-web-services/), are available on the URL path `/api-doc` (for example: `https://myapp.mendixcloud.com/api-doc/`). diff --git a/content/en/docs/refguide/runtime/metrics.md b/content/en/docs/refguide/runtime/metrics.md index 2746dfaffa0..6017c8801bb 100644 --- a/content/en/docs/refguide/runtime/metrics.md +++ b/content/en/docs/refguide/runtime/metrics.md @@ -372,7 +372,7 @@ You can use activities to provide custom metrics from your app. See [Metrics Act ## Java API {#java-api} -Micrometer metrics can be accessed through [Mendix Runtime Java APIs](/apidocs-mxsdk/apidocs/runtime-api/) as well inside Mendix. This can be achieved by using the custom runtime setting `com.mendix.metrics.Type`. This setting defaults to `micrometer`. +Micrometer metrics can be accessed through [Mendix Runtime Java APIs](/apidocs-mxsdk/apidocs/runtime-api-11/) as well inside Mendix. This can be achieved by using the custom runtime setting `com.mendix.metrics.Type`. This setting defaults to `micrometer`. * Custom Runtime Setting – **Name**: `com.mendix.metrics.Type` * **Value**: `micrometer` diff --git a/content/en/docs/refguide10/java-programming/_index.md b/content/en/docs/refguide10/java-programming/_index.md index 576753d1935..43bf02d5e16 100644 --- a/content/en/docs/refguide10/java-programming/_index.md +++ b/content/en/docs/refguide10/java-programming/_index.md @@ -68,7 +68,7 @@ public class JavaAction_1 extends CustomJavaAction You can use the Mendix Java library in the Java code that you write for your Java actions. {{% alert color="info" %}} -You can find the Javadoc at [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/) or in the directory Studio Pro is installed in (for example, *C:\Program Files\Mendix\9.0.0\runtime\javadoc*). +You can find the Javadoc at [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-10/) or in the directory Studio Pro is installed in (for example, *C:\Program Files\Mendix\9.0.0\runtime\javadoc*). {{% /alert %}} This library is automatically added to your libraries when you import your app into Eclipse, it is called *mxruntime.jar*. diff --git a/content/en/docs/refguide10/java-programming/extending-your-application-with-custom-java.md b/content/en/docs/refguide10/java-programming/extending-your-application-with-custom-java.md index 19c6f487da6..fc8f689a3b1 100644 --- a/content/en/docs/refguide10/java-programming/extending-your-application-with-custom-java.md +++ b/content/en/docs/refguide10/java-programming/extending-your-application-with-custom-java.md @@ -14,7 +14,7 @@ Most application logic can be developed using [microflows](/refguide10/microflow This document teaches you how to do the following: * Extend your application with custom Java code you can call from a microflow -* Use the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/) to retrieve data from the database using an XPath in a Java action +* Use the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-10/) to retrieve data from the database using an XPath in a Java action ## Prerequisites {#prerequisites} @@ -174,7 +174,7 @@ Now you will add a button to the Product_NewEdit page which uses a microflow to ## XPath Retrieval in Java {#xpath} -Using the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/), your Java actions can interact with many parts of your app. One thing that many developers want to do is to retrieve a list of objects using an [XPath Constraint](/refguide10/xpath-constraints/). This section describes how to implement an XPath retrieval in a Java action. +Using the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-10/), your Java actions can interact with many parts of your app. One thing that many developers want to do is to retrieve a list of objects using an [XPath Constraint](/refguide10/xpath-constraints/). This section describes how to implement an XPath retrieval in a Java action. Using the Domain Model you set up in [Setting Up a Simple App](#simple-app), above, you want to retrieve a list of Products which meet the following criteria: diff --git a/content/en/docs/refguide10/java-programming/java-api-tutorial.md b/content/en/docs/refguide10/java-programming/java-api-tutorial.md index d009582efed..228da99d821 100644 --- a/content/en/docs/refguide10/java-programming/java-api-tutorial.md +++ b/content/en/docs/refguide10/java-programming/java-api-tutorial.md @@ -7,7 +7,7 @@ description: "Describes how to add a Java action, edit it in Eclipse, and call i ## Introduction -This tutorial contains some examples of how to use the [Mendix Runtime Java API](/apidocs-mxsdk/apidocs/runtime-api/). The tutorial assumes that you have some basic Java and Mendix modeling knowledge. +This tutorial contains some examples of how to use the [Mendix Runtime Java API](/apidocs-mxsdk/apidocs/runtime-api-10/). The tutorial assumes that you have some basic Java and Mendix modeling knowledge. To avoid cluttering the tutorial, only the relevant code is shown, not the entire code generated by Studio Pro itself. Recreating the cases yourself and then trying to execute the actions is recommended. diff --git a/content/en/docs/refguide10/modeling/domain-model/oql/oql-expressions.md b/content/en/docs/refguide10/modeling/domain-model/oql/oql-expressions.md index 671aba85a04..5e28770f093 100644 --- a/content/en/docs/refguide10/modeling/domain-model/oql/oql-expressions.md +++ b/content/en/docs/refguide10/modeling/domain-model/oql/oql-expressions.md @@ -221,7 +221,7 @@ Parameters are external variables that are referenced to by name in an OQL query If you use undefined in `IN` and `LIKE` comparison expressions, the condition always returns `true`. In other cases, undefined parameters cause an exception. -{{% alert color="warning" %}} Parameters are only supported within OQL queries defined in [data sets](/refguide10/data-sets/) or inside Java actions using the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/). They can not be used in [view entities](/refguide10/view-entities/).{{% /alert %}} +{{% alert color="warning" %}} Parameters are only supported within OQL queries defined in [data sets](/refguide10/data-sets/) or inside Java actions using the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-10/). They can not be used in [view entities](/refguide10/view-entities/).{{% /alert %}} ### Examples diff --git a/content/en/docs/refguide10/runtime/_index.md b/content/en/docs/refguide10/runtime/_index.md index b0cd654775f..d00679449fb 100644 --- a/content/en/docs/refguide10/runtime/_index.md +++ b/content/en/docs/refguide10/runtime/_index.md @@ -82,7 +82,7 @@ You need a license to run an application in production mode. Without a license, ## APIs -You can extend the functionality of the Runtime Server by writing Java actions. For more information, see the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/). +You can extend the functionality of the Runtime Server by writing Java actions. For more information, see the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-10/). {{% alert color="info" %}} If the app contains published services, links to available API documentation such as [OpenAPI documentation](/refguide10/open-api/) for [published REST services](/refguide10/published-rest-services/), links to [published OData/GraphQL services](/refguide10/published-odata-services/), and WSDLs for [published web services](/refguide10/published-web-services/), are available on the URL path `/api-doc` (for example: `https://myapp.mendixcloud.com/api-doc/`). diff --git a/content/en/docs/refguide10/runtime/metrics.md b/content/en/docs/refguide10/runtime/metrics.md index 81d54f722c0..7d9aa5f0db6 100644 --- a/content/en/docs/refguide10/runtime/metrics.md +++ b/content/en/docs/refguide10/runtime/metrics.md @@ -376,7 +376,7 @@ You can use activities to provide custom metrics from your app. See [Metrics Act ## Java API {#java-api} -Micrometer metrics can be accessed through [Mendix Runtime Java APIs](/apidocs-mxsdk/apidocs/runtime-api/) as well inside Mendix. This can be achieved by using the custom runtime setting `com.mendix.metrics.Type`. This setting defaults to `micrometer`. +Micrometer metrics can be accessed through [Mendix Runtime Java APIs](/apidocs-mxsdk/apidocs/runtime-api-10/) as well inside Mendix. This can be achieved by using the custom runtime setting `com.mendix.metrics.Type`. This setting defaults to `micrometer`. * Custom Runtime Setting – **Name**: `com.mendix.metrics.Type` * **Value**: `micrometer` diff --git a/content/en/docs/refguide8/runtime/_index.md b/content/en/docs/refguide8/runtime/_index.md index e1fb0ace522..b1754350834 100644 --- a/content/en/docs/refguide8/runtime/_index.md +++ b/content/en/docs/refguide8/runtime/_index.md @@ -87,7 +87,7 @@ You need a license to run an application in production mode. Without a license, ## APIs -You can extend the functionality of the Runtime Server by writing Java actions. For more information, see the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/). +You can extend the functionality of the Runtime Server by writing Java actions. For more information, see the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-9/). {{% alert color="info" %}} Links to available API documentation such as WSDLs for published web services are available on the URL path `/api-doc` (for example: `http://localhost:8080/api-doc/`). diff --git a/content/en/docs/refguide9/general/moving-from-8-to-9/_index.md b/content/en/docs/refguide9/general/moving-from-8-to-9/_index.md index a6fbfa50ef2..4afa02febcd 100644 --- a/content/en/docs/refguide9/general/moving-from-8-to-9/_index.md +++ b/content/en/docs/refguide9/general/moving-from-8-to-9/_index.md @@ -77,7 +77,7 @@ Congratulations! Your app has been successfully upgraded to Mendix 9 and you can ## Mendix Runtime API Changes -Most of the Java API calls that were deprecated in Mendix 8 have been removed. If you were still using such methods in your Java actions, you must replace or delete them. To check which calls were depreciated, click the **Mendix 8 Runtime API** link in our [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/). +Most of the Java API calls that were deprecated in Mendix 8 have been removed. If you were still using such methods in your Java actions, you must replace or delete them. To check which calls were depreciated, click the **Mendix 8 Runtime API** link in our [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-9/). Additionally, refer to the Mendix Studio Pro 9.02 Release notes for more Mendix Runtime API change details. diff --git a/content/en/docs/refguide9/java-programming/_index.md b/content/en/docs/refguide9/java-programming/_index.md index 5a133c1494d..e155c1c0421 100644 --- a/content/en/docs/refguide9/java-programming/_index.md +++ b/content/en/docs/refguide9/java-programming/_index.md @@ -68,7 +68,7 @@ public class JavaAction_1 extends CustomJavaAction You can use the Mendix Java library in the Java code that you write for your Java actions. {{% alert color="info" %}} -You can find the Javadoc at the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/) or in the directory Studio Pro is installed in (for example, *C:\Program Files\Mendix\9.0.0\runtime\javadoc*). +You can find the Javadoc at the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-9/) or in the directory Studio Pro is installed in (for example, *C:\Program Files\Mendix\9.0.0\runtime\javadoc*). {{% /alert %}} This library is automatically added to your libraries when you import your app into Eclipse, it is called *mxruntime.jar*. diff --git a/content/en/docs/refguide9/runtime/_index.md b/content/en/docs/refguide9/runtime/_index.md index 3f90eb6390f..f623d03535d 100644 --- a/content/en/docs/refguide9/runtime/_index.md +++ b/content/en/docs/refguide9/runtime/_index.md @@ -83,7 +83,7 @@ You need a license to run an application in production mode. Without a license, ## APIs -You can extend the functionality of the Runtime Server by writing Java actions. For more information, see the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/). +You can extend the functionality of the Runtime Server by writing Java actions. For more information, see the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-9/). {{% alert color="info" %}} If the app contains published services, links to available API documentation such as [OpenAPI documentation](/refguide9/open-api/) for [published REST services](/refguide9/published-rest-services/), links to [published OData services](/refguide9/published-odata-services/), and WSDLs for [published web services](/refguide9/published-web-services/), are available on the URL path `/api-doc` (for example: `https://myapp.mendixcloud.com/api-doc/`). diff --git a/content/en/docs/refguide9/runtime/metrics.md b/content/en/docs/refguide9/runtime/metrics.md index 4503ff8655f..b79bcf27adb 100644 --- a/content/en/docs/refguide9/runtime/metrics.md +++ b/content/en/docs/refguide9/runtime/metrics.md @@ -331,7 +331,7 @@ You can use activities to provide custom metrics from your app. See [Metrics Act ## Java API {#java-api} -Micrometer metrics can be accessed through [Mendix Runtime Java API](/apidocs-mxsdk/apidocs/runtime-api/) as well inside Mendix. This can be achieved by using the custom runtime setting `com.mendix.metrics.Type`. This setting defaults to `micrometer`. +Micrometer metrics can be accessed through [Mendix Runtime Java API](/apidocs-mxsdk/apidocs/runtime-api-9/) as well inside Mendix. This can be achieved by using the custom runtime setting `com.mendix.metrics.Type`. This setting defaults to `micrometer`. * Custom Runtime Setting – **Name**: `com.mendix.metrics.Type` * **Value**: `micrometer` diff --git a/content/en/docs/releasenotes/studio-pro/10/10.0.md b/content/en/docs/releasenotes/studio-pro/10/10.0.md index 47ef1a0a7fc..d409b9b9c5b 100644 --- a/content/en/docs/releasenotes/studio-pro/10/10.0.md +++ b/content/en/docs/releasenotes/studio-pro/10/10.0.md @@ -101,7 +101,7 @@ For details on upgrading to Studio Pro 10, see [Upgrading from Mendix Studio Pro * We reduced the number of default panes in the default layout of Studio Pro. * We aligned the horizontal indentation of the text in the column headers and cells in the data grid and tree controls. * We made it possible to use the scrollbars in disabled grids. -* We added the `IMxRuntimeRequest.getRootUrl` method to the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/). This method can be used to retrieve the root URL of the application. +* We added the `IMxRuntimeRequest.getRootUrl` method to the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-10/). This method can be used to retrieve the root URL of the application. * We added `getSslContext` to the `IHttpConfiguration` interface in the Mendix Runtime API. This gives the Java code access to the `SslContext` that should be used to call a service based on the Mendix Runtime settings. * We added default values for the `ProcessedTasksCleanupAge` and `ScheduledEventsCleanupAge` Mendix Runtime settings. The default value is now 365 days for existing apps with data and 7 days for new apps or apps with an empty database. Previously, the default values were empty and required an explicit Mendix Runtime setting configuration in order to perform cleanup. * We introduced the new Mendix Runtime setting `StrictChangeBehaviour`. This setting is enabled by default. When this setting is enabled, trying to set an invalid value for an enum or a value for a calculated attribute will throw an `InvalidEnumerationValueException` or `ReadOnlyAttributeException` response, respectively. diff --git a/content/en/docs/releasenotes/studio-pro/10/10.6.md b/content/en/docs/releasenotes/studio-pro/10/10.6.md index 325bf3a7c65..7f9626663bc 100644 --- a/content/en/docs/releasenotes/studio-pro/10/10.6.md +++ b/content/en/docs/releasenotes/studio-pro/10/10.6.md @@ -881,7 +881,7 @@ For more information on how these actions work and their differences, see the [N ### Improvements * We fixed a [known issue](#ki-filedocuments) by reverting the [10.6.0 fix](#199712) for the issue where created files were not deleted when an error occurred in a microflow. (Ticket 199712) -* We reverted the [10.6.0 improvement](#changedBy) where the `changedBy` attribute was updated when `storeFileDocumentContent` or `storeImageDocumentContent` from the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/) was used or when uploading files. +* We reverted the [10.6.0 improvement](#changedBy) where the `changedBy` attribute was updated when `storeFileDocumentContent` or `storeImageDocumentContent` from the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-10/) was used or when uploading files. * We upgraded the Azure Blob Storage dependency from 8.6.6 to 12.25.1. ### Fixes @@ -1056,14 +1056,14 @@ We have also introduced an optimizing storage solution, which is triggered when * We fixed an issue in published web services where creating a mapping for custom authentication resulted in an error. * We now prohibit using "file" as a URL prefix in addition to the name of any known static folder in the deployment directory. * We fixed the "LibGit2Sharp.NotFoundException: No valid git object identified by 'some_branch' exists in the repository" error that occurred while cloning locally. -* The `changedBy` attribute is now updated when `storeFileDocumentContent` or `storeImageDocumentContent` from the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/) is used, or when uploading files. This makes it consistent with other ways of updating file documents or images. +* The `changedBy` attribute is now updated when `storeFileDocumentContent` or `storeImageDocumentContent` from the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-10/) is used, or when uploading files. This makes it consistent with other ways of updating file documents or images. * Reverted in [10.6.1](#revert-changedBy) ### Deprecations * We removed the deprecated **Import Web Service or XML Schema** wizard that was accessible in the domain model editor when you clicked **Import web service/XML file**. * The [Deep Link](/appstore/modules/deep-link/) module is now deprecated. We will end support for it with Studio Pro 11.0.0. We will continue to support the module for Studio Pro 8, 9, and 10 until the end of support for Studio Pro 10 LTS. The module is being deprecated because its functionality has been shifted to [page URLs](/refguide10/page-properties/#url) and [microflow URLs](/refguide10/microflow/#url). Using [these methods](/appstore/modules/deep-link/#migrate-page-micro), you can achieve what you used to accomplish with the Deep Link module in easier ways. -* We deprecated the `END_OF_PARALLEL_SPLIT_BRANCH` enumeration constant in the `WorkflowActivityType` in the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/). +* We deprecated the `END_OF_PARALLEL_SPLIT_BRANCH` enumeration constant in the `WorkflowActivityType` in the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-10/). * As of 10.6, Mendix has discontinued support for CodePush. If your app relies on CodePush for over-the-air updates, it will no longer be available. We recommend migrating your app to a version that supports [Mendix OTA](/refguide10/mobile/distributing-mobile-apps/overtheair-updates/) for managing over-the-air updates. ### Known Issues {#known-issues} diff --git a/content/en/docs/releasenotes/studio-pro/10/10.7.md b/content/en/docs/releasenotes/studio-pro/10/10.7.md index c62c5cd545c..aa8fa768d78 100644 --- a/content/en/docs/releasenotes/studio-pro/10/10.7.md +++ b/content/en/docs/releasenotes/studio-pro/10/10.7.md @@ -82,7 +82,7 @@ The [Events](/refguide10/workflow-properties/#events) setting is deprecated and * We improved the performance of populating tables and views metadata from all schemas accessible by the user in [External Database Connector](/appstore/modules/external-database-connector/). Searching for information within this tab was also improved. * External Database Connector has a new SQL Query text box which offers highlighting of SQL grammar and retains indentation and formatting after it is pasted into the component. It also now supports standard features such as copy, paste, undo, redo, find, and replace. * We improved the performance of a dangling reference cleanup procedure on the first run of a new app. -* We reintroduced the improvement that updates the `changedBy` attribute when `storeFileDocumentContent` or `storeImageDocumentContent` from the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/) is used, or when uploading files. This makes it consistent with other ways of updating file documents or images. +* We reintroduced the improvement that updates the `changedBy` attribute when `storeFileDocumentContent` or `storeImageDocumentContent` from the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-10/) is used, or when uploading files. This makes it consistent with other ways of updating file documents or images. * We improved the handling of errors that can occur when committing a `FileDocument`. ### Fixes @@ -107,7 +107,7 @@ The [Events](/refguide10/workflow-properties/#events) setting is deprecated and * We fixed an issue where MxAssist Logic Bot sent an invalid request to MARS. * We fixed an issue where MxAssist Logic Bot did not show newly created microflows and nanoflows in a suggestion list. * We fixed an issue that occurred when values entered into the new expression editor were not saved (for example, when generating an expression via the **Generate** button in the **Edit Change Item** dialog box). -* Using an empty collection in the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/)'s `XPathQuery.setVariable` or `IParameterMap.putCollection` no longer results in invalid SQL being generated. +* Using an empty collection in the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-10/)'s `XPathQuery.setVariable` or `IParameterMap.putCollection` no longer results in invalid SQL being generated. * We now validate the attribute names of business events on creation to avoid having the same name as an attribute from the generalization entities. * We fixed an issue in the microflow editor where the output variable on a **Cast object** activity was not set to the correct entity. * We fixed an issue in the logic editors where the return type of a list operation showed **(Not set)** when predefined variables were used. diff --git a/content/en/docs/releasenotes/studio-pro/9/9.0.md b/content/en/docs/releasenotes/studio-pro/9/9.0.md index b32ff7ba4c7..5f3d4ceee02 100644 --- a/content/en/docs/releasenotes/studio-pro/9/9.0.md +++ b/content/en/docs/releasenotes/studio-pro/9/9.0.md @@ -198,7 +198,7 @@ Thank you Rob Bartholomew for [thinking along](https://community.mendix.com/link #### Mendix Runtime API Improvements -In the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/), we made the following improvements: +In the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-9/), we made the following improvements: * We improved the performance of the `Core.getActiveSession(userName)` method. * We improved the type-checking on `Core.evaluateExpression()`. It now accepts exactly the same expressions as Studio Pro and no longer accepts other expressions that were wrongly accepted in previous versions. Concretely, the following constructs are no longer accepted (these changes apply only to `Core.evaluateExpression()`, as Studio Pro already does this): @@ -226,7 +226,7 @@ We do not consider hybrid mobile (which is based on Cordova) to be future-proof #### Mendix Runtime API Removals -In the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/), we made the following removals: +In the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-9/), we made the following removals: * We removed the `floatsEqual`, `currenciesEqual`, `parseFloat`, `formatFloat`, and `toFloat` functions, because the float and currencies types no longer exist. These functions were also removed from Studio Pro. * We removed the deprecated `IWebserviceResponse.getSaxSource` and `Configuration.getWebServiceCertificates` methods. @@ -279,7 +279,7 @@ In the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/), we made the fo * `MxContext.setTrackId` * `MxObject.compare` * `MxError` -* We replaced every mention of `CoreAction` in the public [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/) with `ICoreAction`: +* We replaced every mention of `CoreAction` in the public [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-9/) with `ICoreAction`: * `Context.getActionStack` now returns a stack of `ICoreAction` instead of `CoreAction`. * `CoreAction.getUnwrappedAction` now returns an `ICoreAction` instead of a `CoreAction`. * `CoreAction.call` is now final. diff --git a/content/en/docs/releasenotes/studio-pro/9/9.14.md b/content/en/docs/releasenotes/studio-pro/9/9.14.md index 4f111c1dfff..ed759f05bc2 100644 --- a/content/en/docs/releasenotes/studio-pro/9/9.14.md +++ b/content/en/docs/releasenotes/studio-pro/9/9.14.md @@ -104,7 +104,7 @@ weight: 86 * We fixed an issue in MySQL 8.0.29 and above that occurred when dropping a column with a name length over 53 characters long. * We fixed an issue that caused the entity path selector for an association property of a pluggable widget to allow invalid paths to be selected. * We fixed an issue where *.svn* or *.git* files were carried over to exported modules by blocklisting them. -* We fixed the `MetaAssociation#hasMetaDataAccess` function in the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/) to work for specializations of **System.User** as well. This might have an effect on which attributes can be shown in the UI. Whether the content of an attribute is readable has not changed. +* We fixed the `MetaAssociation#hasMetaDataAccess` function in the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-9/) to work for specializations of **System.User** as well. This might have an effect on which attributes can be shown in the UI. Whether the content of an attribute is readable has not changed. * We fixed an issue where the pop-up settings section in the properties dialog box for pages did not correctly show/hide when changing the layout type to or from a (modal) pop-up. ### Breaking Changes diff --git a/content/en/docs/releasenotes/studio-pro/9/9.23.md b/content/en/docs/releasenotes/studio-pro/9/9.23.md index 4f159c0e97f..749358ab11f 100644 --- a/content/en/docs/releasenotes/studio-pro/9/9.23.md +++ b/content/en/docs/releasenotes/studio-pro/9/9.23.md @@ -94,7 +94,7 @@ Now, we only ever initialize the active language. Also, Studio Pro will no longe ### Deprecations -* We deprecated methods only used internally in Mendix that had been exposed in the public [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/). +* We deprecated methods only used internally in Mendix that had been exposed in the public [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-9/). ### Known Issues diff --git a/content/en/docs/releasenotes/studio-pro/9/9.24.md b/content/en/docs/releasenotes/studio-pro/9/9.24.md index 16250d442c6..0561e39cb70 100644 --- a/content/en/docs/releasenotes/studio-pro/9/9.24.md +++ b/content/en/docs/releasenotes/studio-pro/9/9.24.md @@ -297,7 +297,7 @@ Through Open app settings or version control preferences, you can choose which c * We added `mx.exe`, `mxbuild.exe`, `mxutil.exe`, `MprTool.exe`, and `MendixConsoleLog.exe` tools to the Windows x64 and arm64 portable installers. (Ticket 202611) * We improved the performance of retrieving many objects for certain data sources, such as a microflow data source. -* The `changedBy` attribute is now updated when `storeFileDocumentContent` or `storeImageDocumentContent` from the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/) is used, or when uploading files. This makes it consistent with other ways of updating file documents or images. +* The `changedBy` attribute is now updated when `storeFileDocumentContent` or `storeImageDocumentContent` from the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-9/) is used, or when uploading files. This makes it consistent with other ways of updating file documents or images. * We improved handling of errors that can occur when committing `FileDocument`s. (Ticket 222633) * We added an [`mx show-java-version` command](/refguide9/mx-command-line-tool/#show-java-version) to the mx command-line tool. This command shows the configured Java version of the app. @@ -1248,7 +1248,7 @@ We made a major update to your business events modeling experience: ### Deprecations -In the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/), we did the following: +In the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api-9/), we did the following: * We deprecated `Core.isSubClassOf` method variants. You can instead use the `IMendixObject.isInstanceOf(IMetaObject)`, `IMendixObject.isInstanceOf(String)`, `IMetaObject.isSubClassOf(IMetaObject)`, or `IMetaObject.isSubClassOf(String)` methods to achieve the same result. * We deprecated the setter methods that were for internal use. We have provided constructors as an alternative for these methods wherever applicable.