From 6bd8451c88a9c4c15589ae1c1a5b162d6a5434b3 Mon Sep 17 00:00:00 2001 From: Petr Broz Date: Tue, 12 Oct 2021 14:18:40 +0200 Subject: [PATCH 01/33] Fixed VSCode naming. --- docs/deployment/azure/net.md | 2 +- docs/deployment/azure/node.md | 2 +- docs/deployment/heroku/netcore.md | 2 +- docs/environment/rundebug/go.md | 6 +++--- docs/environment/rundebug/php.md | 2 +- docs/environment/setup/go.md | 2 +- docs/environment/setup/nodejs.md | 2 +- docs/environment/setup/nodejs_2legged.md | 2 +- docs/environment/setup/nodejs_3legged.md | 2 +- docs/environment/setup/nodejs_da.md | 2 +- docs/environment/setup/php.md | 2 +- docs/environment/tools/go.md | 4 ++-- docs/environment/tools/net.md | 2 +- docs/environment/tools/netcore.md | 6 +++--- docs/environment/tools/php.md | 8 ++++---- 15 files changed, 23 insertions(+), 23 deletions(-) diff --git a/docs/deployment/azure/net.md b/docs/deployment/azure/net.md index be62822..4ee7893 100644 --- a/docs/deployment/azure/net.md +++ b/docs/deployment/azure/net.md @@ -38,7 +38,7 @@ Done! Click on the ```Site URL``` to see the app in action - Once we have the ```API App``` created, you can deploy from a local Git repo - see [here](deployment/azure/node) for details ### Other Deployment Options -- [Visual Code](https://azure.microsoft.com/en-us/blog/visual-studio-code-and-azure-app-service-a-perfect-fit/)/[Visual Studio](../node) +- [Visual Studio Code](https://azure.microsoft.com/en-us/blog/visual-studio-code-and-azure-app-service-a-perfect-fit/)/[Visual Studio](../node) - [VSTS](https://docs.microsoft.com/en-us/labs/devops/deployazurefunctionswithvsts/) - [Github](https://blogs.msdn.microsoft.com/benjaminperkins/2017/05/10/deploy-github-source-code-repositories-to-an-azure-app-service/) - [BitBucket](https://confluence.atlassian.com/bitbucket/deploy-to-microsoft-azure-900820699.html) diff --git a/docs/deployment/azure/node.md b/docs/deployment/azure/node.md index 82ae94c..33a8627 100644 --- a/docs/deployment/azure/node.md +++ b/docs/deployment/azure/node.md @@ -94,7 +94,7 @@ The app dashboard should look like: Done! Open the app url to see our app in action. **3. Other Deployment Options** -- [Visual Code](https://azure.microsoft.com/en-us/blog/visual-studio-code-and-azure-app-service-a-perfect-fit/)/[Visual Studio](../node) +- [Visual Studio Code](https://azure.microsoft.com/en-us/blog/visual-studio-code-and-azure-app-service-a-perfect-fit/)/[Visual Studio](../node) - [VSTS](https://docs.microsoft.com/en-us/labs/devops/deployazurefunctionswithvsts/) - [Github](https://blogs.msdn.microsoft.com/benjaminperkins/2017/05/10/deploy-github-source-code-repositories-to-an-azure-app-service/) - [BitBucket](https://confluence.atlassian.com/bitbucket/deploy-to-microsoft-azure-900820699.html) diff --git a/docs/deployment/heroku/netcore.md b/docs/deployment/heroku/netcore.md index c57cc87..a040b28 100644 --- a/docs/deployment/heroku/netcore.md +++ b/docs/deployment/heroku/netcore.md @@ -4,7 +4,7 @@ Let's use Github template for Visual Studio: https://github.com/github/gitignore/blob/master/VisualStudio.gitignore -> If using Visual Code, consider adding `.vscode` (that's the folder of your `launch.json` with environment variables). +> If using Visual Studio Code, consider adding `.vscode` (that's the folder of your `launch.json` with environment variables). [step2](/deployment/heroku/heroku_step2.md ':include :type=markdown') diff --git a/docs/environment/rundebug/go.md b/docs/environment/rundebug/go.md index 07be427..f1014ee 100644 --- a/docs/environment/rundebug/go.md +++ b/docs/environment/rundebug/go.md @@ -11,7 +11,7 @@ Open your browser and go to `http://localhost:3000` to check the app. ## Debug -For Visual Code, install [Delve](https://github.com/derekparker/delve), the golang debugger, by typing in on the **Integrated Terminal** (under menu **View**): +For Visual Studio Code, install [Delve](https://github.com/derekparker/delve), the golang debugger, by typing in on the **Integrated Terminal** (under menu **View**): ```bash go get -u github.com/derekparker/delve/cmd/dlv @@ -19,7 +19,7 @@ go get -u github.com/derekparker/delve/cmd/dlv Once `delve` is installed, you can either press F5 or go to menu **Debug** >> **Start debugging**. -!> For debug, make sure the `main.go` file is open on Visual Code before pessing **F5**, otherwise you may see an error (see [Troubleshooting](#troubleshooting)) +!> For debug, make sure the `main.go` file is open on Visual Studio Code before pessing **F5**, otherwise you may see an error (see [Troubleshooting](#troubleshooting)) You will now see a launch.json file created for your workspace, which will contain the configurations for debugging. By default, there would be a single configuration as below: @@ -67,7 +67,7 @@ If by any chance, you receive an error like `Can not debug non-main package`, do executables (g.e. daemon and client). If by any chance, you receive an error like `could not launch process: exec "lldb-server"`, then for sure you are developing on OSX and you are missing the `command line developer tools`. To fix this, just follow these steps: - 1. Open a new Terminal (do not use Visual Code Integrated Terminal) + 1. Open a new Terminal (do not use Visual Studio Code Integrated Terminal) 2. Run xcode-select --install 3. When prompted, click on Install ![](_media/go/osx_setup_tools.png) diff --git a/docs/environment/rundebug/php.md b/docs/environment/rundebug/php.md index 2793b8e..c323b08 100644 --- a/docs/environment/rundebug/php.md +++ b/docs/environment/rundebug/php.md @@ -1,6 +1,6 @@ # Running & Debugging (PHP) -Make sure **PHP Server** & **PHP Debug** extensions are installed in Visual Code, if not, please check the [**Tools**](environment/tools/php) section first. +Make sure **PHP Server** & **PHP Debug** extensions are installed in Visual Studio Code, if not, please check the [**Tools**](environment/tools/php) section first. ## Start/Stop server diff --git a/docs/environment/setup/go.md b/docs/environment/setup/go.md index 64536e4..27d664e 100644 --- a/docs/environment/setup/go.md +++ b/docs/environment/setup/go.md @@ -2,7 +2,7 @@ Inside `$GOPATH` create a `/src` folder for source code, do not use spaces and avoid special chars. Then a subfolder for this this tutorial: **forgesample**. The final result should be **$GOPATH/src/forgesample**. -Open **Visual Code**, then go to menu **File** and select **Open** (MacOS) or **Open Folder** (Windows) and select the newly created folder. +Open **Visual Studio Code**, then go to menu **File** and select **Open** (MacOS) or **Open Folder** (Windows) and select the newly created folder. ## Files and Folders diff --git a/docs/environment/setup/nodejs.md b/docs/environment/setup/nodejs.md index 074d65f..0bf8621 100644 --- a/docs/environment/setup/nodejs.md +++ b/docs/environment/setup/nodejs.md @@ -2,7 +2,7 @@ Create a folder on your machine, do not use spaces and avoid special chars. For this tutorial, let's use **forgesample**. -Open **Visual Code**, then go to menu **File** and select **Open** (MacOS) or **Open Folder** (Windows) and select the newly created folder. +Open **Visual Studio Code**, then go to menu **File** and select **Open** (MacOS) or **Open Folder** (Windows) and select the newly created folder. Now we need the terminal, go to menu **View** >> **Integrated Terminal**. A window should appear on the bottom. Type the following command and follow the steps. For consistency with other Forge samples, when prompted for **entry point:**, use **start.js**. diff --git a/docs/environment/setup/nodejs_2legged.md b/docs/environment/setup/nodejs_2legged.md index 360c75e..110fc38 100644 --- a/docs/environment/setup/nodejs_2legged.md +++ b/docs/environment/setup/nodejs_2legged.md @@ -2,7 +2,7 @@ Create a folder on your machine, do not use spaces and avoid special chars. For this tutorial, let's use **forgesample**. -Open **Visual Code**, then go to menu **File** and select **Open** (MacOS) or **Open Folder** (Windows) and select the newly created folder. +Open **Visual Studio Code**, then go to menu **File** and select **Open** (MacOS) or **Open Folder** (Windows) and select the newly created folder. Now we need the terminal, go to menu **View** >> **Integrated Terminal**. A window should appear on the bottom. Type the following command and follow the steps. For consistency with other Forge samples, when prompted for **entry point:**, use **start.js**. diff --git a/docs/environment/setup/nodejs_3legged.md b/docs/environment/setup/nodejs_3legged.md index 1d0f3a3..e4b7859 100644 --- a/docs/environment/setup/nodejs_3legged.md +++ b/docs/environment/setup/nodejs_3legged.md @@ -2,7 +2,7 @@ Create a folder on your machine, do not use spaces and avoid special chars. For this tutorial, let's use **forgesample**. -Open **Visual Code**, then go to menu **File** and select **Open** (MacOS) or **Open Folder** (Windows) and select the newly created folder. +Open **Visual Studio Code**, then go to menu **File** and select **Open** (MacOS) or **Open Folder** (Windows) and select the newly created folder. Now we need the terminal, go to menu **View** >> **Integrated Terminal**. A window should appear on the bottom. Type the following command and follow the steps. For consistency with other Forge samples, when prompted for **entry point:**, use **start.js**. diff --git a/docs/environment/setup/nodejs_da.md b/docs/environment/setup/nodejs_da.md index 51f3958..cc7c0a9 100644 --- a/docs/environment/setup/nodejs_da.md +++ b/docs/environment/setup/nodejs_da.md @@ -2,7 +2,7 @@ Create a folder on your machine, do not use spaces and avoid special chars. For this tutorial, let's use **forgesample**. -Open [Visual Code](https://code.visualstudio.com/download), then go to menu **File** and select **Open** (MacOS) or **Open Folder** (Windows) and select the newly created folder. +Open [Visual Studio Code](https://code.visualstudio.com/download), then go to menu **File** and select **Open** (MacOS) or **Open Folder** (Windows) and select the newly created folder. Now we need the terminal, go to menu **View** >> **Terminal**. A window should appear on the bottom. Type the following command and follow the steps, you can safely accept the default suggestion, except **entry point:**, use **start.js** (which is used on most of Forge samples). diff --git a/docs/environment/setup/php.md b/docs/environment/setup/php.md index aa8b885..c2fd137 100644 --- a/docs/environment/setup/php.md +++ b/docs/environment/setup/php.md @@ -2,7 +2,7 @@ Create a folder on your machine, do not use spaces and avoid special chars. For this tutorial, let's use **forgesample**. -Open **Visual Code**, then go to menu **File** and select **Open** (MacOS) or **Open Folder** (Windows) and select the newly created folder. +Open **Visual Studio Code**, then go to menu **File** and select **Open** (MacOS) or **Open Folder** (Windows) and select the newly created folder. Now we need the terminal, go to menu **View** >> **Integrated Terminal**. A window should appear on the bottom. Type the following command and follow the steps. diff --git a/docs/environment/tools/go.md b/docs/environment/tools/go.md index e024ee1..f3baa0d 100644 --- a/docs/environment/tools/go.md +++ b/docs/environment/tools/go.md @@ -21,8 +21,8 @@ Now we need an IDE to write the code. There are many options, this tutorial will > For this tutorial, use all default install options. -Next, install Go for Visual Code package which once installed gives support to GoLang. - - Go to Visual Code extension manager (left side, bottom icon) +Next, install Go for Visual Studio Code package which once installed gives support to GoLang. + - Go to Visual Studio Code extension manager (left side, bottom icon) - Type `go` and select `Go` plugin made by ***lukehoben***. ![](_media/go/install_go_extension.gif) diff --git a/docs/environment/tools/net.md b/docs/environment/tools/net.md index 78e32b1..dba6b0d 100644 --- a/docs/environment/tools/net.md +++ b/docs/environment/tools/net.md @@ -6,7 +6,7 @@ The .NET engine is a built-in feature on Windows machines. Any flavor of [Visual Studio](https://visualstudio.microsoft.com/vs/), this tutorial uses the **Community** edition. [Download the installer](https://visualstudio.microsoft.com/vs/) and follow the steps, make sure to select **ASP.NET and web development** and **.NET desktop development** (required for **Modify your models** tutorial). -!> For a better experience, DO NOT use Visual code on this tutorial. +!> For a better experience, DO NOT use Visual Studio Code on this tutorial. ![](_media/net/workloads_2019.png) diff --git a/docs/environment/tools/netcore.md b/docs/environment/tools/netcore.md index 98c72d0..4fefa1a 100644 --- a/docs/environment/tools/netcore.md +++ b/docs/environment/tools/netcore.md @@ -6,7 +6,7 @@ The .NET engine is a built-in feature on Windows machines, .NET Core is installe Any flavor of [Visual Studio](https://visualstudio.microsoft.com/vs/), this tutorial uses the **Community** edition. [Download the installer](https://visualstudio.microsoft.com/vs/) and follow the steps, make sure to select **ASP.NET and web development** and **.NET desktop development** (required for **Modify your models** tutorial). -!> For a better experience, DO NOT use Visual code on this tutorial. +!> For a better experience, DO NOT use Visual Studio Code on this tutorial. ![](_media/net/workloads_2019.png) @@ -23,9 +23,9 @@ For a better experience, use the 2019 version. If you must use older versions al > Make sure the [.NET Core 3.0 SDK](https://dotnet.microsoft.com/download) is installed. -## Visual Code (MacOS/Linux) +## Visual Studio Code (MacOS/Linux) -As an alternative solution, you may use [Visual Code](https://code.visualstudio.com/). This requires the **C#** extension. Remember to install [.NET Core 3.0 SDK and Runtime](https://dotnet.microsoft.com/download). +As an alternative solution, you may use [Visual Studio Code](https://code.visualstudio.com/). This requires the **C#** extension. Remember to install [.NET Core 3.0 SDK and Runtime](https://dotnet.microsoft.com/download). > Visual Studio (for Windows) is required to compile plugins for **Modify your models** tutorial. diff --git a/docs/environment/tools/php.md b/docs/environment/tools/php.md index 1119d1c..34644bc 100644 --- a/docs/environment/tools/php.md +++ b/docs/environment/tools/php.md @@ -16,17 +16,17 @@ Now we need an IDE to write the code. There are many options, this tutorial will > For this tutorial, use all default install options. -Finally, install the extensions of **PHP Server** and **PHP Debug** for Visual Code, -- Go to Visual Code extension manager (left side, bottom icon) +Finally, install the extensions of **PHP Server** and **PHP Debug** for Visual Studio Code, +- Go to Visual Studio Code extension manager (left side, bottom icon) - Type `PHP` and select/install `PHP Debug` & `PHP Server` plugin. ![](_media/php/vs_code_extension.png) - **PHP Server** extension can help host/serve current workspace (or subfolder) with PHP easily. -> If you have mulitple PHP versions installed, you can specify the location of your PHP executable by override the **phpserver.phpPath** in Visual Code **User Settings**. If empty, the extension will use the PHP executable which appears in the $PATH environment variable +> If you have mulitple PHP versions installed, you can specify the location of your PHP executable by override the **phpserver.phpPath** in Visual Studio Code **User Settings**. If empty, the extension will use the PHP executable which appears in the $PATH environment variable -- **PHP Debug** extension is a debug adapter between Visual Code and XDebug. +- **PHP Debug** extension is a debug adapter between Visual Studio Code and XDebug. > **PHP Debug** extension requires to configure with XDebug to make it work, please check the details at PHP Debug extension or a good [blog post about configuration](https://blogs.msdn.microsoft.com/nicktrog/2016/02/11/configuring-visual-studio-code-for-php-development/), and here is the brief steps: > 1. install PHP & xDebug as shown above. > 2. update your php.ini file with the following settings, make sure that zend_extension points to the correct location. From b2e24da715f58b70d7317d0bd59a0ac3cc9bcece Mon Sep 17 00:00:00 2001 From: Denis Grigor Date: Wed, 2 Feb 2022 14:55:42 -0500 Subject: [PATCH 02/33] added Ru localization --- docs/_navbar.md | 1 + docs/index.html | 1 + docs/ru-RU/README.md | 39 ++ docs/ru-RU/_coverpage.md | 12 + docs/ru-RU/_sidebar.md | 34 ++ docs/ru-RU/account/README.md | 38 ++ docs/ru-RU/datamanagement/hubs/net.md | 233 ++++++++ docs/ru-RU/datamanagement/hubs/netcore.md | 303 ++++++++++ docs/ru-RU/datamanagement/hubs/nodejs.md | 174 ++++++ docs/ru-RU/datamanagement/hubs/readme.md | 25 + docs/ru-RU/datamanagement/oss/README.md | 19 + docs/ru-RU/datamanagement/oss/go.md | 201 +++++++ docs/ru-RU/datamanagement/oss/java.md | 359 ++++++++++++ docs/ru-RU/datamanagement/oss/net.md | 169 ++++++ docs/ru-RU/datamanagement/oss/netcore.md | 170 ++++++ docs/ru-RU/datamanagement/oss/nodejs.md | 114 ++++ docs/ru-RU/datamanagement/oss/php.md | 134 +++++ docs/ru-RU/deployment/README.md | 37 ++ docs/ru-RU/deployment/aws/net.md | 28 + docs/ru-RU/deployment/azure/net.md | 65 +++ docs/ru-RU/deployment/azure/node.md | 118 ++++ docs/ru-RU/deployment/heroku/heroku_step1.md | 12 + docs/ru-RU/deployment/heroku/heroku_step2.md | 17 + docs/ru-RU/deployment/heroku/heroku_step3.md | 30 + docs/ru-RU/deployment/heroku/netcore.md | 18 + docs/ru-RU/deployment/heroku/nodejs.md | 16 + docs/ru-RU/deployment/heroku/php.md | 22 + .../ru-RU/designautomation/activity/README.md | 9 + .../designautomation/activity/netcore.md | 113 ++++ .../ru-RU/designautomation/activity/nodejs.md | 141 +++++ .../designautomation/appbundle/README.md | 25 + .../designautomation/appbundle/common.md | 7 + .../appbundle/engines/autocad.md | 186 +++++++ .../appbundle/engines/inventor.md | 139 +++++ .../designautomation/appbundle/engines/max.md | 269 +++++++++ .../appbundle/engines/revit.md | 197 +++++++ .../designautomation/appbundle/netcore.md | 213 +++++++ .../designautomation/appbundle/nodejs.md | 344 ++++++++++++ docs/ru-RU/designautomation/html/README.md | 7 + docs/ru-RU/designautomation/html/netcore.md | 274 +++++++++ docs/ru-RU/designautomation/html/nodejs.md | 304 ++++++++++ .../ru-RU/designautomation/workitem/README.md | 11 + .../designautomation/workitem/netcore.md | 171 ++++++ .../ru-RU/designautomation/workitem/nodejs.md | 221 ++++++++ docs/ru-RU/environment/rundebug/2legged.md | 26 + docs/ru-RU/environment/rundebug/2legged_da.md | 47 ++ docs/ru-RU/environment/rundebug/3legged.md | 15 + docs/ru-RU/environment/rundebug/go.md | 88 +++ docs/ru-RU/environment/rundebug/java.md | 21 + docs/ru-RU/environment/rundebug/net.md | 11 + docs/ru-RU/environment/rundebug/netcore.md | 11 + docs/ru-RU/environment/rundebug/nodejs.md | 11 + docs/ru-RU/environment/rundebug/nodejs_da.md | 11 + docs/ru-RU/environment/rundebug/php.md | 35 ++ docs/ru-RU/environment/rundebug/readme.md | 14 + docs/ru-RU/environment/setup/2legged.md | 9 + docs/ru-RU/environment/setup/2legged_da.md | 23 + docs/ru-RU/environment/setup/3legged.md | 7 + docs/ru-RU/environment/setup/go.md | 166 ++++++ docs/ru-RU/environment/setup/java.md | 320 +++++++++++ docs/ru-RU/environment/setup/net.md | 55 ++ docs/ru-RU/environment/setup/net_2legged.md | 7 + docs/ru-RU/environment/setup/net_3legged.md | 40 ++ .../ru-RU/environment/setup/net_controller.md | 15 + docs/ru-RU/environment/setup/netcore.md | 59 ++ .../environment/setup/netcore_2legged.md | 5 + .../environment/setup/netcore_2legged_da.md | 28 + .../environment/setup/netcore_3legged.md | 5 + docs/ru-RU/environment/setup/netcore_da.md | 142 +++++ docs/ru-RU/environment/setup/nodejs.md | 164 ++++++ .../ru-RU/environment/setup/nodejs_2legged.md | 161 ++++++ .../ru-RU/environment/setup/nodejs_3legged.md | 168 ++++++ docs/ru-RU/environment/setup/nodejs_da.md | 272 +++++++++ docs/ru-RU/environment/setup/php.md | 240 ++++++++ docs/ru-RU/environment/tools/README.md | 7 + docs/ru-RU/environment/tools/go.md | 40 ++ docs/ru-RU/environment/tools/java.md | 15 + docs/ru-RU/environment/tools/net.md | 26 + docs/ru-RU/environment/tools/netcore.md | 36 ++ docs/ru-RU/environment/tools/nodejs.md | 11 + docs/ru-RU/environment/tools/php.md | 44 ++ docs/ru-RU/help/README.md | 27 + .../ru-RU/modelderivative/translate/README.md | 13 + docs/ru-RU/modelderivative/translate/go.md | 71 +++ docs/ru-RU/modelderivative/translate/java.md | 170 ++++++ docs/ru-RU/modelderivative/translate/net.md | 68 +++ .../modelderivative/translate/netcore.md | 69 +++ .../ru-RU/modelderivative/translate/nodejs.md | 62 +++ docs/ru-RU/modelderivative/translate/php.md | 67 +++ docs/ru-RU/oauth/2legged/README.md | 11 + docs/ru-RU/oauth/2legged/go.md | 66 +++ docs/ru-RU/oauth/2legged/java.md | 167 ++++++ docs/ru-RU/oauth/2legged/net.md | 90 +++ docs/ru-RU/oauth/2legged/netcore.md | 90 +++ docs/ru-RU/oauth/2legged/nodejs.md | 93 ++++ docs/ru-RU/oauth/2legged/php.md | 90 +++ docs/ru-RU/oauth/3legged/README.md | 11 + docs/ru-RU/oauth/3legged/net.md | 183 ++++++ docs/ru-RU/oauth/3legged/netcore.md | 199 +++++++ docs/ru-RU/oauth/3legged/nodejs.md | 156 ++++++ docs/ru-RU/oauth/README.md | 34 ++ docs/ru-RU/oauth/user/net.md | 49 ++ docs/ru-RU/oauth/user/netcore.md | 49 ++ docs/ru-RU/oauth/user/nodejs.md | 33 ++ docs/ru-RU/oauth/user/readme.md | 7 + docs/ru-RU/tutorials/dashboard.md | 13 + docs/ru-RU/tutorials/extensions.md | 13 + docs/ru-RU/tutorials/modifymodels.md | 26 + docs/ru-RU/tutorials/viewhubmodels.md | 26 + docs/ru-RU/tutorials/viewmodels.md | 29 + docs/ru-RU/viewer/2legged/README.md | 7 + docs/ru-RU/viewer/2legged/go.md | 5 + docs/ru-RU/viewer/2legged/java.md | 6 + docs/ru-RU/viewer/2legged/net.md | 3 + docs/ru-RU/viewer/2legged/netcore.md | 7 + docs/ru-RU/viewer/2legged/nodejs.md | 5 + docs/ru-RU/viewer/2legged/php.md | 5 + docs/ru-RU/viewer/2legged/ui.md | 526 ++++++++++++++++++ docs/ru-RU/viewer/3legged/go.md | 3 + docs/ru-RU/viewer/3legged/net.md | 5 + docs/ru-RU/viewer/3legged/netcore.md | 7 + docs/ru-RU/viewer/3legged/nodejs.md | 5 + docs/ru-RU/viewer/3legged/php.md | 3 + docs/ru-RU/viewer/3legged/readme.md | 7 + docs/ru-RU/viewer/3legged/ui.md | 439 +++++++++++++++ docs/ru-RU/viewer/dashboard/2dviewer.md | 1 + docs/ru-RU/viewer/dashboard/charts.md | 142 +++++ docs/ru-RU/viewer/dashboard/layout.md | 92 +++ docs/ru-RU/viewer/dashboard/panelbasics.md | 143 +++++ docs/ru-RU/viewer/extensions/examples.md | 23 + docs/ru-RU/viewer/extensions/panel.md | 193 +++++++ docs/ru-RU/viewer/extensions/selection.md | 151 +++++ docs/ru-RU/viewer/extensions/skeleton.md | 106 ++++ docs/ru-RU/viewer/go.md | 12 + docs/ru-RU/viewer/java.md | 15 + docs/ru-RU/viewer/net.md | 16 + docs/ru-RU/viewer/netcore.md | 10 + docs/ru-RU/viewer/nodejs.md | 12 + docs/ru-RU/viewer/php.md | 12 + 139 files changed, 11318 insertions(+) create mode 100644 docs/ru-RU/README.md create mode 100644 docs/ru-RU/_coverpage.md create mode 100644 docs/ru-RU/_sidebar.md create mode 100644 docs/ru-RU/account/README.md create mode 100644 docs/ru-RU/datamanagement/hubs/net.md create mode 100644 docs/ru-RU/datamanagement/hubs/netcore.md create mode 100644 docs/ru-RU/datamanagement/hubs/nodejs.md create mode 100644 docs/ru-RU/datamanagement/hubs/readme.md create mode 100644 docs/ru-RU/datamanagement/oss/README.md create mode 100644 docs/ru-RU/datamanagement/oss/go.md create mode 100644 docs/ru-RU/datamanagement/oss/java.md create mode 100644 docs/ru-RU/datamanagement/oss/net.md create mode 100644 docs/ru-RU/datamanagement/oss/netcore.md create mode 100644 docs/ru-RU/datamanagement/oss/nodejs.md create mode 100644 docs/ru-RU/datamanagement/oss/php.md create mode 100644 docs/ru-RU/deployment/README.md create mode 100644 docs/ru-RU/deployment/aws/net.md create mode 100644 docs/ru-RU/deployment/azure/net.md create mode 100644 docs/ru-RU/deployment/azure/node.md create mode 100644 docs/ru-RU/deployment/heroku/heroku_step1.md create mode 100644 docs/ru-RU/deployment/heroku/heroku_step2.md create mode 100644 docs/ru-RU/deployment/heroku/heroku_step3.md create mode 100644 docs/ru-RU/deployment/heroku/netcore.md create mode 100644 docs/ru-RU/deployment/heroku/nodejs.md create mode 100644 docs/ru-RU/deployment/heroku/php.md create mode 100644 docs/ru-RU/designautomation/activity/README.md create mode 100644 docs/ru-RU/designautomation/activity/netcore.md create mode 100644 docs/ru-RU/designautomation/activity/nodejs.md create mode 100644 docs/ru-RU/designautomation/appbundle/README.md create mode 100644 docs/ru-RU/designautomation/appbundle/common.md create mode 100644 docs/ru-RU/designautomation/appbundle/engines/autocad.md create mode 100644 docs/ru-RU/designautomation/appbundle/engines/inventor.md create mode 100644 docs/ru-RU/designautomation/appbundle/engines/max.md create mode 100644 docs/ru-RU/designautomation/appbundle/engines/revit.md create mode 100644 docs/ru-RU/designautomation/appbundle/netcore.md create mode 100644 docs/ru-RU/designautomation/appbundle/nodejs.md create mode 100644 docs/ru-RU/designautomation/html/README.md create mode 100644 docs/ru-RU/designautomation/html/netcore.md create mode 100644 docs/ru-RU/designautomation/html/nodejs.md create mode 100644 docs/ru-RU/designautomation/workitem/README.md create mode 100644 docs/ru-RU/designautomation/workitem/netcore.md create mode 100644 docs/ru-RU/designautomation/workitem/nodejs.md create mode 100644 docs/ru-RU/environment/rundebug/2legged.md create mode 100644 docs/ru-RU/environment/rundebug/2legged_da.md create mode 100644 docs/ru-RU/environment/rundebug/3legged.md create mode 100644 docs/ru-RU/environment/rundebug/go.md create mode 100644 docs/ru-RU/environment/rundebug/java.md create mode 100644 docs/ru-RU/environment/rundebug/net.md create mode 100644 docs/ru-RU/environment/rundebug/netcore.md create mode 100644 docs/ru-RU/environment/rundebug/nodejs.md create mode 100644 docs/ru-RU/environment/rundebug/nodejs_da.md create mode 100644 docs/ru-RU/environment/rundebug/php.md create mode 100644 docs/ru-RU/environment/rundebug/readme.md create mode 100644 docs/ru-RU/environment/setup/2legged.md create mode 100644 docs/ru-RU/environment/setup/2legged_da.md create mode 100644 docs/ru-RU/environment/setup/3legged.md create mode 100644 docs/ru-RU/environment/setup/go.md create mode 100644 docs/ru-RU/environment/setup/java.md create mode 100644 docs/ru-RU/environment/setup/net.md create mode 100644 docs/ru-RU/environment/setup/net_2legged.md create mode 100644 docs/ru-RU/environment/setup/net_3legged.md create mode 100644 docs/ru-RU/environment/setup/net_controller.md create mode 100644 docs/ru-RU/environment/setup/netcore.md create mode 100644 docs/ru-RU/environment/setup/netcore_2legged.md create mode 100644 docs/ru-RU/environment/setup/netcore_2legged_da.md create mode 100644 docs/ru-RU/environment/setup/netcore_3legged.md create mode 100644 docs/ru-RU/environment/setup/netcore_da.md create mode 100644 docs/ru-RU/environment/setup/nodejs.md create mode 100644 docs/ru-RU/environment/setup/nodejs_2legged.md create mode 100644 docs/ru-RU/environment/setup/nodejs_3legged.md create mode 100644 docs/ru-RU/environment/setup/nodejs_da.md create mode 100644 docs/ru-RU/environment/setup/php.md create mode 100644 docs/ru-RU/environment/tools/README.md create mode 100644 docs/ru-RU/environment/tools/go.md create mode 100644 docs/ru-RU/environment/tools/java.md create mode 100644 docs/ru-RU/environment/tools/net.md create mode 100644 docs/ru-RU/environment/tools/netcore.md create mode 100644 docs/ru-RU/environment/tools/nodejs.md create mode 100644 docs/ru-RU/environment/tools/php.md create mode 100644 docs/ru-RU/help/README.md create mode 100644 docs/ru-RU/modelderivative/translate/README.md create mode 100644 docs/ru-RU/modelderivative/translate/go.md create mode 100644 docs/ru-RU/modelderivative/translate/java.md create mode 100644 docs/ru-RU/modelderivative/translate/net.md create mode 100644 docs/ru-RU/modelderivative/translate/netcore.md create mode 100644 docs/ru-RU/modelderivative/translate/nodejs.md create mode 100644 docs/ru-RU/modelderivative/translate/php.md create mode 100644 docs/ru-RU/oauth/2legged/README.md create mode 100644 docs/ru-RU/oauth/2legged/go.md create mode 100644 docs/ru-RU/oauth/2legged/java.md create mode 100644 docs/ru-RU/oauth/2legged/net.md create mode 100644 docs/ru-RU/oauth/2legged/netcore.md create mode 100644 docs/ru-RU/oauth/2legged/nodejs.md create mode 100644 docs/ru-RU/oauth/2legged/php.md create mode 100644 docs/ru-RU/oauth/3legged/README.md create mode 100644 docs/ru-RU/oauth/3legged/net.md create mode 100644 docs/ru-RU/oauth/3legged/netcore.md create mode 100644 docs/ru-RU/oauth/3legged/nodejs.md create mode 100644 docs/ru-RU/oauth/README.md create mode 100644 docs/ru-RU/oauth/user/net.md create mode 100644 docs/ru-RU/oauth/user/netcore.md create mode 100644 docs/ru-RU/oauth/user/nodejs.md create mode 100644 docs/ru-RU/oauth/user/readme.md create mode 100644 docs/ru-RU/tutorials/dashboard.md create mode 100644 docs/ru-RU/tutorials/extensions.md create mode 100644 docs/ru-RU/tutorials/modifymodels.md create mode 100644 docs/ru-RU/tutorials/viewhubmodels.md create mode 100644 docs/ru-RU/tutorials/viewmodels.md create mode 100644 docs/ru-RU/viewer/2legged/README.md create mode 100644 docs/ru-RU/viewer/2legged/go.md create mode 100644 docs/ru-RU/viewer/2legged/java.md create mode 100644 docs/ru-RU/viewer/2legged/net.md create mode 100644 docs/ru-RU/viewer/2legged/netcore.md create mode 100644 docs/ru-RU/viewer/2legged/nodejs.md create mode 100644 docs/ru-RU/viewer/2legged/php.md create mode 100644 docs/ru-RU/viewer/2legged/ui.md create mode 100644 docs/ru-RU/viewer/3legged/go.md create mode 100644 docs/ru-RU/viewer/3legged/net.md create mode 100644 docs/ru-RU/viewer/3legged/netcore.md create mode 100644 docs/ru-RU/viewer/3legged/nodejs.md create mode 100644 docs/ru-RU/viewer/3legged/php.md create mode 100644 docs/ru-RU/viewer/3legged/readme.md create mode 100644 docs/ru-RU/viewer/3legged/ui.md create mode 100644 docs/ru-RU/viewer/dashboard/2dviewer.md create mode 100644 docs/ru-RU/viewer/dashboard/charts.md create mode 100644 docs/ru-RU/viewer/dashboard/layout.md create mode 100644 docs/ru-RU/viewer/dashboard/panelbasics.md create mode 100644 docs/ru-RU/viewer/extensions/examples.md create mode 100644 docs/ru-RU/viewer/extensions/panel.md create mode 100644 docs/ru-RU/viewer/extensions/selection.md create mode 100644 docs/ru-RU/viewer/extensions/skeleton.md create mode 100644 docs/ru-RU/viewer/go.md create mode 100644 docs/ru-RU/viewer/java.md create mode 100644 docs/ru-RU/viewer/net.md create mode 100644 docs/ru-RU/viewer/netcore.md create mode 100644 docs/ru-RU/viewer/nodejs.md create mode 100644 docs/ru-RU/viewer/php.md diff --git a/docs/_navbar.md b/docs/_navbar.md index d1df028..0e71f2c 100644 --- a/docs/_navbar.md +++ b/docs/_navbar.md @@ -2,3 +2,4 @@ * [JA](/ja-JP/) * [ZH-CN](/zh-CN/) * [ZH-TW](/zh-TW/) +* [RU](/ru-RU/) diff --git a/docs/index.html b/docs/index.html index a526211..5dfc68f9 100644 --- a/docs/index.html +++ b/docs/index.html @@ -30,6 +30,7 @@ '/ja-JP/.*_sidebar.md': '/ja-JP/_sidebar.md', '/zh-CN/.*_sidebar.md': '/zh-CN/_sidebar.md', '/zh-TW/.*_sidebar.md': '/zh-TW/_sidebar.md', + '/ru-RU/.*_sidebar.md': '/ru-RU/_sidebar.md', '/.*/_sidebar.md': '/_sidebar.md' }, maxLevel: 4, diff --git a/docs/ru-RU/README.md b/docs/ru-RU/README.md new file mode 100644 index 0000000..fcae741 --- /dev/null +++ b/docs/ru-RU/README.md @@ -0,0 +1,39 @@ +# Образовательный курс по платформе Autodesk Forge + +Познакомьтесь с основами работы платформы Autodesk Forge (аутентификация, управление данными, конвертация файлов, визуализаця моделей в вебе и т.д.), используя наши тематические руководства. + +**ВАЖНО: Этот курс является переведённой версией англоязычного руководства Learn Autodesk Forge https://learnforge.autodesk.io, там вы всегда найдете самую актуальную информацию. Тем не менее, мы тщательно отслеживаем обновления и стараемся оперативно перевести и добавить их в русскоязычное руководство.** + +**Если вы найдете ошибки/неточности в переводе, пожалуйста, направьте запрос нашим специалистам [www.autodesk.ru/adn_contacts](https://www.autodesk.ru/autodesk-developer-network/contacts).** + +## Что такое Autodesk Forge? + +Платформа Autodesk Forge - это набор облачных API (Application Programming Interface), на базе которых компании могут создавать инженерные веб-сервисы и приложения в области цифрового производства, анимации и графики, архитектуры, инфраструктуры и строительства. + +Autodesk Forge предоставляет программистам инструменты для веб-разработки. + +С помощью Autodesk Forge вы можете: +- **Просматривать 3D-модели в облаке**: Viewer API позволяет визуализировать информационные модели и извлекать из них метаданные прямо в вашем браузере без необходимости устанавливать дополнительное программное обеспечение (на вход принимается 60+ форматов файлов). +- **Централизованно управлять данными**: Data Management API позволяет вам получать доступ к репозиториям данных A360, Fusion360, BIM360 и Object Storage Service (OSS). +- **Конвертировать проектные файлы в 60+ других отраслевых форматов**: Используя Model Derivative API, вы сможете подготовить ваши файлы для дальнейшего просмотра в браузере, извлечения геометрии, метаданных и т.д. (на вход принимается более 60 стандартных отраслевых форматов). + +... и многое другое. + +## Содержание + +- [Первые шаги](/ru-RU/account/): создание и активация учетной записи. +- [Веб-технологии](/ru-RU/environment/tools/): технологии, которые помогут вам разработать веб-сервисы с использованием Autodesk Forge. +- [Аутентификация (OAuth)](/ru-RU/oauth/): кратко о безопасности и аутентификации. +- Пошаговые руководства: + - [Визуализация моделей](/ru-RU/tutorials/viewmodels): загрузка и визуализация 3D-моделей в браузере. + - [Просмотр моделей из репозиториев Autodesk BIM 360 & Fusion 360](/ru-RU/tutorials/viewhubmodels): доступ к данным из BIM 360 & Fusion 360 и визуализация моделей в вашем веб-приложении. + - [Редактирование моделей в вебе](): изменение данных в файлах .dwg, .ipt, .rvt и .max прямо в облаке. +- [Запуск и проверка кода](/ru-RU/environment/rundebug/readme.md): выполнение кода и советы по его проверке. +- [Расширения для Viewer](/ru-RU/tutorials/extensions.md): добавление дополнительных функций в Forge Viewer (кнопки, панели, дополнительные поля, цветовые схемы и т.п.). +- [Развертывание](/ru-RU/deployment/): Пошаговое развертывание кода с помощью **AWS**, **Azure**, **Heroku** и **AppHarbor**. + +Готовы начать? + +Далее: [Первые шаги](/ru-RU/account/) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/?id=learn-autodesk-forge). diff --git a/docs/ru-RU/_coverpage.md b/docs/ru-RU/_coverpage.md new file mode 100644 index 0000000..1f8cd24 --- /dev/null +++ b/docs/ru-RU/_coverpage.md @@ -0,0 +1,12 @@ +![Autodesk Forge](_media/logo.png) + +# Autodesk Forge + +> Powering the Future of Making Things. + +- Просмотр 2D и 3D-моделей в браузере +- Конвертация проектных файлов и выгрузка ценных данных +- Обновление моделей в облаке + +[Сайт платформы Autodesk Forge](http://developer.autodesk.com) +[Первые шаги](#learn-autodesk-forge) diff --git a/docs/ru-RU/_sidebar.md b/docs/ru-RU/_sidebar.md new file mode 100644 index 0000000..039e445 --- /dev/null +++ b/docs/ru-RU/_sidebar.md @@ -0,0 +1,34 @@ +- [Главная страница](/ru-RU/?id=learn-autodesk-forge) +- [Первые шаги](/ru-RU/account/) +- [Веб-технологии](/ru-RU/environment/tools/) +- [Аутентификация (OAuth)](/ru-RU/oauth/) +- [Визуализация моделей](/ru-RU/tutorials/viewmodels.md) + - [Настройка сервера](/ru-RU/environment/setup/2legged) + - [Аутентификация](/ru-RU/oauth/2legged/) + - [Загрузка файлов в OSS (object storage service)](/ru-RU/datamanagement/oss/) + - [Конвертация файлов](/ru-RU/modelderivative/translate/) + - [Отображение файлов в Viewer](/ru-RU/viewer/2legged/) +- [Просмотр моделей из репозиториев Autodesk BIM 360 & Fusion 360](/ru-RU/tutorials/viewhubmodels.md) + - [Настройка сервера](/ru-RU/environment/setup/3legged) + - [Аутентификация](/ru-RU/oauth/3legged/) + - [Указание репозиториев данных и проектов](/ru-RU/datamanagement/hubs/readme.md) + - [Обработка информации профиля пользователя](/ru-RU/oauth/user/readme.md) + - [Отображение файлов в Viewer](/ru-RU/viewer/3legged/readme.md) +- [Редактирование моделей в вебе](/ru-RU/tutorials/modifymodels.md) + - [Настройка сервера](/ru-RU/environment/setup/2legged_da.md) + - [Базовый пользовательский интерфейс (UI)](/ru-RU/designautomation/html/) + - [Подготовка плагина](/ru-RU/designautomation/appbundle/) + - [Определение Activity](/ru-RU/designautomation/activity/) + - [Запуск Workitem](/ru-RU/designautomation/workitem/) +- [Запуск и проверка кода](/ru-RU/environment/rundebug/readme.md) +- [Расширения для Viewer](/ru-RU/tutorials/extensions.md) + - [Базовая структура расширения](/ru-RU/viewer/extensions/skeleton) + - [Выделение элементов](/ru-RU/viewer/extensions/selection) + - [Панель свойств в интерфейсе](/ru-RU/viewer/extensions/panel) + - [Примеры](/ru-RU/viewer/extensions/examples) +- [Создание dashboard](/ru-RU/tutorials/dashboard.md) + - [Макет веб-приложения](/ru-RU/viewer/dashboard/layout) + - [Подготовка данных](/ru-RU/viewer/dashboard/panelbasics) + - [Добавление графиков](/ru-RU/viewer/dashboard/charts) +- [Развертывание](/ru-RU/deployment/) +- [Техподдержка и полезные онлайн-ресурсы](/ru-RU/help/) diff --git a/docs/ru-RU/account/README.md b/docs/ru-RU/account/README.md new file mode 100644 index 0000000..2aa92c8 --- /dev/null +++ b/docs/ru-RU/account/README.md @@ -0,0 +1,38 @@ +# Аккаунт Autodesk + +Для использования платформы Autodesk Forge вам нужно создать аккаунт на сайте платформы [https://forge.autodesk.com](https://forge.autodesk.com). + +## Создание аккаунта Forge + +Перейдите на сайт платформы [Autodesk Forge](https://forge.autodesk.com/), нажмите “SIGN UP” для создания нового аккаунта или "SIGN IN" для входа в уже существующий. Если вы создаете новый аккаунт, не забудьте подтвердить вашу электронную почту, пройдя по отправленной вам в письме ссылке. + +![](/_media/forge/dev_portal_home.png) + +## Приобретение подписки + +Обратите внимание, что при первой авторизации на сайте вы получаете доступ к бесплатному пробному периоду (Trial), который длится 90 дней и включает в себя приветственный пакет из 100 cloud credits, а также 5 ГБ облачного пространства. При этом вы получаете полный доступ ко всем компонентам платформы Forge (включая платные, например, **Model Derivative**). + +Для активации пробного периода перейдите в раздел **My Subscription** (рус. мои подписки) и нажмите на **START FREE TRIAL** (рус. начать бесплатный пробный период). + +![](_media/account/activate_sub.png) + +По истечении пробного периода вам нужно приобрести подписку Autodesk Forge. +Все вопросы по процессу оплаты за Autodesk Forge в России и СНГ вы можете напрявлять нашим специалистам [https://autodesk.ru/adn_contacts](https://autodesk.ru/adn_contacts). + +## Создание приложения + +Наведите курсор на правый верхний угол страницы - вы увидите ваше имя и меню вашего аккаунта. Перейдите в раздел **My Apps** (рус. мои приложения) и нажмите на “CREATE APP” (рус. создать приложение). + +Выберите те API, которые вы планируете использовать для создания вашего веб-сервиса (на этом этапе вы можете выбрать все компоненты). Введите название вашего приложения, его описание и callback URL (рус. ссылка обратного вызова): `http://localhost:3000/api/forge/callback/oauth` (в этом руководстве мы не используем этот callback URL, но он используется в других примерах кода Autodesk Forge). + +Как только вы подготовите ваше приложение, вы получите Client ID и Client Secret, которые появятся на странице созданного приложения. Эти ключи понадобятся вам для дальнейшего использования платформы Autodesk Forge (процедура аутентификации) и, в частности, для прохождения этого курса. + +![](_media/account/create_app.gif) + +! **Всегда помните, что Client Secret - это конфиденциальные данные, которые доступны только владельцу аккаунта Autodesk Forge. НИКОГДА не делитесь вашими Client Secret. Если они попали к 3-му лицу, незамедлительно обновите Client Secret в вашем аккаунте.** + +Поздравляем, настройка вашего аккаунта завершена! + +Далее: [Веб-технологии](/ru-RU/environment/tools/) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/account/). diff --git a/docs/ru-RU/datamanagement/hubs/net.md b/docs/ru-RU/datamanagement/hubs/net.md new file mode 100644 index 0000000..fe11577 --- /dev/null +++ b/docs/ru-RU/datamanagement/hubs/net.md @@ -0,0 +1,233 @@ +# Репозитории данных и проекты (.NET Framework) + +## DataManagementController.cs + +Создайте .NET WebAPI Controller с именем **DataManagementController** (см. [как создать контроллер](/ru-RU/environment/setup/net_controller)) и добавьте следующий код: + +> Сначала появятся ошибки, но позже мы их исправим. + +```csharp +using Autodesk.Forge; +using Autodesk.Forge.Model; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Web.Http; + +namespace forgeSample.Controllers +{ + public class DataManagementController : ApiController + { + /// + /// Credentials on this request + /// + private Credentials Credentials { get; set; } + + /// + /// GET TreeNode passing the ID + /// + [HttpGet] + [Route("api/forge/datamanagement")] + public async Task> GetTreeNodeAsync([FromUri]string id) + { + Credentials = await Credentials.FromSessionAsync(); + if (Credentials == null) + { + return null; + } + + IList nodes = new List(); + + if (id == "#") // root + return await GetHubsAsync(); + else + { + string[] idParams = id.Split('/'); + string resource = idParams[idParams.Length - 2]; + switch (resource) + { + case "hubs": // hubs node selected/expanded, show projects + return await GetProjectsAsync(id); + case "projects": // projects node selected/expanded, show root folder contents + return await GetProjectContents(id); + case "folders": // folders node selected/expanded, show folder contents + return await GetFolderContents(id); + case "items": + return await GetItemVersions(id); + } + } + + return nodes; + } + } +} +``` + +Код выше получает запрос от дерева пользовательского интерфейса (англ. UI tree). Параметр `id` указывает узел, который расширяется: `#`означает корневой узел, поэтому укажите репозитории. После этого он содержит `href` ресурса, поэтому при расширении одного `репозитория` конечная точка должна возвращать все проекты, которые в нем сожержатся. Приведенный выше код вызывает разные функции `get`. Для его завершения также скопируйте следующий код в файл (внутри того же класса `DataManagementController`). + +```csharp +private async Task> GetHubsAsync() +{ + IList nodes = new List(); + + // the API SDK + HubsApi hubsApi = new HubsApi(); + hubsApi.Configuration.AccessToken = Credentials.TokenInternal; + + var hubs = await hubsApi.GetHubsAsync(); + foreach (KeyValuePair hubInfo in new DynamicDictionaryItems(hubs.data)) + { + // check the type of the hub to show an icon + string nodeType = "hubs"; + switch ((string)hubInfo.Value.attributes.extension.type) + { + case "hubs:autodesk.core:Hub": + nodeType = "hubs"; + break; + case "hubs:autodesk.a360:PersonalHub": + nodeType = "personalHub"; + break; + case "hubs:autodesk.bim360:Account": + nodeType = "bim360Hubs"; + break; + } + + // create a treenode with the values + jsTreeNode hubNode = new jsTreeNode(hubInfo.Value.links.self.href, hubInfo.Value.attributes.name, nodeType, true); + nodes.Add(hubNode); + } + + return nodes; +} + +private async Task> GetProjectsAsync(string href) +{ + IList nodes = new List(); + + // the API SDK + ProjectsApi projectsApi = new ProjectsApi(); + projectsApi.Configuration.AccessToken = Credentials.TokenInternal; + + // extract the hubId from the href + string[] idParams = href.Split('/'); + string hubId = idParams[idParams.Length - 1]; + + var projects = await projectsApi.GetHubProjectsAsync(hubId); + foreach (KeyValuePair projectInfo in new DynamicDictionaryItems(projects.data)) + { + // check the type of the project to show an icon + string nodeType = "projects"; + switch ((string)projectInfo.Value.attributes.extension.type) + { + case "projects:autodesk.core:Project": + nodeType = "a360projects"; + break; + case "projects:autodesk.bim360:Project": + nodeType = "bim360projects"; + break; + } + + // create a treenode with the values + jsTreeNode projectNode = new jsTreeNode(projectInfo.Value.links.self.href, projectInfo.Value.attributes.name, nodeType, true); + nodes.Add(projectNode); + } + + return nodes; +} + +private async Task> GetProjectContents(string href) +{ + IList nodes = new List(); + + // the API SDK + ProjectsApi projectApi = new ProjectsApi(); + projectApi.Configuration.AccessToken = Credentials.TokenInternal; + + // extract the hubId & projectId from the href + string[] idParams = href.Split('/'); + string hubId = idParams[idParams.Length - 3]; + string projectId = idParams[idParams.Length - 1]; + + var folders = await projectApi.GetProjectTopFoldersAsync(hubId, projectId); + foreach (KeyValuePair folder in new DynamicDictionaryItems(folders.data)) + { + nodes.Add(new jsTreeNode(folder.Value.links.self.href, folder.Value.attributes.displayName, "folders", true)); + } + return nodes; +} + +private async Task> GetFolderContents(string href) +{ + IList nodes = new List(); + + // the API SDK + FoldersApi folderApi = new FoldersApi(); + folderApi.Configuration.AccessToken = Credentials.TokenInternal; + + // extract the projectId & folderId from the href + string[] idParams = href.Split('/'); + string folderId = idParams[idParams.Length - 1]; + string projectId = idParams[idParams.Length - 3]; + + var folderContents = await folderApi.GetFolderContentsAsync(projectId, folderId); + foreach (KeyValuePair folderContentItem in new DynamicDictionaryItems(folderContents.data)) + { + string displayName = folderContentItem.Value.attributes.displayName; + jsTreeNode itemNode = new jsTreeNode(folderContentItem.Value.links.self.href, displayName, (string)folderContentItem.Value.type, true); + nodes.Add(itemNode); + } + + return nodes; +} + +private async Task> GetItemVersions(string href) +{ + IList nodes = new List(); + + // the API SDK + ItemsApi itemApi = new ItemsApi(); + itemApi.Configuration.AccessToken = Credentials.TokenInternal; + + // extract the projectId & itemId from the href + string[] idParams = href.Split('/'); + string itemId = idParams[idParams.Length - 1]; + string projectId = idParams[idParams.Length - 3]; + + var versions = await itemApi.GetItemVersionsAsync(projectId, itemId); + foreach (KeyValuePair version in new DynamicDictionaryItems(versions.data)) + { + DateTime versionDate = version.Value.attributes.lastModifiedTime; + string urn = string.Empty; + try { urn = (string)version.Value.relationships.derivatives.data.id; } + catch { urn = "not_available"; } // some BIM 360 versions don't have viewable + jsTreeNode node = new jsTreeNode(urn, versionDate.ToString("dd/MM/yy HH:mm:ss"), "versions", false); + nodes.Add(node); + } + + return nodes; +} + +public class jsTreeNode +{ + public jsTreeNode(string id, string text, string type, bool children) + { + this.id = id; + this.text = text; + this.type = type; + this.children = children; + } + + public string id { get; set; } + public string text { get; set; } + public string type { get; set; } + public bool children { get; set; } +} +``` + +Последняя функция `get` возвращает **Версии** для каждого элемента (файла), где свойство `.relationships.derivatives.data.id` содержит `URN` для **Viewer**. Важно проверить, доступен ли этот атрибут, поскольку некоторые элементы могут не отображаться (например, ZIP-файлы или файлы .DOCx) или еще не прошли конвертацию. + +Обратите внимание, что мы повторно используем `Credentials`, представленные через свойство. + +Далее: [Обработка информации профиля пользователя](/ru-RU/oauth/user/readme) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/datamanagement/hubs/net). diff --git a/docs/ru-RU/datamanagement/hubs/netcore.md b/docs/ru-RU/datamanagement/hubs/netcore.md new file mode 100644 index 0000000..13a05a5 --- /dev/null +++ b/docs/ru-RU/datamanagement/hubs/netcore.md @@ -0,0 +1,303 @@ +# Репозитории данных и проекты (.NET Core) + +## DataManagementController.cs + +В папке **Controllers** создайте класс **DataManagementController** в папке класса с тем же именем (`DataManagementController.cs`) и добавьте следующий код: + +> Сначала появятся ошибки, но позже мы их исправим. + +```csharp +using Autodesk.Forge; +using Autodesk.Forge.Model; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; + +namespace forgeSample.Controllers +{ + public class DataManagementController : ControllerBase + { + /// + /// Credentials on this request + /// + private Credentials Credentials { get; set; } + + /// + /// GET TreeNode passing the ID + /// + [HttpGet] + [Route("api/forge/datamanagement")] + public async Task> GetTreeNodeAsync(string id) + { + Credentials = await Credentials.FromSessionAsync(base.Request.Cookies, Response.Cookies); + if (Credentials == null) { return null; } + + IList nodes = new List(); + + if (id == "#") // root + return await GetHubsAsync(); + else + { + string[] idParams = id.Split('/'); + string resource = idParams[idParams.Length - 2]; + switch (resource) + { + case "hubs": // hubs node selected/expanded, show projects + return await GetProjectsAsync(id); + case "projects": // projects node selected/expanded, show root folder contents + return await GetProjectContents(id); + case "folders": // folders node selected/expanded, show folder contents + return await GetFolderContents(id); + case "items": + return await GetItemVersions(id); + } + } + + return nodes; + } + } +} +``` + + +Код выше получает запрос от дерева пользовательского интерфейса (англ. UI tree). Параметр `id` указывает узел, который расширяется: `#`означает корневой узел, поэтому укажите репозитории. После этого он содержит `href` ресурса, поэтому при расширении одного `репозитория` конечная точка должна возвращать все проекты, которые в нем сожержатся. Приведенный выше код вызывает разные функции `get`. Для его завершения также скопируйте следующий код в файл (внутри того же класса `DataManagementController`). + + +```csharp +private async Task> GetHubsAsync() +{ + IList nodes = new List(); + + // the API SDK + HubsApi hubsApi = new HubsApi(); + hubsApi.Configuration.AccessToken = Credentials.TokenInternal; + + var hubs = await hubsApi.GetHubsAsync(); + foreach (KeyValuePair hubInfo in new DynamicDictionaryItems(hubs.data)) + { + // check the type of the hub to show an icon + string nodeType = "hubs"; + switch ((string)hubInfo.Value.attributes.extension.type) + { + case "hubs:autodesk.core:Hub": + nodeType = "hubs"; // if showing only BIM 360, mark this as 'unsupported' + break; + case "hubs:autodesk.a360:PersonalHub": + nodeType = "personalHub"; // if showing only BIM 360, mark this as 'unsupported' + break; + case "hubs:autodesk.bim360:Account": + nodeType = "bim360Hubs"; + break; + } + + // create a treenode with the values + jsTreeNode hubNode = new jsTreeNode(hubInfo.Value.links.self.href, hubInfo.Value.attributes.name, nodeType, !(nodeType == "unsupported")); + nodes.Add(hubNode); + } + + return nodes; +} + +private async Task> GetProjectsAsync(string href) +{ + IList nodes = new List(); + + // the API SDK + ProjectsApi projectsApi = new ProjectsApi(); + projectsApi.Configuration.AccessToken = Credentials.TokenInternal; + + // extract the hubId from the href + string[] idParams = href.Split('/'); + string hubId = idParams[idParams.Length - 1]; + + var projects = await projectsApi.GetHubProjectsAsync(hubId); + foreach (KeyValuePair projectInfo in new DynamicDictionaryItems(projects.data)) + { + // check the type of the project to show an icon + string nodeType = "projects"; + switch ((string)projectInfo.Value.attributes.extension.type) + { + case "projects:autodesk.core:Project": + nodeType = "a360projects"; + break; + case "projects:autodesk.bim360:Project": + nodeType = "bim360projects"; + break; + } + + // create a treenode with the values + jsTreeNode projectNode = new jsTreeNode(projectInfo.Value.links.self.href, projectInfo.Value.attributes.name, nodeType, true); + nodes.Add(projectNode); + } + + return nodes; +} + +private async Task> GetProjectContents(string href) +{ + IList nodes = new List(); + + // the API SDK + ProjectsApi projectApi = new ProjectsApi(); + projectApi.Configuration.AccessToken = Credentials.TokenInternal; + + // extract the hubId & projectId from the href + string[] idParams = href.Split('/'); + string hubId = idParams[idParams.Length - 3]; + string projectId = idParams[idParams.Length - 1]; + + var folders = await projectApi.GetProjectTopFoldersAsync(hubId, projectId); + foreach (KeyValuePair folder in new DynamicDictionaryItems(folders.data)) + { + nodes.Add(new jsTreeNode(folder.Value.links.self.href, folder.Value.attributes.displayName, "folders", true)); + } + return nodes; +} + +private async Task> GetFolderContents(string href) +{ + IList nodes = new List(); + + // the API SDK + FoldersApi folderApi = new FoldersApi(); + folderApi.Configuration.AccessToken = Credentials.TokenInternal; + + // extract the projectId & folderId from the href + string[] idParams = href.Split('/'); + string folderId = idParams[idParams.Length - 1]; + string projectId = idParams[idParams.Length - 3]; + + // check if folder specifies visible types + JArray visibleTypes = null; + dynamic folder = (await folderApi.GetFolderAsync(projectId, folderId)).ToJson(); + if (folder.data.attributes != null && folder.data.attributes.extension != null && folder.data.attributes.extension.data != null && !(folder.data.attributes.extension.data is JArray) && folder.data.attributes.extension.data.visibleTypes != null){ + visibleTypes = folder.data.attributes.extension.data.visibleTypes; + visibleTypes.Add("items:autodesk.bim360:C4RModel"); // C4R models are not returned on visibleTypes, therefore add them here + } + + var folderContents = await folderApi.GetFolderContentsAsync(projectId, folderId); + // the GET Folder Contents has 2 main properties: data & included (not always available) + var folderData = new DynamicDictionaryItems(folderContents.data); + var folderIncluded = (folderContents.Dictionary.ContainsKey("included") ? new DynamicDictionaryItems(folderContents.included) : null); + + // let's start iterating the FOLDER DATA + foreach (KeyValuePair folderContentItem in folderData) + { + // do we need to skip some items? based on the visibleTypes of this folder + string extension = folderContentItem.Value.attributes.extension.type; + if (extension.IndexOf("Folder") /*any folder*/ == -1 && visibleTypes != null && !visibleTypes.ToString().Contains(extension)) continue; + + // if the type is items:autodesk.bim360:Document we need some manipulation... + if (extension.Equals("items:autodesk.bim360:Document")) + { + // as this is a DOCUMENT, lets interate the FOLDER INCLUDED to get the name (known issue) + foreach (KeyValuePair includedItem in folderIncluded) + { + // check if the id match... + if (includedItem.Value.relationships.item.data.id.IndexOf(folderContentItem.Value.id) != -1) + { + // found it! now we need to go back on the FOLDER DATA to get the respective FILE for this DOCUMENT + foreach (KeyValuePair folderContentItem1 in folderData) + { + if (folderContentItem1.Value.attributes.extension.type.IndexOf("File") == -1) continue; // skip if type is NOT File + + // check if the sourceFileName match... + if (folderContentItem1.Value.attributes.extension.data.sourceFileName == includedItem.Value.attributes.extension.data.sourceFileName) + { + // ready! + + // let's return for the jsTree with a special id: + // itemUrn|versionUrn|viewableId + // itemUrn: used as target_urn to get document issues + // versionUrn: used to launch the Viewer + // viewableId: which viewable should be loaded on the Viewer + // this information will be extracted when the user click on the tree node, see ForgeTree.js:136 (activate_node.jstree event handler) + string treeId = string.Format("{0}|{1}|{2}", + folderContentItem.Value.id, // item urn + Base64Encode(folderContentItem1.Value.relationships.tip.data.id), // version urn + includedItem.Value.attributes.extension.data.viewableId // viewableID + ); + nodes.Add(new jsTreeNode(treeId, WebUtility.UrlDecode(includedItem.Value.attributes.name), "bim360documents", false)); + } + } + } + } + } + else + { + // non-Plans folder items + nodes.Add(new jsTreeNode(folderContentItem.Value.links.self.href, folderContentItem.Value.attributes.displayName, (string)folderContentItem.Value.type, true)); + } + } + + return nodes; +} + +private async Task> GetItemVersions(string href) +{ + IList nodes = new List(); + + // the API SDK + ItemsApi itemApi = new ItemsApi(); + itemApi.Configuration.AccessToken = Credentials.TokenInternal; + + // extract the projectId & itemId from the href + string[] idParams = href.Split('/'); + string itemId = idParams[idParams.Length - 1]; + string projectId = idParams[idParams.Length - 3]; + + var versions = await itemApi.GetItemVersionsAsync(projectId, itemId); + foreach (KeyValuePair version in new DynamicDictionaryItems(versions.data)) + { + DateTime versionDate = version.Value.attributes.lastModifiedTime; + string verNum = version.Value.id.Split("=")[1]; + string userName = version.Value.attributes.lastModifiedUserName; + + string urn = string.Empty; + try { urn = (string)version.Value.relationships.derivatives.data.id; } + catch { urn = Base64Encode(version.Value.id); } // some BIM 360 versions don't have viewable + + jsTreeNode node = new jsTreeNode( + urn, + string.Format("v{0}: {1} by {2}", verNum, versionDate.ToString("dd/MM/yy HH:mm:ss"), userName), + "versions", + false); + nodes.Add(node); + } + + return nodes; +} + +public static string Base64Encode(string plainText) +{ + var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText); + return System.Convert.ToBase64String(plainTextBytes).Replace("/", "_"); +} + +public class jsTreeNode +{ + public jsTreeNode(string id, string text, string type, bool children) + { + this.id = id; + this.text = text; + this.type = type; + this.children = children; + } + + public string id { get; set; } + public string text { get; set; } + public string type { get; set; } + public bool children { get; set; } +} +``` + +Последняя функция `get` возвращает **Версии** для каждого элемента (файла), где свойство `.relationships.derivatives.data.id` содержит `URN` для **Viewer**. Важно проверить, доступен ли этот атрибут, поскольку некоторые элементы могут не отображаться (например, ZIP-файлы или файлы .DOCx) или еще не прошли конвертацию. + +Обратите внимание, что мы повторно используем `Credentials`, представленные через свойство. + +Далее: [Обработка информации профиля пользователя](/ru-RU/oauth/user/readme) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/datamanagement/hubs/netcore). diff --git a/docs/ru-RU/datamanagement/hubs/nodejs.md b/docs/ru-RU/datamanagement/hubs/nodejs.md new file mode 100644 index 0000000..0c8007d --- /dev/null +++ b/docs/ru-RU/datamanagement/hubs/nodejs.md @@ -0,0 +1,174 @@ +# Репозитории данных и проекты (Node.js) + +## routes/datamanagement.js + +Создайте файл `routes/datamanagement.js` и добавьте следующий код: + +```javascript +const express = require('express'); +const { HubsApi, ProjectsApi, FoldersApi, ItemsApi } = require('forge-apis'); + +const { OAuth } = require('./common/oauth'); + +let router = express.Router(); + +router.get('/datamanagement', async (req, res) => { + // The id querystring parameter contains what was selected on the UI tree, make sure it's valid + const href = decodeURIComponent(req.query.id); + if (href === '') { + res.status(500).end(); + return; + } + + // Get the access token + const oauth = new OAuth(req.session); + const internalToken = await oauth.getInternalToken(); + if (href === '#') { + // If href is '#', it's the root tree node + getHubs(/ru-RU/oauth.getClient(), internalToken, res); + } else { + // Otherwise let's break it by '/' + const params = href.split('/'); + const resourceName = params[params.length - 2]; + const resourceId = params[params.length - 1]; + switch (resourceName) { + case 'hubs': + getProjects(resourceId, oauth.getClient(), internalToken, res); + break; + case 'projects': + // For a project, first we need the top/root folder + const hubId = params[params.length - 3]; + getFolders(hubId, resourceId/*project_id*/, oauth.getClient(), internalToken, res); + break; + case 'folders': + { + const projectId = params[params.length - 3]; + getFolderContents(projectId, resourceId/*folder_id*/, oauth.getClient(), internalToken, res); + break; + } + case 'items': + { + const projectId = params[params.length - 3]; + getVersions(projectId, resourceId/*item_id*/, oauth.getClient(), internalToken, res); + break; + } + } + } +}); +``` + +Код выше получает запрос от дерева пользовательского интерфейса (англ. UI tree). Параметр `id` указывает узел, который расширяется: `#`означает корневой узел, поэтому укажите репозитории. После этого он содержит `href` ресурса, поэтому при расширении одного `репозитория` конечная точка должна возвращать все проекты, которые в нем сожержатся. Приведенный выше код вызывает разные функции `get`. Для его завершения также скопируйте следующий код в файл (внутри того же класса `DataManagementController`). + + +```javascript +async function getHubs(/ru-RU/oauthClient, credentials, res) { + const hubs = new HubsApi(); + const data = await hubs.getHubs({}, oauthClient, credentials); + res.json(data.body.data.map((hub) => { + let hubType; + switch (hub.attributes.extension.type) { + case 'hubs:autodesk.core:Hub': + hubType = 'hubs'; + break; + case 'hubs:autodesk.a360:PersonalHub': + hubType = 'personalHub'; + break; + case 'hubs:autodesk.bim360:Account': + hubType = 'bim360Hubs'; + break; + } + return createTreeNode( + hub.links.self.href, + hub.attributes.name, + hubType, + true + ); + })); +} + +async function getProjects(hubId, oauthClient, credentials, res) { + const projects = new ProjectsApi(); + const data = await projects.getHubProjects(hubId, {}, oauthClient, credentials); + res.json(data.body.data.map((project) => { + let projectType = 'projects'; + switch (project.attributes.extension.type) { + case 'projects:autodesk.core:Project': + projectType = 'a360projects'; + break; + case 'projects:autodesk.bim360:Project': + projectType = 'bim360projects'; + break; + } + return createTreeNode( + project.links.self.href, + project.attributes.name, + projectType, + true + ); + })); +} + +async function getFolders(hubId, projectId, oauthClient, credentials, res) { + const projects = new ProjectsApi(); + const folders = await projects.getProjectTopFolders(hubId, projectId, oauthClient, credentials); + res.json(folders.body.data.map((item) => { + return createTreeNode( + item.links.self.href, + item.attributes.displayName == null ? item.attributes.name : item.attributes.displayName, + item.type, + true + ); + })); +} + +async function getFolderContents(projectId, folderId, oauthClient, credentials, res) { + const folders = new FoldersApi(); + const contents = await folders.getFolderContents(projectId, folderId, {}, oauthClient, credentials); + const treeNodes = contents.body.data.map((item) => { + var name = (item.attributes.name == null ? item.attributes.displayName : item.attributes.name); + if (name !== '') { // BIM 360 Items with no displayName also don't have storage, so not file to transfer + return createTreeNode( + item.links.self.href, + name, + item.type, + true + ); + } else { + return null; + } + }); + res.json(treeNodes.filter(node => node !== null)); +} + +async function getVersions(projectId, itemId, oauthClient, credentials, res) { + const items = new ItemsApi(); + const versions = await items.getItemVersions(projectId, itemId, {}, oauthClient, credentials); + res.json(versions.body.data.map((version) => { + const dateFormated = new Date(version.attributes.lastModifiedTime).toLocaleString(); + const versionst = version.id.match(/^(.*)\?version=(\d+)$/)[2]; + const viewerUrn = (version.relationships != null && version.relationships.derivatives != null ? version.relationships.derivatives.data.id : null); + return createTreeNode( + viewerUrn, + decodeURI('v' + versionst + ': ' + dateFormated + ' by ' + version.attributes.lastModifiedUserName), + (/ru-RU/viewerUrn != null ? 'versions' : 'unsupported'), + false + ); + })); +} + +// Format data for tree +function createTreeNode(_id, _text, _type, _children) { + return { id: _id, text: _text, type: _type, children: _children }; +} + +module.exports = router; +``` + +Последняя функция `get` возвращает **Версии** для каждого элемента (файла), где свойство `.relationships.derivatives.data.id` содержит `URN` для **Viewer**. Важно проверить, доступен ли этот атрибут, поскольку некоторые элементы могут не отображаться (например, ZIP-файлы или файлы .DOCx) или еще не прошли конвертацию. + + +Обратите внимание, что здесь мы повторно используем auth helpers из `routes/common/oauth.js`. + +Далее: [Обработка информации профиля пользователя](/ru-RU/oauth/user/readme) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/datamanagement/hubs/nodejs). diff --git a/docs/ru-RU/datamanagement/hubs/readme.md b/docs/ru-RU/datamanagement/hubs/readme.md new file mode 100644 index 0000000..757eb5f --- /dev/null +++ b/docs/ru-RU/datamanagement/hubs/readme.md @@ -0,0 +1,25 @@ +# Репозитории данных (англ. hubs) и проекты + +[Data Management API](https://developer.autodesk.com/en/docs/data/v2/overview/) обеспечивает единый доступ к данным из BIM 360 Hub, Fusion Team Hub (ранее известный как A360 Team Hub), BIM 360 Docs Hub и A360 Personal Hub. + +![](_media/datamanagement/entities_and_domains.png) + +Для навигации и доступа к данным BIM 360, Fusion Team, A360 Personal и Object Storage Service (OSS) необходимо знать следующую терминологию: + +- `hubs`: репозитории данных BIM 360 Team, Fusion Team, BIM 360 Docs или A360 Personal +- `projects`: проекты BIM 360 Team, Fusion Team, BIM 360 Docs или A360 Personal +- `folders`: логическая организация (структура) элементов внутри одного проекта - папки +- `items`: один или несколько файлов, например, dwg, pdf или Fusion designs and drawings - элементы +- `versions`: состояние элемента; аналог конкретной версии файла - версии +- `buckets`: контейнеры для объектов с уникальными именами - бакеты +- `objects`: двоичные данные, идентифицированные URN или ключом и хранящиеся в определенном контейнере - объекты + +> Каждый аккаунт **BIM 360 Docs** - это репозиторий, к которому текущий пользователь имеет доступ. Чтобы идентифицировать эти репозитории, тип `attribute.extension.type` должен быть **hubs:autodesk.bim360:Account** (или проверьте приставку `b.` перед **id**). + +![](_media/datamanagement/hub_extension_types.png) + +В этом разделе мы создадим конечную точку для возврата списка с **Hubs**, **Projects**, **Folders**, **Items** (файлов) и соответствующих **Versions** (которые можно отобразить в Viewer). + +Выберите ваш язык: [Node.js](/ru-RU/datamanagement/hubs/nodejs) | [.NET Framework](/ru-RU/datamanagement/hubs/net) | [.NET Core](/ru-RU/datamanagement/hubs/netcore) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/datamanagement/hubs/readme). diff --git a/docs/ru-RU/datamanagement/oss/README.md b/docs/ru-RU/datamanagement/oss/README.md new file mode 100644 index 0000000..e3ed448 --- /dev/null +++ b/docs/ru-RU/datamanagement/oss/README.md @@ -0,0 +1,19 @@ +# Data Management (OSS) + +В Forge OSS (Object Storage Service) файлы хранятся в виде объектов в бакетах (англ. buckets). Помимо предоставления вашему приложению возможности загружать данные из экосистемы Forge, OSS также предоставляет возможности для управления собственными бакетами и объектами вашего приложения (включая создание, перечисление, удаление, выгрузку и загрузку). + +У каждого бакета есть [политика хранения](https://developer.autodesk.com/en/docs/data/v2/overview/retention-policy/), которая определяет время хранения файлов: + + - **краткосрочное (transient)**: Хранилище, которое сохраняет файлы всего 24 часа, идеально подходит для временных объектов. **В этом руководстве мы воспользуемся этой политикой хранения**. + - **временное (temporary)**: Хранилище, которое сохраняет файлы в течение 30 дней. + - **постоянное (persistent)**: Хранилище, которое сохраняет файлы до их удаления пользователем. + +В этом разделе мы создадим несколько конечных точек для создания бакетов, загрузки файлов и составления списка бакетов и объектов. + +> Код из этого руководства будет ставиться перед ключом бакета (англ. bucket key) с вашим Forge Client ID, чтобы избежать дублирования имен. + +!> Обратите внимание, что ключ бакета должен иметь вид [-_.a-z0-9]{3,128} + +Выберите язык: [Node.js](/ru-RU/datamanagement/oss/nodejs) | [.NET Framework](/ru-RU/datamanagement/oss/net) | [.NET Core](/ru-RU/datamanagement/oss/netcore) | [Go](/ru-RU/datamanagement/oss/go) | [PHP](/ru-RU/datamanagement/oss/php) | [Java](/ru-RU/datamanagement/oss/java) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/datamanagement/oss/). diff --git a/docs/ru-RU/datamanagement/oss/go.md b/docs/ru-RU/datamanagement/oss/go.md new file mode 100644 index 0000000..1d229ca --- /dev/null +++ b/docs/ru-RU/datamanagement/oss/go.md @@ -0,0 +1,201 @@ +# Загрузка файлов в OSS (Go) + +В этом разделе нам нужны 3 функции: + +1. Создание бакетов +2. Указание репозиториев данных и объектов (файлов) +3. Загрузка объектов (файлов) + +Мы структурируем это в 2 файлах: + +## oss.go + +Создайте файл `/server/oss.go`, который отвечает за реализацию 1 и 2 пункта, и добавьте следующий код: + +```go +package server + +import ( + "log" + "net/http" + "encoding/json" + "encoding/base64" +) + +// BucketCreateInput reflects the expected body when processing the POST request to bucket managing endpoint +type BucketCreateInput struct { + BucketKey string `json:"bucketKey"` +} + + +type Node struct { + ID string `json:"id"` + Text string `json:"text"` + Type string `json:"type"` + Children bool `json:"children"` +} + +// manageBuckets performs depending on the request method: +// case POST: creates a new bucket, receive input in the form of {'bucketKey': 'theKey'} and return 200. +// case GET: return all buckets or objects in form of list of nodes +func (service ForgeServices) manageBuckets(writer http.ResponseWriter, request *http.Request) { + if request.Method == http.MethodPost { + decoder := json.NewDecoder(request.Body) + defer request.Body.Close() + + createBucketRequest := &BucketCreateInput{} + err := decoder.Decode(createBucketRequest) + if err != nil { + http.Error(writer, "Could not parse body: "+err.Error(), http.StatusBadRequest) + } + + log.Println("Request for creating a bucket with key = ", createBucketRequest.BucketKey) + + _, err = service.CreateBucket(createBucketRequest.BucketKey, "transient") + if err != nil { + http.Error(writer, "Could not create bucket: "+err.Error(), http.StatusInternalServerError) + return + } + + writer.WriteHeader(http.StatusOK) + return + } + + if request.Method == http.MethodGet { + encoder := json.NewEncoder(writer) + var result []Node + + id := request.URL.Query().Get("id") + log.Println("Received listing request with id=", id) + if id != "#" { + log.Printf("Got bucketKey=%s, returning list of object in that bucket", id) + objectList, err := service.ListObjects(id, "", "", "") + if err != nil { + http.Error(writer, "Could not get the object list: "+err.Error(), http.StatusInternalServerError) + return + } + + for _, item := range objectList.Items { + result = append(result, Node{ + ID: base64.RawStdEncoding.EncodeToString([]byte(item.ObjectID)), + Text: item.ObjectKey, + Type: "object", + Children: false, + }) + } + + } else { + log.Println("Returning list of buckets") + bucketList, err := service.ListBuckets("", "", "") + if err != nil { + http.Error(writer, "Could not get the bucket list: "+err.Error(), http.StatusInternalServerError) + return + } + + + nodeChannel := make(chan Node, len(bucketList.Items)) + + for _, bucket := range bucketList.Items { + + go func(bucketKey string) { + children := false + objectList, err := service.ListObjects(bucketKey, "", "", "") + if err == nil && len(objectList.Items) > 0 { + children = true + } + + node := Node { + ID: bucketKey, + Text: bucketKey, + Type: "bucket", + Children: children, + } + nodeChannel <- node + + }(bucket.BucketKey) + + } + + for range bucketList.Items { + result = append(result, <- nodeChannel) + } + } + + writer.Header().Add("Content-Type", "application/json") + err := encoder.Encode(result) + if err != nil { + http.Error(writer, + "Could not encode bucket/object list into response body: "+err.Error(), + http.StatusInternalServerError) + return + } + + return + } + + http.Error(writer, "Unsupported request method", http.StatusMethodNotAllowed) + return +} + +``` + +Мы планируем поддерживать [jsTree](https://www.jstree.com/) со стороны frontend, поэтому наш **GET oss/buckets** должен возвращать параметр строки запроса (англ. querystring parameter) `id` и бакеты, если `id=#` и объекты для данного bucketKey переданы как `id=bucketKey`. + + +## uploader.go + +Создайте файл `/server/uploader.go` со следующим кодом: + +```go +package server + +import ( + "net/http" + "io/ioutil" + "log" + "encoding/binary" +) + +// manageObjects uploads an object given a file and bucketKey as a multipart/form-data. +// For simplicity, non-resumable. +func (service ForgeServices) manageObjects(writer http.ResponseWriter, request *http.Request) { + + if request.Method != http.MethodPost { + http.Error(writer, "Unsupported request method", http.StatusMethodNotAllowed) + return + } + + request.ParseMultipartForm(32 << 20) + bucketKey := request.FormValue("bucketKey") + + file, header, err := request.FormFile("fileToUpload") + if err != nil { + http.Error(writer, "Could not get the file from form: "+err.Error(), http.StatusBadRequest) + return + } + defer file.Close() + + data, err := ioutil.ReadAll(file) + if err != nil { + http.Error(writer, "Problem reading the file content: "+err.Error(), http.StatusBadRequest) + return + } + defer request.Body.Close() + + log.Printf("Received request to upload a file of size %v to bucket %s\n", binary.Size(data), bucketKey) + + _, err = service.UploadObject(bucketKey, header.Filename, data) + if err != nil { + http.Error(writer, "Could not upload file: "+err.Error(), http.StatusBadRequest) + return + } + + return +} +``` + +!> Загрузка файла из браузера напрямую в Atodesk Forge возможна, но требует предоставления токена доступа **write-enabled**, что **НЕ БЕЗОПАСНО**. + +Далее: [Конвертация файлов](/ru-RU/modelderivative/translate/) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/datamanagement/oss/go). diff --git a/docs/ru-RU/datamanagement/oss/java.md b/docs/ru-RU/datamanagement/oss/java.md new file mode 100644 index 0000000..7bd42b2 --- /dev/null +++ b/docs/ru-RU/datamanagement/oss/java.md @@ -0,0 +1,359 @@ +# Загрузка файлов в OSS (JAVA) + +В этом разделе нам нужны 3 функции: + +1. Создание бакетов +2. Указание репозиториев данных и объектов (файлов) +3. Загрузка объектов (файлов) + +## oss.java + +Создайте Java Class с названием `/src/main/java/oss.java`, который отвечает за создание и указание бакетов. + + +```java +package forgesample; + +import java.io.*; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.*; +import javax.xml.bind.DatatypeConverter; + +import org.json.*; + +import com.autodesk.client.auth.OAuth2TwoLegged; +import com.autodesk.client.ApiException; +import com.autodesk.client.ApiResponse; +import com.autodesk.client.api.*; +import com.autodesk.client.model.*; + + +@WebServlet({ "/oss" }) +public class oss extends HttpServlet { + + public oss() { + } + + public void init() throws ServletException { + + } + + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + + // for get buckets info + + String id = req.getParameter("id"); + resp.setCharacterEncoding("utf8"); + resp.setContentType("application/json"); + try { + // get oAuth of internal, in order to get the token with higher permissions + OAuth2TwoLegged forgeOAuth = oauth.getOAuthInternal(); + if (id.equals("#")) {// root + BucketsApi bucketsApi = new BucketsApi(); + + ApiResponse buckets = bucketsApi.getBuckets("us", 100, "abc", forgeOAuth, + forgeOAuth.getCredentials()); + + JSONArray bucketsArray = new JSONArray(); + PrintWriter out = resp.getWriter(); + + // iterate buckets + for (int i = 0; i < buckets.getData().getItems().size(); i++) { + + // get bucker info + BucketsItems eachItem = buckets.getData().getItems().get(i); + JSONObject obj = new JSONObject(); + + obj.put("id", eachItem.getBucketKey()); + obj.put("text", eachItem.getBucketKey()); + obj.put("type", "bucket"); + obj.put("children", true); + + bucketsArray.put(obj); + + } + + out.print(bucketsArray); + + } else { + + // as we have the id (bucketKey), let's return all objects + ObjectsApi objectsApi = new ObjectsApi(); + + ApiResponse objects = objectsApi.getObjects(id, 100, null, null, forgeOAuth, + forgeOAuth.getCredentials()); + + JSONArray objectsArray = new JSONArray(); + PrintWriter out = resp.getWriter(); + + // iterate each items of the bucket + for (int i = 0; i < objects.getData().getItems().size(); i++) { + + // make a note with the base64 urn of the item + ObjectDetails eachItem = objects.getData().getItems().get(i); + String base64Urn = DatatypeConverter.printBase64Binary(eachItem.getObjectId().getBytes()); + + JSONObject obj = new JSONObject(); + + obj.put("id", base64Urn); + obj.put("text", eachItem.getObjectKey()); + obj.put("type", "object"); + obj.put("children", false); + + objectsArray.put(obj); + + } + + out.print(objectsArray); + + } + } catch (ApiException autodeskExp) { + System.out.print("get buckets & objects exception: " + autodeskExp.toString()); + resp.setStatus(500); + + } catch (Exception exp) { + System.out.print("get buckets & objects exception: " + exp.toString()); + resp.setStatus(500); + } + + } + + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + + // for create bucket + + try { + + // from + // https://stackoverflow.com/questions/3831680/httpservletrequest-get-json-post-data/3831791 + StringBuffer jb = new StringBuffer(); + String line = null; + try { + BufferedReader reader = req.getReader(); + while ((line = reader.readLine()) != null) + jb.append(line); + } catch (Exception e) { + /* report an error */ } + + // Create a new bucket + try { + // get oAuth of internal, in order to get the token with higher permissions + OAuth2TwoLegged forgeOAuth = oauth.getOAuthInternal(); + + JSONObject jsonObject = new JSONObject(jb.toString()); + String bucketKey = jsonObject.getString("bucketKey"); + + // build the payload of the http request + BucketsApi bucketsApi = new BucketsApi(); + PostBucketsPayload postBuckets = new PostBucketsPayload(); + postBuckets.setBucketKey(bucketKey); + // expires in 24h + postBuckets.setPolicyKey(PostBucketsPayload.PolicyKeyEnum.TRANSIENT); + + ApiResponse newbucket = bucketsApi.createBucket(postBuckets, null, forgeOAuth, + forgeOAuth.getCredentials()); + + resp.setStatus(200); + + } catch (ApiException autodeskExp) { + System.out.print("get buckets & objects exception: " + autodeskExp.toString()); + resp.setStatus(500); + + } catch (Exception exp) { + System.out.print("get buckets & objects exception: " + exp.toString()); + resp.setStatus(500); + + } + + } catch (JSONException e) { + // crash and burn + throw new IOException("Error parsing JSON request string"); + } + + } + + public void destroy() { + super.destroy(); + } +} +``` + + + +Мы планируем поддерживать [jsTree](https://www.jstree.com/) со стороны frontend, поэтому наш **GET oss/buckets** должен возвращать параметр строки запроса (англ. querystring parameter) `id` и бакеты, если `id=#` и объекты для данного bucketKey переданы как `id=bucketKey`. + +## ossuploads.java + +Создайте файл `/src/main/ossuploads.java` со следующим кодом. Он отвечает за загрузку файлов. Рабочий процесс (англ. workflow) получает файлы и загружает их в Forge. + +```java +package forgesample; + +import java.io.*; +import java.util.Iterator; +import java.util.List; +import java.util.regex.*; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.commons.fileupload.servlet.ServletFileUpload; + +import com.autodesk.client.auth.OAuth2TwoLegged; + +import com.autodesk.client.ApiException; +import com.autodesk.client.ApiResponse; +import com.autodesk.client.api.ObjectsApi; +import com.autodesk.client.model.ObjectDetails; + +@WebServlet({ "/ossuploads" }) +public class ossuploads extends HttpServlet { + + public ossuploads() { + } + + public void init() throws ServletException { + + } + + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + + } + + private String filename(String contentTxt) throws UnsupportedEncodingException { + Pattern pattern = Pattern.compile("filename=\"(.*)\""); + Matcher matcher = pattern.matcher(contentTxt); + matcher.find(); + return matcher.group(1); + } + + private byte[] bodyContent(HttpServletRequest request) throws IOException { + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + InputStream in = request.getInputStream(); + byte[] buffer = new byte[1024]; + int length = -1; + while ((length = in.read(buffer)) != -1) { + out.write(buffer, 0, length); + } + return out.toByteArray(); + } + } + + protected void doPost(HttpServletRequest req, HttpServletResponse res) + throws ServletException, IOException, FileNotFoundException { + + // for uploading file + + try { + // from + // https://stackoverflow.com/questions/13048939/file-upload-with-servletfileuploads-parserequest + if (!ServletFileUpload.isMultipartContent(req)) { + // not multiparts/formdata + res.setStatus(500); + } else { + // bucket name to store the file + String bucketKey = ""; + // name of the new file + String filename = ""; + // path on server to store the new file temporarily + String serverFilesPath = "/fileuploads"; + + // from + // https://stackoverflow.com/questions/3831680/httpservletrequest-get-json-post-data/3831791 + + List items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(req); + Iterator iter = items.iterator(); + + File fileToUpload = null; + + // get post body to extract file name and bucket name + while (iter.hasNext()) { + FileItem item = (FileItem) iter.next(); + + if (!item.isFormField()) { + filename = item.getName(); + + String root = getServletContext().getRealPath("/"); + File path = new File(root + serverFilesPath); + if (!path.exists()) { + boolean status = path.mkdirs(); + } + + // store the file stream on server + String thisFilePathOnServer = path + "/" + filename; + fileToUpload = new File(thisFilePathOnServer); + item.write(fileToUpload); + } else { + // get bucket name + if (item.getFieldName().equals("bucketKey")) { + bucketKey = item.getString(); + } + } + } + + ObjectsApi objectsApi = new ObjectsApi(); + + // get oAuth of internal, in order to get the token with higher permissions + + OAuth2TwoLegged forgeOAuth = oauth.getOAuthInternal(); + + ApiResponse response = objectsApi.uploadObject(bucketKey, filename, + (int) fileToUpload.length(), fileToUpload, null, null, forgeOAuth, forgeOAuth.getCredentials()); + + res.setStatus(response.getStatusCode()); + } + + } catch (ApiException adskexp) { + + } catch (FileNotFoundException fileexp) { + System.out.print("get buckets & objects exception: " + fileexp.toString()); + + } + + catch (Exception exp) { + System.out.print("get buckets & objects exception: " + exp.toString()); + + } + } + + public void destroy() { + super.destroy(); + } +} +``` + +Теперь предоставьте конечную точку в `/web/WEB-INF/web.xml`, добавьте следующий код перед ``: + +```xml + + oss + forgesample.oss + + + oss + /api/forge/oss/buckets + + + ossuploads + forgesample.ossuploads + + + ossuploads + /api/forge/oss/objects + +``` + +Обратите внимание, что мы повторно используем файл `/src/main/java/oauth.java` для вызова `.getTokenInternal()` у всех функций. + +!> Загрузка файла из браузера напрямую в Atodesk Forge возможна, но требует предоставления токена доступа **write-enabled**, что **НЕ БЕЗОПАСНО**. + +Далее: [Конвертация файлов](/ru-RU/modelderivative/translate/) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/datamanagement/oss/java). diff --git a/docs/ru-RU/datamanagement/oss/net.md b/docs/ru-RU/datamanagement/oss/net.md new file mode 100644 index 0000000..835fae5 --- /dev/null +++ b/docs/ru-RU/datamanagement/oss/net.md @@ -0,0 +1,169 @@ +# Загрузка файлов в OSS (.NET Framework) + +В этом разделе нам нужны 3 функции: + +1. Создание бакетов +2. Указание репозиториев данных и объектов (файлов) +3. Загрузка объектов (файлов) + +## OSSController.cs + +Создайте .NET WebAPI Controller с названием **OSSController** (см. [как создать контроллер](/ru-RU/environment/setup/net_controller)) и добавьте следующий код: + +```csharp +using Autodesk.Forge; +using Autodesk.Forge.Model; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using System.Web; +using System.Web.Http; + +namespace forgeSample.Controllers +{ + public class OSSController : ApiController + { + public string ClientId { get { return OAuthController.GetAppSetting("FORGE_CLIENT_ID").ToLower(); } } + + /// + /// Return list of buckets (id=#) or list of objects (id=bucketKey) + /// + [HttpGet] + [Route("api/forge/oss/buckets")] + public async Task> GetOSSAsync([FromUri]string id) + { + IList nodes = new List(); + dynamic oauth = await OAuthController.GetInternalAsync(); + + if (id == "#") // root + { + // in this case, let's return all buckets + BucketsApi appBckets = new BucketsApi(); + appBckets.Configuration.AccessToken = oauth.access_token; + + // to simplify, let's return only the first 100 buckets + dynamic buckets = await appBckets.GetBucketsAsync("US", 100); + foreach (KeyValuePair bucket in new DynamicDictionaryItems(buckets.items)) + { + nodes.Add(new TreeNode(bucket.Value.bucketKey, bucket.Value.bucketKey.Replace(ClientId + "-", string.Empty), "bucket", true)); + } + } + else + { + // as we have the id (bucketKey), let's return all + ObjectsApi objects = new ObjectsApi(); + objects.Configuration.AccessToken = oauth.access_token; + var objectsList = objects.GetObjects(id); + foreach (KeyValuePair objInfo in new DynamicDictionaryItems(objectsList.items)) + { + nodes.Add(new TreeNode(Base64Encode((string)objInfo.Value.objectId), + objInfo.Value.objectKey, "object", false)); + } + } + return nodes; + } + + /// + /// Model data for jsTree used on GetOSSAsync + /// + public class TreeNode + { + public TreeNode(string id, string text, string type, bool children) + { + this.id = id; + this.text = text; + this.type = type; + this.children = children; + } + + public string id { get; set; } + public string text { get; set; } + public string type { get; set; } + public bool children { get; set; } + } + + /// + /// Create a new bucket + /// + [HttpPost] + [Route("api/forge/oss/buckets")] + public async Task CreateBucket([FromBody]CreateBucketModel bucket) + { + BucketsApi buckets = new BucketsApi(); + dynamic token = await OAuthController.GetInternalAsync(); + buckets.Configuration.AccessToken = token.access_token; + PostBucketsPayload bucketPayload = new PostBucketsPayload(string.Format("{0}-{1}", ClientId, bucket.bucketKey.ToLower()), null, + PostBucketsPayload.PolicyKeyEnum.Transient); + return await buckets.CreateBucketAsync(bucketPayload, "US"); + } + + /// + /// Input model for CreateBucket method + /// + public class CreateBucketModel + { + public string bucketKey { get; set; } + } + + /// + /// Receive a file from the client and upload to the bucket + /// + /// + [HttpPost] + [Route("api/forge/oss/objects")] + public async Task UploadObject() + { + // basic input validation + HttpRequest req = HttpContext.Current.Request; + if (string.IsNullOrWhiteSpace(req.Params["bucketKey"])) + throw new System.Exception("BucketKey parameter was not provided."); + + if (req.Files.Count != 1) + throw new System.Exception("Missing file to upload"); // for now, let's support just 1 file at a time + + string bucketKey = req.Params["bucketKey"]; + HttpPostedFile file = req.Files[0]; + + // save the file on the server + var fileSavePath = Path.Combine(HttpContext.Current.Server.MapPath("~/App_Data"), file.FileName); + file.SaveAs(fileSavePath); + + // get the bucket... + dynamic oauth = await OAuthController.GetInternalAsync(); + ObjectsApi objects = new ObjectsApi(); + objects.Configuration.AccessToken = oauth.access_token; + + // upload the file/object, which will create a new object + dynamic uploadedObj; + using (StreamReader streamReader = new StreamReader(fileSavePath)) + { + uploadedObj = await objects.UploadObjectAsync(bucketKey, + file.FileName, (int)streamReader.BaseStream.Length, streamReader.BaseStream, + "application/octet-stream"); + } + + // cleanup + File.Delete(fileSavePath); + + return uploadedObj; + } + + /// + /// Base64 enconde a string + /// + public static string Base64Encode(string plainText) + { + var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText); + return System.Convert.ToBase64String(plainTextBytes); + } + } +} +``` + +Т.к. мы планируем поддерживать [jsTree](https://www.jstree.com/), наш **GetOSSAsync** должен возвращать возвращать параметр строки запроса (англ. querystring parameter) `id` и бакеты, если `id=#` и объекты для данного bucketKey переданы как `id=bucketKey`. **CreateBucket** нужен параметр **bucketKey**, чтобы создать бакет. Последнее, но не менее важно, **UploadObject** получает файлы из браузера, временно сохраняет их в **/App_Data/**, а затем загружает в соответствующий бакет. + +!> Загрузка файла из браузера напрямую в Atodesk Forge возможна, но требует предоставления токена доступа **write-enabled**, что **НЕ БЕЗОПАСНО**. + +Далее: [Конвертация файлов](/ru-RU/modelderivative/translate/) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/datamanagement/oss/net). diff --git a/docs/ru-RU/datamanagement/oss/netcore.md b/docs/ru-RU/datamanagement/oss/netcore.md new file mode 100644 index 0000000..b3dab4c --- /dev/null +++ b/docs/ru-RU/datamanagement/oss/netcore.md @@ -0,0 +1,170 @@ +# Загрузка файлов в OSS (.NET Core) + +В этом разделе нам нужны 3 функции: + +1. Создание бакетов +2. Указание репозиториев данных и объектов (файлов) +3. Загрузка объектов (файлов) + +## OSSController.cs + +В папке **Controllers** создайте класс с названием **OSSController** в папке классов с тем же именем (`OSSController.cs`) и добавьте следующий код: + +```csharp +using Autodesk.Forge; +using Autodesk.Forge.Model; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace forgeSample.Controllers +{ + [ApiController] + public class OSSController : ControllerBase + { + private IWebHostEnvironment _env; + public OSSController(IWebHostEnvironment env) { _env = env; } + public string ClientId { get { return OAuthController.GetAppSetting("FORGE_CLIENT_ID").ToLower(); } } + + /// + /// Return list of buckets (id=#) or list of objects (id=bucketKey) + /// + [HttpGet] + [Route("api/forge/oss/buckets")] + public async Task> GetOSSAsync(string id) + { + IList nodes = new List(); + dynamic oauth = await OAuthController.GetInternalAsync(); + + if (id == "#") // root + { + // in this case, let's return all buckets + BucketsApi appBckets = new BucketsApi(); + appBckets.Configuration.AccessToken = oauth.access_token; + + // to simplify, let's return only the first 100 buckets + dynamic buckets = await appBckets.GetBucketsAsync("US", 100); + foreach (KeyValuePair bucket in new DynamicDictionaryItems(buckets.items)) + { + nodes.Add(new TreeNode(bucket.Value.bucketKey, bucket.Value.bucketKey.Replace(ClientId + "-", string.Empty), "bucket", true)); + } + } + else + { + // as we have the id (bucketKey), let's return all + ObjectsApi objects = new ObjectsApi(); + objects.Configuration.AccessToken = oauth.access_token; + var objectsList = objects.GetObjects(id); + foreach (KeyValuePair objInfo in new DynamicDictionaryItems(objectsList.items)) + { + nodes.Add(new TreeNode(Base64Encode((string)objInfo.Value.objectId), + objInfo.Value.objectKey, "object", false)); + } + } + return nodes; + } + + /// + /// Model data for jsTree used on GetOSSAsync + /// + public class TreeNode + { + public TreeNode(string id, string text, string type, bool children) + { + this.id = id; + this.text = text; + this.type = type; + this.children = children; + } + + public string id { get; set; } + public string text { get; set; } + public string type { get; set; } + public bool children { get; set; } + } + + /// + /// Create a new bucket + /// + [HttpPost] + [Route("api/forge/oss/buckets")] + public async Task CreateBucket([FromBody]CreateBucketModel bucket) + { + BucketsApi buckets = new BucketsApi(); + dynamic token = await OAuthController.GetInternalAsync(); + buckets.Configuration.AccessToken = token.access_token; + PostBucketsPayload bucketPayload = new PostBucketsPayload(string.Format("{0}-{1}", ClientId, bucket.bucketKey.ToLower()), null, + PostBucketsPayload.PolicyKeyEnum.Transient); + return await buckets.CreateBucketAsync(bucketPayload, "US"); + } + + /// + /// Input model for CreateBucket method + /// + public class CreateBucketModel + { + public string bucketKey { get; set; } + } + + /// + /// Receive a file from the client and upload to the bucket + /// + /// + [HttpPost] + [Route("api/forge/oss/objects")] + public async Task UploadObject([FromForm]UploadFile input) + { + // save the file on the server + var fileSavePath = Path.Combine(_env.WebRootPath, Path.GetFileName(input.fileToUpload.FileName)); + using (var stream = new FileStream(fileSavePath, FileMode.Create)) + await input.fileToUpload.CopyToAsync(stream); + + + // get the bucket... + dynamic oauth = await OAuthController.GetInternalAsync(); + ObjectsApi objects = new ObjectsApi(); + objects.Configuration.AccessToken = oauth.access_token; + + // upload the file/object, which will create a new object + dynamic uploadedObj; + using (StreamReader streamReader = new StreamReader(fileSavePath)) + { + uploadedObj = await objects.UploadObjectAsync(input.bucketKey, + Path.GetFileName(input.fileToUpload.FileName), (int)streamReader.BaseStream.Length, streamReader.BaseStream, + "application/octet-stream"); + } + + // cleanup + System.IO.File.Delete(fileSavePath); + + return uploadedObj; + } + + public class UploadFile + { + public string bucketKey { get; set; } + public IFormFile fileToUpload { get; set; } + } + + /// + /// Base64 enconde a string + /// + public static string Base64Encode(string plainText) + { + var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText); + return System.Convert.ToBase64String(plainTextBytes); + } + } +} +``` + +Т.к. мы планируем поддерживать [jsTree](https://www.jstree.com/), нашему **GetOSSAsync** необходим `id` в качестве параметра строки запроса (англ. querystring parameter), он возвращает бакеты, если `id=#` и объекты для данного bucketKey переданы как `id=bucketKey`. **CreateBucket** нужен параметр **bucketKey**, чтобы создать бакет. Последнее, но не менее важно, **UploadObject** получает файлы из браузера, временно сохраняет их в **/App_Data/**, а затем загружает в соответствующий бакет. + +!> Загрузка файла из браузера напрямую в Atodesk Forge возможна, но требует предоставления токена доступа **write-enabled**, что **НЕ БЕЗОПАСНО**. + +Далее: [Конвертация файлов](/ru-RU/modelderivative/translate/) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/datamanagement/oss/netcore). diff --git a/docs/ru-RU/datamanagement/oss/nodejs.md b/docs/ru-RU/datamanagement/oss/nodejs.md new file mode 100644 index 0000000..ff1dafe --- /dev/null +++ b/docs/ru-RU/datamanagement/oss/nodejs.md @@ -0,0 +1,114 @@ +# Загрузка файлов в OSS (Node.js) + +В этом разделе нам нужны 3 функции: + +1. Создание бакетов +2. Указание репозиториев данных и объектов (файлов) +3. Загрузка объектов (файлов) + +## routes/oss.js + +Создайте файл `routes/oss.js` со следующим кодом: + +```javascript +const fs = require('fs'); +const express = require('express'); +const multer = require('multer'); +const { BucketsApi, ObjectsApi, PostBucketsPayload } = require('forge-apis'); + +const { getClient, getInternalToken } = require('./common/oauth'); +const config = require('../config'); + +let router = express.Router(); + +// Middleware for obtaining a token for each request. +router.use(async (req, res, next) => { + const token = await getInternalToken(); + req.oauth_token = token; + req.oauth_client = getClient(); + next(); +}); + +// GET /api/forge/oss/buckets - expects a query param 'id'; if the param is '#' or empty, +// returns a JSON with list of buckets, otherwise returns a JSON with list of objects in bucket with given name. +router.get('/buckets', async (req, res, next) => { + const bucket_name = req.query.id; + if (!bucket_name || bucket_name === '#') { + try { + // Retrieve buckets from Forge using the [BucketsApi](https://github.com/Autodesk-Forge/forge-api-nodejs-client/blob/master/docs/BucketsApi.md#getBuckets) + const buckets = await new BucketsApi().getBuckets({ limit: 64 }, req.oauth_client, req.oauth_token); + res.json(buckets.body.items.map((bucket) => { + return { + id: bucket.bucketKey, + // Remove bucket key prefix that was added during bucket creation + text: bucket.bucketKey.replace(config.credentials.client_id.toLowerCase() + '-', ''), + type: 'bucket', + children: true + }; + })); + } catch(err) { + next(err); + } + } else { + try { + // Retrieve objects from Forge using the [ObjectsApi](https://github.com/Autodesk-Forge/forge-api-nodejs-client/blob/master/docs/ObjectsApi.md#getObjects) + const objects = await new ObjectsApi().getObjects(bucket_name, {}, req.oauth_client, req.oauth_token); + res.json(objects.body.items.map((object) => { + return { + id: Buffer.from(object.objectId).toString('base64'), + text: object.objectKey, + type: 'object', + children: false + }; + })); + } catch(err) { + next(err); + } + } +}); + +// POST /api/forge/oss/buckets - creates a new bucket. +// Request body must be a valid JSON in the form of { "bucketKey": "" }. +router.post('/buckets', async (req, res, next) => { + let payload = new PostBucketsPayload(); + payload.bucketKey = config.credentials.client_id.toLowerCase() + '-' + req.body.bucketKey; + payload.policyKey = 'transient'; // expires in 24h + try { + // Create a bucket using [BucketsApi](https://github.com/Autodesk-Forge/forge-api-nodejs-client/blob/master/docs/BucketsApi.md#createBucket). + await new BucketsApi().createBucket(payload, {}, req.oauth_client, req.oauth_token); + res.status(200).end(); + } catch(err) { + next(err); + } +}); + +// POST /api/forge/oss/objects - uploads new object to given bucket. +// Request body must be structured as 'form-data' dictionary +// with the uploaded file under "fileToUpload" key, and the bucket name under "bucketKey". +router.post('/objects', multer({ dest: 'uploads/' }).single('fileToUpload'), async (req, res, next) => { + fs.readFile(req.file.path, async (err, data) => { + if (err) { + next(err); + } + try { + // Upload an object to bucket using [ObjectsApi](https://github.com/Autodesk-Forge/forge-api-nodejs-client/blob/master/docs/ObjectsApi.md#uploadObject). + await new ObjectsApi().uploadObject(req.body.bucketKey, req.file.originalname, data.length, data, {}, req.oauth_client, req.oauth_token); + res.status(200).end(); + } catch(err) { + next(err); + } + }); +}); + +module.exports = router; +``` + +Т.к. мы планируем поддерживать [jsTree](https://www.jstree.com/), наш **GET /api/forge/oss/buckets** должен обрабатывать параметр строки запроса `id` (англ. querystring parameter) и возвращать все бакеты, если `id=#` и объекты для данного bucketKey переданы как `id=bucketKey`. Конечная точка загрузки использует модуль [multer](https://github.com/expressjs/multer) для обработки загрузки. Это сохраняет файл на сервере (например, в папке **/uploads/**), поэтому мы можем впоследствии загрузить его в Forge. + +Обратите внимание, что мы повторно используем authentication helpers из `routes/common/oauth.js` как промежуточный слой (англ. middleware) этого маршрутизатора. + +!> Загрузка файла из браузера напрямую в Autodesk Forge возможна, но требует предоставления токена доступа **write-enabled**, что **НЕ БЕЗОПАСНО**. + +Далее: [Конвертация файлов](/ru-RU/modelderivative/translate/) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/datamanagement/oss/nodejs). diff --git a/docs/ru-RU/datamanagement/oss/php.md b/docs/ru-RU/datamanagement/oss/php.md new file mode 100644 index 0000000..dfb1684 --- /dev/null +++ b/docs/ru-RU/datamanagement/oss/php.md @@ -0,0 +1,134 @@ +# Загрузка файлов в OSS (PHP) + +В этом разделе нам нужны 3 функции: + +1. Создание бакетов - Примечание: Технически название вашего бакета должно быть уникальным для всей платформы Forge - чтобы упростить работу с этим руководством, ваш client ID по умолчанию будет добавлен к имени вашего бакета и, безусловно, скрыт в пользовательском интерфейсе. Вам нужно убедиться только в том, что имя бакета уникально в пределах вашего текущего веб-приложения Forge. +2. Указание репозиториев данных и объектов (файлов) +3. Загрузка объектов (файлов) + +## OSS.php + +Создайте файл `/server/oss.php` со следующим кодом: + +```php +getTokenInternal(); + + // get the request body + $body = json_decode(file_get_contents('php://input', 'r'), true); + + $bucketKey = ForgeConfig::$prepend_bucketkey?(strtolower(ForgeConfig::getForgeID()).'_'.$body['bucketKey']):$body['bucketKey']; //Prepend the client ID to the bucket key to avoid conflict with existing buckets + + // $policeKey = $body['policyKey']; + $policeKey = "transient"; + + $apiInstance = new BucketsApi($accessToken); + $post_bucket = new PostBucketsPayload(); + $post_bucket->setBucketKey($bucketKey); + $post_bucket->setPolicyKey($policeKey); + + try { + $result = $apiInstance->createBucket($post_bucket); + print_r($result); + } catch (Exception $e) { + echo 'Exception when calling BucketsApi->createBucket: ', $e->getMessage(), PHP_EOL; + } + } + + + ///////////////////////////////////////////////////////////////////////// + public function getBucketsAndObjects(){ + global $twoLeggedAuth; + $accessToken = $twoLeggedAuth->getTokenInternal(); + + $id = $_GET['id']; + try{ + if ($id === '#') {// root + $apiInstance = new BucketsApi($accessToken); + $result = $apiInstance->getBuckets(); + $resultArray = json_decode($result, true); + $buckets = $resultArray['items']; + $bucketsLength = count($buckets); + $bucketlist = array(); + for($i=0; $i< $bucketsLength; $i++){ + $cbkey = $buckets[$i]['bucketKey']; + $cbtext = ForgeConfig::$prepend_bucketkey&&strpos($cbkey, strtolower(ForgeConfig::getForgeID())) === 0? end(explode('_', $cbkey)):$cbkey; //remove the client ID prefix from the displayed bucket key + $bucketInfo = array('id'=>$cbkey, + 'text'=> $cbtext, + 'type'=>'bucket', + 'children'=>true + ); + array_push($bucketlist, $bucketInfo); + } + print_r(json_encode($bucketlist)); + } + else{ + $apiInstance = new ObjectsApi($accessToken); + $bucket_key = $id; + $result = $apiInstance->getObjects($bucket_key); + $resultArray = json_decode($result, true); + $objects = $resultArray['items']; + + $objectsLength = count($objects); + $objectlist = array(); + for($i=0; $i< $objectsLength; $i++){ + $objectInfo = array('id'=>base64_encode($objects[$i]['objectId']), + 'text'=>$objects[$i]['objectKey'], + 'type'=>'object', + 'children'=>false + ); + array_push($objectlist, $objectInfo); + } + print_r(json_encode($objectlist)); + } + }catch(Exception $e){ + echo 'Exception when calling ObjectsApi->getObjects: ', $e->getMessage(), PHP_EOL; + } + + } + + + public function uploadFile(){ + global $twoLeggedAuth; + $accessToken = $twoLeggedAuth->getTokenInternal(); + + $body = $_POST; + $file = $_FILES; + + $apiInstance = new ObjectsApi($accessToken); + $bucket_key = $body['bucketKey']; + $fileToUpload = $file['fileToUpload']; + $filePath = $fileToUpload['tmp_name']; + $content_length = filesize($filePath); + + $fileRead = fread($filePath, $content_length); + + try { + $result = $apiInstance->uploadObject($bucket_key, $fileRead, $content_length, $filePath ); + print_r($result); + } catch (Exception $e) { + echo 'Exception when calling ObjectsApi->uploadObject: ', $e->getMessage(), PHP_EOL; + } + } +} +``` + +Т.к. мы планируем поддерживать [jsTree](https://www.jstree.com/), наш **GET oss/buckets** должен возвращать параметр строки запроса (англ. querystring parameter) `id` и бакеты, если `id=#` и объекты для данного bucketKey переданы как `id=bucketKey`. У конечной точки загрузки все еще есть проблемы с загрузкой, мы вернемся к этому позже. + +Обратите внимание, что мы повторно используем файл `/server/oauth.php` для вызова `.getTokenInternal()` у всех функций. + +Далее: [Конвертация файлов](/ru-RU/modelderivative/translate/) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/datamanagement/oss/php). diff --git a/docs/ru-RU/deployment/README.md b/docs/ru-RU/deployment/README.md new file mode 100644 index 0000000..acb7a42 --- /dev/null +++ b/docs/ru-RU/deployment/README.md @@ -0,0 +1,37 @@ +# Развертывание + +Настал момент запустить ваше приложение! Для этого есть несколько способов, давайте обсудим некоторые из них. + +## Amazon Web Services + +Amazon Web Services (AWS) поддерживает разные среды и языки программирования, например: + +**Elastic Beanstalk** + +_Просто загрузите программный код, а Elastic Beanstalk автоматически выполнит развертывание: выделит ресурсы, займется балансировкой нагрузки, автоматическим масштабированием и мониторингом работоспособности приложения. При этом пользователь сохраняет полный контроль над ресурсами AWS, используемыми для приложения, и в любое время может получить к ним доступ._ [Узнайте больше](https://aws.amazon.com/elasticbeanstalk/). + +Выберите язык программирования: [.NET Framework](/ru-RU/deployment/aws/net) + +## Microsoft Azure + +Azure поддерживает множество сред и языков программирования, например: + +**App Service** + +_Разрабатывайте собственные веб-приложения с полностью управляемой платформой: быстро создавайте, развертывайте и масштабируйте облачные и мобильные приложения, работающие на любой платформе. Обеспечьте строгие требования к производительности, безопасности и соответствию с помощью надежной и полностью управляемой платформы App Service для технического обслуживания инфраструктуры._ [Узнайте больше](https://azure.microsoft.com/en-us/services/app-service/). + +Выберите язык программирования: [Node.js](/ru-RU/deployment/azure/node) | [.NET Framework or Core](/ru-RU/deployment/azure/net) + +## Heroku + +_Heroku - это облачная PaaS платформа (англ. platform-as-a-service), основанная на управляемой контейнерной системе для создания, запуска и поддержки веб-приложений. Веб-технологии, интегрированные сервисы и экосистема Heroku созданы для обеспечения качественной и бесперебойной работы программистов._ [Learn more](https://devcenter.heroku.com/articles/git). + +Выберите язык программирования: [Node.js](/ru-RU/deployment/heroku/nodejs) | [.NET Core](/ru-RU/deployment/heroku/netcore) | [PHP](/ru-RU/deployment/heroku/php) + +## AppHarbor + +_AppHarbor - это платформа, построенная на .NET. AppHarbor может развернуть и масштабировать любое стандартное .NET приложение в интернете._ [Узнайте больше](https://appharbor.com/). + +Выберите язык программирования: [.NET Framework](https://forge.autodesk.com/blog/deploying-forge-aspnet-samples-appharbor) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/deployment/). diff --git a/docs/ru-RU/deployment/aws/net.md b/docs/ru-RU/deployment/aws/net.md new file mode 100644 index 0000000..9e6b1a3 --- /dev/null +++ b/docs/ru-RU/deployment/aws/net.md @@ -0,0 +1,28 @@ +# Amazon Web Services (AWS) + +Сначала создайте и активируйте [аккаунт AWS](https://aws.amazon.com/). + +## Требования + +AWS Toolkit for Visual Studio представляет собой расширение для Microsoft Visual Studio в системе Microsoft Windows, облегчающее процесс разработки, отладки и развертывания приложений .NET с использованием Amazon Web Services. + +- [Загрузка AWS Toolkit for Visual Studio](https://aws.amazon.com/visualstudio/) + +## Подготовьте ваш проект + +Elastic Beanstalk не может переопределить ваши `appSettings`, поэтому их нужно удалить до публикации. Это довольно просто, если добавить код ниже в ваш файл `web.release.config`. Следующий раздел **Пошаговое видео** содержит видео с подробным объяснением процесса. + +```xml + + + + +``` + +## Пошаговое видео + +Ниже вы найдете 7-минутное видео о развертывании на AWS Elastic Beanstalk. + +[Видео](https://www.youtube.com/embed/49X4ROI6PWs ':include :type=iframe width=100% height=400px') + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/deployment/aws/net). diff --git a/docs/ru-RU/deployment/azure/net.md b/docs/ru-RU/deployment/azure/net.md new file mode 100644 index 0000000..6ad747b --- /dev/null +++ b/docs/ru-RU/deployment/azure/net.md @@ -0,0 +1,65 @@ +# Приложения Forge на .NET с Azure App Service + +Этот раздел поможет вам развернуть приложения .NET в Azure App Service как веб-приложение с [Visual Studio 2017](https://visualstudio.microsoft.com/vs/). + +В этом руководстве мы будем использовать пример ViewHubModels из [предыдущего раздела](/ru-RU/tutorials/viewhubmodels). Полный пример вы найдете по ссылке [Github repo](https://github.com/Autodesk-Forge/learn.forge.viewhubmodels/tree/net). Те же инструкции должны работать и для примера **View Models**. + +[Войдите в свой аккаунт Azure или создайте учётную запись](https://signup.azure.com/) для [Microsoft Azure Computing Platform & Services](https://azure.microsoft.com/) и получите доступ к [бесплатному пробному периоду](https://azure.microsoft.com/en-us/free/?cdn=disable), который длится 12 месяцев и включает $200 credit. + +## Требования + +Это руководство работает с [Visual Studio 2017](https://visualstudio.microsoft.com/vs/). + +# Создавайте и развертывайте на Azure + +- Откройте проект в Visual Studio. Нажмите правой кнопкой мыши на проект и выберите ```Build > Publish```, чтобы начать публикацию в ```Azure App Service``` +![](_media/deployment/azure/create_web_app_net.png) + +- Войдите в/создайте басплатную учетную запись Azure +![](_media/deployment/azure/create_web_app_net_2.png) +![](_media/deployment/azure/create_web_app_net_3.png) + +- Настройте ```Name``` и ```Plan``` соответственно. Обратите внимание, что ```Name``` должно быть уникальным и совпадать с именем в вашем Callback Url - это входит в URL-адрес вашего приложения. Нажмите```Create```, чтобы начать развертывание. +![](_media/deployment/azure/create_web_app_net_4.png) + +- Проверьте build output в деталях развертывания. выходных данных сборки. Обратите внимание, что новый профиль был создан автоматически, поэтому в дальнейшем вы можете развернуть свое решение непосредственно в этом приложении, вам не нужно будет повторять эти шаги снова. +![](_media/deployment/azure/net_app_published_result.png) + +- Настройте переменные среды с данными аккаунта Forge (```FORGE_CLIENT_ID``` и ```FORGE_CLIENT_SECRET```) и Callback Url (придерживаясь структуры ```http://.azurewebsites.net/api/forge/callback/oauth```) +![](_media/deployment/azure/vsAppSettings.png) + +Готово! Нажмите на ```Site URL```, чтобы увидеть приложение в работе. + +# Альтернативные подходы + +- Вы можете создать приложение на портале Azure Portal в вашем браузере. Просто выберите ```API App``` в ```Marketplace``` (при создании приложения в браузере) и ```Existing App```(при публикации или создании ```Deployment Profile``` в Visual Studio). + + ![](_media/deployment/azure/app_dashboard.png) +- Как только вы создадите ```API App```, вы сможете развернуть его из локального репозитория Git - подробности [здесь](/ru-RU/deployment/azure/node). + +### Другие варианты развертывания +- [Visual Code](https://azure.microsoft.com/en-us/blog/visual-studio-code-and-azure-app-service-a-perfect-fit/)/[Visual Studio](../node) +- [VSTS](https://docs.microsoft.com/en-us/labs/devops/deployazurefunctionswithvsts/) +- [Github](https://blogs.msdn.microsoft.com/benjaminperkins/2017/05/10/deploy-github-source-code-repositories-to-an-azure-app-service/) +- [BitBucket](https://confluence.atlassian.com/bitbucket/deploy-to-microsoft-azure-900820699.html) +- [FTP](https://docs.microsoft.com/en-us/azure/app-service/deploy-ftp) + +# Демо-ролик + +Посмотрите это видео, демонстрирующее развертывание нашего примера .NET в [Visual Studio 2017](https://visualstudio.microsoft.com/vs/) (те же инструкции подойдут для вашего приложения Node.js с Visual Studio) + +[Демо .NET](https://www.youtube.com/embed/dDg-fQ7SHAQ ':include :type=iframe width=100% height=400px') + +# Материалы для ознакомления + +- Попробуйте [Application Insights](https://azure.microsoft.com/en-us/services/monitor/), [Cost Management](https://portal.azure.com/#blade/Microsoft_Azure_Billing/ModernBillingMenuBlade/Overview), [Security Center](https://portal.azure.com/#blade/Microsoft_Azure_Security/SecurityMenuBlade/18) и [другие инструменты и функции Azure Cloud](https://azure.microsoft.com/en-us/services/) +- Узнайте про [Resource Groups](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-overview), [Service Plans](https://azure.microsoft.com/en-us/pricing/details/app-service/plans/),[Azure Templates](https://azure.microsoft.com/en-us/resources/templates/) и [Staging Environment](https://docs.microsoft.com/en-us/azure/app-service/deploy-staging-slots) +- [Microsoft Azure Developer Camp: Build a Cloud-Native App](https://mva.microsoft.com/en-us/training-courses/microsoft-azure-developer-camp-build-a-cloud-native-app-8299) +- [Monitor Azure App Service performance](https://docs.microsoft.com/en-us/azure/application-insights/app-insights-azure-web-apps) +- [Using Azure Web Site Logging and Diagnostics](https://azure.microsoft.com/en-us/resources/videos/azure-web-site-logging-and-diagnostics/) +- [Pricing - App Service](https://azure.microsoft.com/en-us/pricing/details/app-service/windows/) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/deployment/azure/net). diff --git a/docs/ru-RU/deployment/azure/node.md b/docs/ru-RU/deployment/azure/node.md new file mode 100644 index 0000000..67e446b --- /dev/null +++ b/docs/ru-RU/deployment/azure/node.md @@ -0,0 +1,118 @@ +# Приложения Forge на Node.js с Azure App Service + +Этот раздел поможет вам развернуть приложения Node.js в Azure App Service как веб-приложение на [портале Azure](https://azure.microsoft.com/en-us/features/azure-portal/) или [Git](https://git-scm.com/). + +В этом руководстве мы будем использовать пример *Просмотр моделей из репозиториев Autodesk BIM 360 & Fusion 360* из [предыдущего раздела](/ru-RU/tutorials/viewhubmodels). Полный пример вы найдете по ссылке [Github repo](https://github.com/Autodesk-Forge/learn.forge.viewhubmodels/tree/nodejs). Те же инструкции должны работать и для примера **Визуализация моделей**. + +[Войдите в свой аккаунт Azure или создайте учётную запись](https://signup.azure.com/) для [Microsoft Azure Computing Platform & Services](https://azure.microsoft.com/) и получите доступ к [бесплатному пробному периоду](https://azure.microsoft.com/en-us/free/?cdn=disable), который длится 12 месяцев и включает $200 credit. + +## Требования + +Большинство шагов можно выполнить через Web Portal, тем не менее, вам потребуется [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest). Azure CLI - это интерфейс командной строки Azure для управления ресурсами Azure. + +## Создайте веб-приложение Azure + +Существует два способа создать приложение Azure: через Web Portal или с CLI. + +**1. Создание приложения с Web Portal** + +- Создайте ```Resource Group``` или ```Web App``` + + ![](_media/deployment/azure/create_web_app_1.png) +- Настройте ```Runtime Stack``` на ```NodeJs``` и нажмите ```Create``` + + ![](_media/deployment/azure/create_web_app_node.png) +- Возможно, создание займёт время - как только оно завершится, проверьте настройки приложения. + + ![](_media/deployment/azure/app_dashboard.png) + +**2. Создание приложения с Azure CLI** + +- Создайте```Resource Group``` (или используйте уже существующий) и ```Web App``` с коммандами ниже: + +```bash +# login with credentials explicitly or simply use 'azure login' to log in with a browser session or authorisation code +az login -u -p + +# Create a Resource Group +az group create --location westus --name myResourceGroup + +# Create an App Service Plan in free tier +az appservice plan create --name myAppServicePlan --resource-group myResourceGroup --sku FREE + +# Create a Web App +az webapp create --name --plan myAppServicePlan --resource-group myResourceGroup +``` + +# Развертывание приложения + +В этом руководстве мы будем использовать ```Local Git``` для развертывания кода. Мы можем сделать это через Web Portal и с CLI. + +**1. Развертывание через Web Portal** + +- Перейдите в ```Deployment Center```, чтобы отрегулировать настройки развертывания +![](_media/deployment/azure/deployment_settings_1.png) + +- Выберите ваш сервер сборки (англ. build server) +![](_media/deployment/azure/deployment_settings_kudu.png) + +- Укажите источник развертывания - ```Local Git``` +![](_media/deployment/azure/deployment_settings_localgit_1.png) + +- Нажмите выделенную кнопку справа вверху, чтобы открыть Azure CLI, запустите ```az webapp deployment user set --user-name $username --password $password```, чтобы настроить данные развертывания и записать полученный Git URL. +![](_media/deployment/azure/deployment_settings_azure.png) + +- Настройте переменные среды с данными аккаунта Forge (```FORGE_CLIENT_ID``` и ```FORGE_CLIENT_SECRET```) и Callback Url (придерживаясь структуры ```http://.azurewebsites.net/api/forge/callback/oauth```) +![](_media/deployment/azure/portalAppSettings.png) + +**2. Развертывание с использованием CLI** + +``` bash +# Set the account-level deployment credentials +az webapp deployment user set --user-name $username --password $password + +# Configure local Git and get deployment URL +echo $(az webapp deployment source config-local-git --name --resource-group --query url --output tsv) + +# Set up the environment variables +az webapp config appsettings set -g MyResourceGroup -n --settings FORGE_CLIENT_ID= FORGE_CLIENT_SECRET= FORGE_CLIENT_SECRET= FORGE_CALLBACK_URL= +``` + +- Отправьте локальный репозиторий в Azure Web App с помощью [Git CLI](https://git-scm.com/book/en/v2/Getting-Started-The-Command-Line) или вашего любимого клиента Git + +```bash +# Add the Azure remote to your local Git respository and push your code +cd /path/to/local/repo +git remote add azure +git push azure master # use 'git push azure :master' if you would like to push other local branches than master +``` + +Dashboard приложения должен быть таким: + +![](_media/deployment/azure/app_dashboard.png) + +Готово! Нажмите на URL приложения, чтобы увидеть его в работе. + +**3. Другие варианты развертывания** +- [Visual Code](https://azure.microsoft.com/en-us/blog/visual-studio-code-and-azure-app-service-a-perfect-fit/)/[Visual Studio](../node) +- [VSTS](https://docs.microsoft.com/en-us/labs/devops/deployazurefunctionswithvsts/) +- [Github](https://blogs.msdn.microsoft.com/benjaminperkins/2017/05/10/deploy-github-source-code-repositories-to-an-azure-app-service/) +- [BitBucket](https://confluence.atlassian.com/bitbucket/deploy-to-microsoft-azure-900820699.html) +- [FTP](https://docs.microsoft.com/en-us/azure/app-service/deploy-ftp) + +# Демо-ролик + +Посмотрите это видео, демонстрирующее шаги по резвертыванию через Azure Portal и CLI (демо-ролик основан на Bash, но комманды будут такими же и для Windows CLI и [Powershell](https://docs.microsoft.com/en-us/powershell/scripting/getting-started/getting-started-with-windows-powershell). И вы можете запустить Bash на Windows! Подробности [здесь](http://mingw.org/wiki/msys) или [здесь](https://gitforwindows.org/) (как часть Git) или попробуйте [Linux Subsystem](https://docs.microsoft.com/en-us/windows/wsl/install-win10)) + +[viewNodejs](https://www.youtube.com/embed/h_b_te0Iza0 ':include :type=iframe width=100% height=400px') + +# Материалы для ознакомления +- Автоматизация и тестирование после развертывания через [Azure Pipelines](https://docs.microsoft.com/en-us/azure/devops/pipelines/languages/javascript?view=vsts) +- Попробуйте [Application Insights](https://azure.microsoft.com/en-us/services/monitor/), [Cost Management](https://portal.azure.com/#blade/Microsoft_Azure_Billing/ModernBillingMenuBlade/Overview), [Security Center](https://portal.azure.com/#blade/Microsoft_Azure_Security/SecurityMenuBlade/18) и [другие инструменты и функции Azure Cloud](https://azure.microsoft.com/en-us/services/) +- Узнайте про [Resource Groups](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-overview), [Service Plans](https://azure.microsoft.com/en-us/pricing/details/app-service/plans/),[Azure Templates](https://azure.microsoft.com/en-us/resources/templates/) и [Staging Environment](https://docs.microsoft.com/en-us/azure/app-service/deploy-staging-slots)? +- [Building Cloud-Native Applications with Node.js and Azure](https://azure.microsoft.com/en-us/resources/building-cloud-native-applications-with-node-js-and-azure/en-us/) +- [Monitor Azure App Service performance](https://docs.microsoft.com/en-us/azure/application-insights/app-insights-azure-web-apps) +- [Using Azure Web Site Logging and Diagnostics](https://azure.microsoft.com/en-us/resources/videos/azure-web-site-logging-and-diagnostics/) +- [Pricing - App Service](https://azure.microsoft.com/en-us/pricing/details/app-service/windows/) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/deployment/azure/node). diff --git a/docs/ru-RU/deployment/heroku/heroku_step1.md b/docs/ru-RU/deployment/heroku/heroku_step1.md new file mode 100644 index 0000000..62deaf6 --- /dev/null +++ b/docs/ru-RU/deployment/heroku/heroku_step1.md @@ -0,0 +1,12 @@ +Сначала создайте и активируйте [аккаунт Heroku](https://www.heroku.com/). + +## Требования + +Heroku управляет развертыванием приложений с помощью Git, популярной системы контроля версий. Интерфейс командной строки Heroku (англ. Command Line Interface, CLI) позволяет легко создавать приложения Heroku и управлять ими прямо из терминала - это важная часть использования Heroku. + +- [Загрузка Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +- [Загрузка Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli) + +## Подготовьте ваш проект + +В корневой папке проекта **forgesample** создайте файл `.gitignore` и добавьте содержание, которое не должно контролироваться **git**. diff --git a/docs/ru-RU/deployment/heroku/heroku_step2.md b/docs/ru-RU/deployment/heroku/heroku_step2.md new file mode 100644 index 0000000..8db492f --- /dev/null +++ b/docs/ru-RU/deployment/heroku/heroku_step2.md @@ -0,0 +1,17 @@ +Теперь инициализируйте **git** для этой папки и зафиксируйте текущие файлы. В терминале (меню **View** >> **Integrated terminal**) введите (по одной строчке): + +```bash +git init +git add . +git commit -m "v1" +``` + +## Подключитесь к Heroku + +Теперь настало время развернуть `v1` вашего примера. В том же терминале войдите в вашу учетную запись: + +```bash +heroku login +``` + +Затем создайте приложение Heroku и свяжите его с локальной папкой (по одной строчке): diff --git a/docs/ru-RU/deployment/heroku/heroku_step3.md b/docs/ru-RU/deployment/heroku/heroku_step3.md new file mode 100644 index 0000000..b0a613e --- /dev/null +++ b/docs/ru-RU/deployment/heroku/heroku_step3.md @@ -0,0 +1,30 @@ +> Названия приложений Heroku должны быть уникальны, потому, если вы получаете ошибку `Name is already taken` при нажатии **create**, измените имя вашего приложения. + +Сейчас ваш локальный **git** знает о **копии** в Heroku. Отправляйте изменения с локального **git** на копию с помощью: + +```bash +git push heroku master +``` + +## Настройте переменные среды + +Лучше всего иметь Keys&Secrets для локальной разработки, поэтому перейдите к своим приложениям на сайте платформы Autodesk Forge и [создайте новое приложение](/ru-RU/account/?id=create-an-app), например, **forge sample production**. + +Войдите в учетную запись [Heroku Dashboard](https://dashboard.heroku.com/), где должно быть указано ваше приложение. Перейдите в **Settings** и создайте **Config Vars**, как показано на видео ниже: + +![](_media/deployment/heroku/env_vars.gif) + +!>Если вы создаете приложение, которое требует трёхфакторную аутентификацию (**Просмотр моделей из репозиториев Autodesk BIM 360 & Fusion 360**), вам также нужно создать переменную **FORGE_CALLBACK_URL** - `https://YOUR_HEROKU_APP_NAME.herokuapp.com/api/forge/callback/oauth`. Помните, что Callback URL должна совпадать в вашем аккаунте Heroku и Autodesk Forge! + +Готово! Ваше приложение будет доступно по адресу Heroku, что-то похожее на: **YourAppName.herokuapp.com**. + +## Разверните изменения + +Когда у вас появится новая версия проекта, войдите в аккаунт и просто добавьте `commit` и `push` live: + +```bash +heroku login +git add . +git commit -m "v2" +git push heroku master +``` diff --git a/docs/ru-RU/deployment/heroku/netcore.md b/docs/ru-RU/deployment/heroku/netcore.md new file mode 100644 index 0000000..3c2d9d0 --- /dev/null +++ b/docs/ru-RU/deployment/heroku/netcore.md @@ -0,0 +1,18 @@ +# Heroku (.NET Core) + +[Шаг 1](/ru-RU/deployment/heroku/heroku_step1.md ':include :type=markdown') + +Давайте использовать шаблон Github для Visual Studio: https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +> При использовании Visual Code вы можете добавить `.vscode` (это папка вашего `launch.json` с переменными среды). + +[Шаг 2](/ru-RU/deployment/heroku/heroku_step2.md ':include :type=markdown') + +```bash +heroku create forgesample --buildpack https://github.com/jincod/dotnetcore-buildpack.git +heroku git:remote -a forgesample +``` + +[Шаг 3](/ru-RU/deployment/heroku/heroku_step3.md ':include :type=markdown') + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/deployment/heroku/netcore). diff --git a/docs/ru-RU/deployment/heroku/nodejs.md b/docs/ru-RU/deployment/heroku/nodejs.md new file mode 100644 index 0000000..c0e60d8 --- /dev/null +++ b/docs/ru-RU/deployment/heroku/nodejs.md @@ -0,0 +1,16 @@ +# Heroku (Nodejs) + +[Шаг 1](/ru-RU/deployment/heroku/heroku_step1.md ':include :type=markdown') + +Давайте использовать шаблон Github для Node: https://github.com/github/gitignore/blob/master/Node.gitignore + +[Шаг 2](/ru-RU/deployment/heroku/heroku_step2.md ':include :type=markdown') + +```bash +heroku create forgesample +heroku git:remote -a forgesample +``` + +[Шаг 3](/ru-RU/deployment/heroku/heroku_step3.md ':include :type=markdown') + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/deployment/heroku/nodejs). diff --git a/docs/ru-RU/deployment/heroku/php.md b/docs/ru-RU/deployment/heroku/php.md new file mode 100644 index 0000000..f6bfebd --- /dev/null +++ b/docs/ru-RU/deployment/heroku/php.md @@ -0,0 +1,22 @@ +# Heroku (PHP) + +[Шаг 1](/ru-RU/deployment/heroku/heroku_step1.md ':include :type=markdown') + +Давайте использовать следующий код: +``` +vendor/ +.vscode/ +.DS_Store +Thumbs.db +``` + +[Шаг 2](/ru-RU/deployment/heroku/heroku_step2.md ':include :type=markdown') + +```bash +heroku create forgesample +heroku git:remote -a forgesample +``` + +[Шаг 3](/ru-RU/deployment/heroku/heroku_step3.md ':include :type=markdown') + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/deployment/heroku/php). diff --git a/docs/ru-RU/designautomation/activity/README.md b/docs/ru-RU/designautomation/activity/README.md new file mode 100644 index 0000000..b2267e9 --- /dev/null +++ b/docs/ru-RU/designautomation/activity/README.md @@ -0,0 +1,9 @@ +# Определение Activity + +Activity - это спецификация для действия, которое может быть выполнено с использованием указанного движка (англ. engine). В объектно-ориентированном программировании это понятие схоже с *"определением функции"*. Activity определяет, как именно ваш код будет запускаться в облаке, количество входных и выходных файлов, а также AppBundle и точку входа (англ. entry-point) для использования. + +В этом руководстве Activity имеет 2 input (файл и данные JSON) и 1 output файл. + +Выберите язык: [Node.js](/ru-RU/designautomation/activity/nodejs) | [.NET Core](/ru-RU/designautomation/activity/netcore) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/designautomation/activity/). diff --git a/docs/ru-RU/designautomation/activity/netcore.md b/docs/ru-RU/designautomation/activity/netcore.md new file mode 100644 index 0000000..07a5784 --- /dev/null +++ b/docs/ru-RU/designautomation/activity/netcore.md @@ -0,0 +1,113 @@ +# Определение Activity (.NET Core) + +Все перечисленные ниже методы должны быть добавлены в класс `DesignAutomationController`. + +**1. EngineAttributes** + +Для определения activity нам понадобится исполняемый файл и расширение файла по умолчанию. Эта вспомогательная функция предоставляет его (по названию движка). + +```csharp +/// +/// Helps identify the engine +/// +private dynamic EngineAttributes(string engine) +{ + if (engine.Contains("3dsMax")) return new { commandLine = "$(engine.path)\\3dsmaxbatch.exe -sceneFile \"$(args[inputFile].path)\" $(settings[script].path)", extension = "max", script = "da = dotNetClass(\"Autodesk.Forge.Sample.DesignAutomation.Max.RuntimeExecute\")\nda.ModifyWindowWidthHeight()\n" }; + if (engine.Contains("AutoCAD")) return new { commandLine = "$(engine.path)\\accoreconsole.exe /i \"$(args[inputFile].path)\" /al \"$(appbundles[{0}].path)\" /s $(settings[script].path)", extension = "dwg", script = "UpdateParam\n" }; + if (engine.Contains("Inventor")) return new { commandLine = "$(engine.path)\\inventorcoreconsole.exe /i \"$(args[inputFile].path)\" /al \"$(appbundles[{0}].path)\"", extension = "ipt", script = string.Empty }; + if (engine.Contains("Revit")) return new { commandLine = "$(engine.path)\\revitcoreconsole.exe /i \"$(args[inputFile].path)\" /al \"$(appbundles[{0}].path)\"", extension = "rvt", script = string.Empty }; + throw new Exception("Invalid engine"); +} +``` + +**2. CreateActivity** + +Определите новую activity с исходным файлом (input file), входными параметрами (input data [JSON]) и результатом на выходе (output file). + +```csharp +/// +/// Define a new activity +/// +[HttpPost] +[Route("api/forge/designautomation/activities")] +public async Task CreateActivity([FromBody]JObject activitySpecs) +{ + // basic input validation + string zipFileName = activitySpecs["zipFileName"].Value(); + string engineName = activitySpecs["engine"].Value(); + + // standard name for this sample + string appBundleName = zipFileName + "AppBundle"; + string activityName = zipFileName + "Activity"; + + // + Page activities = await _designAutomation.GetActivitiesAsync(); + string qualifiedActivityId = string.Format("{0}.{1}+{2}", NickName, activityName, Alias); + if (!activities.Data.Contains(qualifiedActivityId)) + { + // define the activity + // ToDo: parametrize for different engines... + dynamic engineAttributes = EngineAttributes(engineName); + string commandLine = string.Format(engineAttributes.commandLine, appBundleName); + Activity activitySpec = new Activity() + { + Id = activityName, + Appbundles = new List() { string.Format("{0}.{1}+{2}", NickName, appBundleName, Alias) }, + CommandLine = new List() { commandLine }, + Engine = engineName, + Parameters = new Dictionary() + { + { "inputFile", new Parameter() { Description = "input file", LocalName = "$(inputFile)", Ondemand = false, Required = true, Verb = Verb.Get, Zip = false } }, + { "inputJson", new Parameter() { Description = "input json", LocalName = "params.json", Ondemand = false, Required = false, Verb = Verb.Get, Zip = false } }, + { "outputFile", new Parameter() { Description = "output file", LocalName = "outputFile." + engineAttributes.extension, Ondemand = false, Required = true, Verb = Verb.Put, Zip = false } } + }, + Settings = new Dictionary() + { + { "script", new StringSetting(){ Value = engineAttributes.script } } + } + }; + Activity newActivity = await _designAutomation.CreateActivityAsync(activitySpec); + + // specify the alias for this Activity + Alias aliasSpec = new Alias() { Id = Alias, Version = 1 }; + Alias newAlias = await _designAutomation.CreateActivityAliasAsync(activityName, aliasSpec); + + return Ok(new { Activity = qualifiedActivityId }); + } + + // as this activity points to a AppBundle "dev" alias (which points to the last version of the bundle), + // there is no need to update it (for this sample), but this may be extended for different contexts + return Ok(new { Activity = "Activity already defined" }); +} +``` + +**3. GetDefinedActivities** + +Нам также понадобится метод для возврата всех заданных activities. Обратите внимание, что возвращаются только те, которые определены вами (мы используем `Forge Client Id` в качестве псевдонима (англ. nick name), который затем появляется как префикс). + +```csharp +/// +/// Get all Activities defined for this account +/// +[HttpGet] +[Route("api/forge/designautomation/activities")] +public async Task> GetDefinedActivities() +{ + // filter list of + Page activities = await _designAutomation.GetActivitiesAsync(); + List definedActivities = new List(); + foreach (string activity in activities.Data) + if (activity.StartsWith(NickName) && activity.IndexOf("$LATEST") == -1) + definedActivities.Add(activity.Replace(NickName + ".", String.Empty)); + + return definedActivities; +} +``` + +Теперь вы можете нажать на **Configure** (вверху справа), выбрать AppBundle, выбрать Engine и нажать **Define Activity**, что определит и загрузит appbundle и определит activity. На панели результатов (слева) показаны соответствующие ID. **Все остальные кнопки пока не работают** ... давайте продолжим. + +![](_media/designautomation/define_activity.gif) + +Далее: [Запуск workitem](/ru-RU/designautomation/workitem/) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/designautomation/activity/netcore). diff --git a/docs/ru-RU/designautomation/activity/nodejs.md b/docs/ru-RU/designautomation/activity/nodejs.md new file mode 100644 index 0000000..61ce333 --- /dev/null +++ b/docs/ru-RU/designautomation/activity/nodejs.md @@ -0,0 +1,141 @@ +# Определение Activity (Node.js) + +**Activity** + +Теперь мы напишем конечные точки для создания новой activity и получения уже существующих activities. Скопируйте следующий код в файл `DesignAutomation.js`перед последней строкой `module.exports = router;`: + +```javascript +/// +/// CreateActivity a new Activity +/// +router.post('/forge/designautomation/activities', async /*CreateActivity*/ (req, res) => { + const activitySpecs = req.body; + + // basic input validation + const zipFileName = activitySpecs.zipFileName; + const engineName = activitySpecs.engine; + + // standard name for this sample + const appBundleName = zipFileName + 'AppBundle'; + const activityName = zipFileName + 'Activity'; + + // get defined activities + const api = await Utils.dav3API(req.oauth_token); + let activities = null; + try { + activities = await api.getActivities(); + } catch (ex) { + console.error(ex); + return (res.status(500).json({ + diagnostic: 'Failed to get activity list' + })); + } + const qualifiedActivityId = `${Utils.NickName}.${activityName}+${Utils.Alias}`; + if (!activities.data.includes(qualifiedActivityId)) { + // define the activity + const engineAttributes = Utils.EngineAttributes(engineName); + const commandLine = engineAttributes.commandLine.replace('{0}', appBundleName); + const activitySpec = { + id: activityName, + appbundles: [`${Utils.NickName}.${appBundleName}+${Utils.Alias}`], + commandLine: [commandLine], + engine: engineName, + parameters: { + inputFile: { + description: 'input file', + localName: '$(inputFile)', + ondemand: false, + required: true, + verb: dav3.Verb.get, + zip: false + }, + inputJson: { + description: 'input json', + localName: 'params.json', + ondemand: false, + required: false, + verb: dav3.Verb.get, + zip: false + }, + outputFile: { + description: 'output file', + localName: 'outputFile.' + engineAttributes.extension, + ondemand: false, + required: true, + verb: dav3.Verb.put, + zip: false + } + }, + settings: { + script: { + value: engineAttributes.script + } + } + }; + try { + const newActivity = await api.createActivity(activitySpec); + } catch (ex) { + console.error(ex); + return (res.status(500).json({ + diagnostic: 'Failed to create new activity' + })); + } + // specify the alias for this Activity + const aliasSpec = { + id: Utils.Alias, + version: 1 + }; + try { + const newAlias = await api.createActivityAlias(activityName, aliasSpec); + } catch (ex) { + console.error(ex); + return (res.status(500).json({ + diagnostic: 'Failed to create new alias for activity' + })); + } + res.status(200).json({ + activity: qualifiedActivityId + }); + return; + } + + // as this activity points to a AppBundle "dev" alias (which points to the last version of the bundle), + // there is no need to update it (for this sample), but this may be extended for different contexts + res.status(200).json({ + activity: 'Activity already defined' + }); +}); + +/// +/// Get all Activities defined for this account +/// +router.get('/forge/designautomation/activities', async /*GetDefinedActivities*/ (req, res) => { + const api = await Utils.dav3API(req.oauth_token); + // filter list of + let activities = null; + try { + activities = await api.getActivities(); + } catch (ex) { + console.error(ex); + return (res.status(500).json({ + diagnostic: 'Failed to get activity list' + })); + } + let definedActivities = []; + for (let i = 0; i < activities.data.length; i++) { + let activity = activities.data[i]; + if (activity.startsWith(Utils.NickName) && activity.indexOf('$LATEST') === -1) + definedActivities.push(activity.replace(Utils.NickName + '.', '')); + } + + res.status(200).json(definedActivities); +}); +``` +Теперь вы можете нажать на **Configure** (вверху справа), выбрать AppBundle, выбрать Engine и нажать **Define Activity**, что определит и загрузит appbundle и определит activity. На панели результатов (слева) показаны соответствующие ID. **Все остальные кнопки пока не работают** ... давайте продолжим. + +![](_media/designautomation/define_activity.gif) + +Далее: [Запуск workitem](/ru-RU/designautomation/workitem/README.md) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/designautomation/activity/nodejs). + diff --git a/docs/ru-RU/designautomation/appbundle/README.md b/docs/ru-RU/designautomation/appbundle/README.md new file mode 100644 index 0000000..dfe99b8 --- /dev/null +++ b/docs/ru-RU/designautomation/appbundle/README.md @@ -0,0 +1,25 @@ +# Подготовка плагина AppBundle для Design Automation + +AppBundle определяет, какие операции буду выполняться в Design Automation. +Design Automation использует .bundle так же, как Autodesk App Store. Это означает, что вам нужно создать `PackageContents.xml` и ZIP-файл с` DLL` (и другими необходимыми файлами). Для получения подробной информации о том, как их создать, перейдите на сайт [Autodesk App Store – Рекомендации и инструкции для публикации разработок](https://www.autodesk.ru/autodesk-developer-network/software-platform-russian/develop-exchange-apps). + +В этом разделе мы создадим базовый плагин, который обновит параметры `width` и `height` и сохранит полученный файл, а также вспомогательные файлы (`PackageContents.xml`) и структуру папок для их размещения. Создайте файл .ZIP, готовый для загрузки в Design Automation. + +### Требования + +- **7zip**: используется для создания .ZIP с файлами bundle. Пожалуйста, установите [его здесь](https://www.7-zip.org/). В этом руководстве предполагается, что **7zip** по умолчанию загружается в папку _C:\Program Files\7-Zip\7z.exe_. + +### Дополнительные требования + +Для следующего раздела вы можете использовать заранее подготовленный плагин. Если вы захотите создать его, вам потребуется: +- **Visual Studio**: Visual Studio 2017 или более новая версия. Пожалуйста, перейдите [по ссылке](https://visualstudio.microsoft.com/vs/). + +- **AutoCAD, Inventor, Revit и 3ds Max**: Для разработки, тестирования и отладки вашего плагина Design Automation: [AutoCAD](https://www.autodesk.ru/products/autocad/overview) | [Inventor](https://www.autodesk.ru/products/inventor/overview) | [Revit](https://www.autodesk.ru/products/revit/overview) | [3ds Max](https://www.autodesk.ru/products/3ds-max/overview). + +*** + +На следующем шаге выберите **Engine** (рус. движок), т.е. продукт Autodesk, в котором будет запускаться ваш плагин. Вам потребуется установить соответствующее приложение для локальной компиляции, отладки и тестирования. + +Выберите движок: [AutoCAD](/ru-RU/designautomation/appbundle/engines/autocad) | [Inventor](/ru-RU/designautomation/appbundle/engines/inventor) | [Revit](/ru-RU/designautomation/appbundle/engines/revit) | [3ds Max](/ru-RU/designautomation/appbundle/engines/max) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/designautomation/appbundle/). diff --git a/docs/ru-RU/designautomation/appbundle/common.md b/docs/ru-RU/designautomation/appbundle/common.md new file mode 100644 index 0000000..5e0e356 --- /dev/null +++ b/docs/ru-RU/designautomation/appbundle/common.md @@ -0,0 +1,7 @@ +# Загрузка app bundle + +Сейчас, когда ZIP bundle готов, можно загружать Design Automation. + +Выберите язык: [Node.js](/ru-RU/designautomation/appbundle/nodejs) | [.NET Core](/ru-RU/designautomation/appbundle/netcore) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/designautomation/appbundle/common). diff --git a/docs/ru-RU/designautomation/appbundle/engines/autocad.md b/docs/ru-RU/designautomation/appbundle/engines/autocad.md new file mode 100644 index 0000000..a270ca1 --- /dev/null +++ b/docs/ru-RU/designautomation/appbundle/engines/autocad.md @@ -0,0 +1,186 @@ +# Подготовка AutoCAD bundle + +В этом разделе мы создадим базовый плагин AutoCAD для Design Automation. Чтобы узнать больше информации, перейдите на сайт [My First AutoCAD Plugin](https://knowledge.autodesk.com/support/autocad/learn-explore/caas/simplecontent/content/my-first-autocad-plug-overview.html). + +> Вы можете [скачать Bundle ZIP](https://github.com/Autodesk-Forge/learn.forge.designautomation/raw/master/forgesample/wwwroot/bundles/UpdateDWGParam.zip) в папку `/public/bundles/` (Node.js) или `/forgeSample/wwwroot/bundles` (.NET Core) и [пропустить этот раздел](/ru-RU/designautomation/appbundle/common.md) + +## Создание нового проекта + +Щелкните правой кнопкой мыши на решение (англ. solution), затем выберите **Add** >> **New Project**. Выберите **Windows Desktop**, затем **Class Library** и, наконец, назовите его `UpdateDWGParam`. Затем щелкните проект правой кнопкой мыши, выберите **Manage NuGet Packages...**, в разделе **Browser** вы можете выполнить поиск **AutoCAD.NET** и установить `AutoCAD.NET.Core` (который также устанавливает `AutoCAD.NET.Model`). Затем найдите и установите `Newtonsoft.Json` (который используется для анализа входных данных в формате JSON). + +> Пожалуйста, выберите .NET Framework 4.7. Если его нет в списке, [загрузите Dev Pack](https://dotnet.microsoft.com/download/dotnet-framework/net47). + +![](_media/designautomation/autocad/new_project.gif) + +В результате **package.config** должен выглядеть вот так. В этом примере используется версия 20, которая должна работать во всех доступных версиях. Вы можете отрегулировать под конкретную версию. + +```xml + + + + + + +``` + +В проекте должен быть класс `Class1.cs`, давайте изменим название файла на `Commands.cs` (для постоянства). + +## Commands.cs + +Это основной код, который будет работать с AutoCAD. Скопируйте следующий код в `Commands.cs`. Класс содержит одну настраиваемую команду AutoCAD, `UpdateParam`, определенную как метод с тем же именем. Эта команда вызывается движком Design Automation, как будет указано в **Activity** (следующий шаг этого руководства). + +```csharp +using Autodesk.AutoCAD.ApplicationServices.Core; +using Autodesk.AutoCAD.DatabaseServices; +using Autodesk.AutoCAD.Runtime; +using Newtonsoft.Json; +using System.IO; + +[assembly: CommandClass(typeof(UpdateDWGParam.Commands))] +[assembly: ExtensionApplication(null)] + +namespace UpdateDWGParam +{ + public class Commands + { + [CommandMethod("UpdateParam", CommandFlags.Modal)] + public static void UpdateParam() + { + //Get active document of drawing with Dynamic block + var doc = Application.DocumentManager.MdiActiveDocument; + var db = doc.Database; + + // read input parameters from JSON file + InputParams inputParams = JsonConvert.DeserializeObject(File.ReadAllText("params.json")); + + using (Transaction t = db.TransactionManager.StartTransaction()) + { + var bt = t.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; + + foreach (ObjectId btrId in bt) + { + //get the blockDef and check if is anonymous + BlockTableRecord btr = (BlockTableRecord)t.GetObject(btrId, OpenMode.ForRead); + if (btr.IsDynamicBlock) + { + //get all anonymous blocks from this dynamic block + ObjectIdCollection anonymousIds = btr.GetAnonymousBlockIds(); + ObjectIdCollection dynBlockRefs = new ObjectIdCollection(); + foreach (ObjectId anonymousBtrId in anonymousIds) + { + //get the anonymous block + BlockTableRecord anonymousBtr = (BlockTableRecord)t.GetObject(anonymousBtrId, OpenMode.ForRead); + //and all references to this block + ObjectIdCollection blockRefIds = anonymousBtr.GetBlockReferenceIds(true, true); + foreach (ObjectId id in blockRefIds) + { + dynBlockRefs.Add(id); + } + } + if (dynBlockRefs.Count > 0) + { + //Get the first dynamic block reference, we have only one Dyanmic Block reference in Drawing + var dBref = t.GetObject(dynBlockRefs[0], OpenMode.ForWrite) as BlockReference; + UpdateDynamicProperties(dBref, inputParams); + } + } + } + t.Commit(); + } + LogTrace("Saving file..."); + db.SaveAs("outputFile.dwg", DwgVersion.Current); + } + + /// + /// This updates the Dyanmic Blockreference with given Width and Height + /// The initial parameters of Dynamic Blockrefence, Width =20.00 and Height =40.00 + /// + /// + /// + /// + private static void UpdateDynamicProperties(BlockReference br, InputParams inputParams) + { + // Only continue is we have a valid dynamic block + if (br != null && br.IsDynamicBlock) + { + // Get the dynamic block's property collection + DynamicBlockReferencePropertyCollection pc = br.DynamicBlockReferencePropertyCollection; + foreach (DynamicBlockReferenceProperty prop in pc) + { + switch (prop.PropertyName) + { + case "Width": + prop.Value = inputParams.Width; + break; + case "Height": + prop.Value = inputParams.Height; + break; + default: + break; + } + } + } + } + + /// + /// This will appear on the Design Automation output + /// + private static void LogTrace(string format, params object[] args) { Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(format, args); } + } + + public class InputParams + { + public double Width { get; set; } + public double Height { get; set; } + } +} +``` + +## PackageContents.xml + +Создайте папку с названием `UpdateDWGParam.bundle` и, внутри этой папки, файл с названием `PackageContents.xml`, затем скопируйте туда код ниже. Узнайте больше [PackageContents.xml Format Reference](https://knowledge.autodesk.com/search-result/caas/CloudHelp/cloudhelp/2016/ENU/AutoCAD-Customization/files/GUID-BC76355D-682B-46ED-B9B7-66C95EEF2BD0-htm.html). Этот файл определяет новую пользовательскую команду AutoCAD `UpdateParam`, которая будет вызываться при работе Design Automation. + +```xml + + + + + + + + + + + + +``` + +Наконец, создайте подпапку `Contents` и оставьте её пустой. К этому моменту проект должен выглядеть так: + +![](_media/designautomation/autocad/bundle_folders.png) + +## Событие после сборки (англ. Post-build event) + +> Для Node.js необходимо настроить папку вывода ZIP AppBundle. + +Теперь нам нужно заархивировать папку .bundle. Щелкните проект правой кнопкой мыши, выберите **Properties**, затем откройте **Build Events** и скопируйте код ниже в поле **Post-build event command line**, как показано на изображении ниже. + +``` +xcopy /Y /F "$(TargetDir)*.dll" "$(ProjectDir)UpdateDWGParam.bundle\Contents\" +del /F "$(ProjectDir)..\forgesample\wwwroot\bundles\UpdateDWGParam.zip" +"C:\Program Files\7-Zip\7z.exe" a -tzip "$(ProjectDir)../forgesample/wwwroot/bundles/UpdateDWGParam.zip" "$(ProjectDir)UpdateDWGParam.bundle\" -xr0!*.pdb +``` + +Это скопирует DLL из /bin/debug/ в папку .bundle/Contents, затем используйте [7zip](https://www.7-zip.org/) для создания zip-архива, и затем, наконец, скопируйте ZIP-архив в /bundles. папки веб-приложения. + +![](_media/designautomation/autocad/post_build.png) + +> Обратите внимание, как **Post-build event** использует имена проекта и папки. Убедитесь, что вы используете эти имена. + +Если вы сейчас собираете проект `UpdateDWGParam`, вы должны увидеть что-то подобное в окне **Output**. Обратите внимание на 2 заархивированные папки и 3 файла. ZIP-файл создается непосредственно в папке /wwwroot/bundles. Это означает, что у вас все отлично! + +![](_media/designautomation/autocad/build_output.png) + +Далее: [Загрузка плагина](/ru-RU/designautomation/appbundle/common) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/designautomation/appbundle/engines/autocad). diff --git a/docs/ru-RU/designautomation/appbundle/engines/inventor.md b/docs/ru-RU/designautomation/appbundle/engines/inventor.md new file mode 100644 index 0000000..d995e0b --- /dev/null +++ b/docs/ru-RU/designautomation/appbundle/engines/inventor.md @@ -0,0 +1,139 @@ +# Подготовка Inventor bundle + +В этом разделе мы создадим базовый плагин Inventor для Design Automation. Чтобы узнать больше информации, перейдите на сайт [My First Inventor Plugin](https://knowledge.autodesk.com/support/inventor-products/learn-explore/caas/simplecontent/content/my-first-inventor-plug-overview.html). Версия на русском языке: [Моя первая программа для Autodesk Inventor](https://www.autodesk.ru/autodesk-developer-network/api-trainings/my-first-plugin/my-first-program). + +> Вы можете [скачать Bundle ZIP](https://github.com/Autodesk-Forge/learn.forge.designautomation/raw/master/forgesample/wwwroot/bundles/UpdateIPTParam.zip) в папку `/public/bundles/` (Node.js) или `/forgeSample/wwwroot/bundles` (.NET Core) и [пропустить этот раздел](/ru-RU/designautomation/appbundle/common.md) + +## Требования + +- Шаблон **Design Automation for Inventor**: перейдите на Visual Studio Market Place, скачайте и откройте его [по ссылке](https://marketplace.visualstudio.com/items?itemName=Autodesk.DesignAutomation), потом следуйте инструкциям для загрузки. + +![](_media/designautomation/inventor/da4inventor_template.png) + +## Создание нового проекта + +Щелкните правой кнопкой мыши на решение (англ. solution), затем выберите **Add** >> **New Project**. Найдите шаблоны **Inventor**, затем **Plugin project** и, наконец, назовите его `UpdateIPTParam`. Щелкните проект правой кнопкой мыши, перейдите в **Manage NuGet Packages...**, в разделе **Browse** вы можете выбрать `Newtonsoft.Json` и обновить (этот пакет уже находится в решении, если нет - установите его) + +> Пожалуйста, выберите .NET Framework 4.7. Если его нет в списке, [загрузите Dev Pack](https://dotnet.microsoft.com/download/dotnet-framework/net47). + +![](_media/designautomation/inventor/new_project.gif) + +## SampleAutomation.cs + +Откройте файл `SampleAutomation.cs` и скопируйте код ниже. Здесь параметры обновляются методом `Run`. + +```csharp +using Inventor; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; + +namespace UpdateIPTParam +{ + [ComVisible(true)] + public class SampleAutomation + { + private InventorServer m_server; + public SampleAutomation(InventorServer app) { m_server = app; } + + public void Run(Document doc) + { + try + { + // update parameters in the doc + ChangeParameters(doc); + + // generate outputs + var docDir = System.IO.Path.GetDirectoryName(doc.FullFileName); + + // save output file + var documentType = doc.DocumentType; + if (documentType == DocumentTypeEnum.kPartDocumentObject) + { + // the name must be in sync with OutputIpt localName in Activity + var fileName = System.IO.Path.Combine(docDir, "outputFile.ipt"); + + // save file + doc.SaveAs(fileName, false); + } + } + catch (Exception e) { LogTrace("Processing failed: {0}", e.ToString()); } + } + + /// + /// Change parameters in Inventor document. + /// + /// The Inventor document. + /// JSON with changed parameters. + public void ChangeParameters(Document doc) + { + var theParams = GetParameters(doc); + + Dictionary parameters = JsonConvert.DeserializeObject>(System.IO.File.ReadAllText("params.json")); + foreach (KeyValuePair entry in parameters) + { + try + { + Parameter param = theParams[entry.Key.ToLower()]; + param.Expression = entry.Value; + } + catch (Exception e) { LogTrace("Cannot update {0}: {1}", entry.Key, e.Message); } + } + doc.Update(); + } + + /// + /// Get parameters for the document. + /// + /// Parameters. Throws exception if parameters are not found. + private static Parameters GetParameters(Document doc) + { + var docType = doc.DocumentType; + switch (docType) + { + case DocumentTypeEnum.kAssemblyDocumentObject: + var asm = doc as AssemblyDocument; + return asm.ComponentDefinition.Parameters; + case DocumentTypeEnum.kPartDocumentObject: + var ipt = doc as PartDocument; + return ipt.ComponentDefinition.Parameters; + default: + throw new ApplicationException(string.Format("Unexpected document type ({0})", docType)); + } + } + + /// + /// This will appear on the Design Automation output + /// + private static void LogTrace(string format, params object[] args) { Trace.TraceInformation(format, args); } + } +} +``` + +## Событие после сборки (англ. Post-build event) + +> Для Node.js необходимо настроить папку вывода ZIP AppBundle. + +Теперь нам нужно заархивировать папку .bundle. Щелкните проект правой кнопкой мыши, выберите **Properties**, затем откройте **Build Events** и скопируйте код ниже в поле **Post-build event command line**, как показано на изображении ниже. + +``` +xcopy /Y /F "$(ProjectDir)PackageContents.xml" "$(TargetDir)\Bundle\$(MSBuildProjectName).bundle\" +xcopy /Y /F "$(TargetDir)*.*" "$(TargetDir)\Bundle\$(MSBuildProjectName).bundle\Contents\" +del /F "$(ProjectDir)..\forgesample\wwwroot\bundles\UpdateIPTParam.zip" +"C:\Program Files\7-Zip\7z.exe" a -tzip "$(ProjectDir)../forgesample/wwwroot/bundles/UpdateIPTParam.zip" "$(TargetDir)\bundle\$(MSBuildProjectName).bundle\" -xr0!*.pdb +``` + +Это скопирует DLL из /bin/debug/ в папку .bundle/Contents, затем используйте [7zip](https://www.7-zip.org/) для создания zip-архива, и затем, наконец, скопируйте ZIP-архив в /bundles. папки веб-приложения. + +![](_media/designautomation/inventor/post_build.png) + +Если вы сейчас собираете проект `UpdateIPTParam`, вы должны увидеть что-то подобное в окне **Output**. Обратите внимание на 2 заархивированные папки. ZIP-файл создается непосредственно в папке /wwwroot/bundles. Это означает, что у вас все отлично! + +![](_media/designautomation/inventor/build_output.png) + +Далее: [Загрузка плагина](/ru-RU/designautomation/appbundle/common) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/designautomation/appbundle/engines/inventor). diff --git a/docs/ru-RU/designautomation/appbundle/engines/max.md b/docs/ru-RU/designautomation/appbundle/engines/max.md new file mode 100644 index 0000000..6acae84 --- /dev/null +++ b/docs/ru-RU/designautomation/appbundle/engines/max.md @@ -0,0 +1,269 @@ +# Подготовка 3ds Max bundle + +В этом разделе мы создадим базовый плагин 3ds Max для Design Automation. Обратите внимание, что 3ds Max можно автоматизировать с помощью MAXScript, Python, NET API и C ++. 3ds Max .NET API, вероятно, используется для плагинов не очень часто, однако для других продуктов Design Automation это типичный API. Ресурсы 3ds Max для .NET API можно найти здесь (ссылки 2019, но .NET API поддерживается для всех доступных версий движков 3ds Max Design Automation): +* [Writing 3ds Max .NET plugins](http://help.autodesk.com/view/3DSMAX/2019/ENU/?guid=__developer_3ds_max_sdk___the_learning_path_lesson_7_writing__net_plug_ins_html) +* [The 3ds Max .NET SDK](http://help.autodesk.com/view/3DSMAX/2019/ENU/?guid=__developer_3ds_max__net_sdk_html) +* [GetCOREInterface Blog .NET Samples](https://getcoreinterface.typepad.com/blog/2017/10/updated-net-api-samples-for-3ds-max-2018.html) + +Помните, что для Design Automation не должно быть UI или promts, которые нельзя автоматизировать. Чтобы автоматизировать движок 3ds Max DA, вы должны предоставить MAXScript. Обычно это очень просто, т.к. большинство настроек можно быстро открыть в MAXScript (см. [function publishing for C++](http://help.autodesk.com/view/3DSMAX/2019/ENU/?guid=__developer_3ds_max_sdk_features_function_publishing_html) и [MAXScript .NET handling](http://help.autodesk.com/view/3DSMAX/2019/ENU/?guid=GUID-779FD7AC-953D-4567-B2A8-60B1D8695B95)) + +> Вы можете [скачать the Bundle ZIP](https://github.com/Autodesk-Forge/learn.forge.designautomation/raw/master/forgesample/wwwroot/bundles/UpdateMAXParam.zip) в папку `/public/bundles/` (Node.js) или `/forgeSample/wwwroot/bundles` (.NET Core) и [пропустить этот раздел](/ru-RU/designautomation/appbundle/common.md) + +## Создание нового проекта .NET + +Щелкните правой кнопкой мыши на решение (англ. solution), затем выберите **Add** >> **New Project**. Выберите **Windows Desktop**, затем **Class Library** и, наконец, назовите его `UpdateMAXParam`. Затем вам нужно будет сослаться на управляемую сборку Autodesk.Max.Dll (основной модуль 3ds Max .NET API). Этот модуль находится в папке 3dsmax.exe, и при обращении к нему не забудьте выключить флажок "Copy Local". Есть несколько других модулей, используемых для поддержки .NET API (см.[The 3ds Max .NET SDK](http://help.autodesk.com/view/3DSMAX/2019/ENU/?guid=__developer_3ds_max__net_sdk_html)), но для этого руководства мы будем использовать только Autodesk.Max.dll. Затем найдите и установите `Newtonsoft.Json` (который используется для анализа входных данных в формате JSON). + +> Пожалуйста, выберите .NET Framework 4.7. Если его нет в списке, [загрузите Dev Pack](https://dotnet.microsoft.com/download/dotnet-framework/net47). + +![](_media/designautomation/max/new_project.gif) + +В результате **package.config** должен выглядеть вот так для модуля Newtonsoft.Json module. + +```xml + + + + +``` + +В проекте должен быть класс `Class1.cs`, давайте изменим название файла на `Command.cs` (для постоянства). + +## Commands.cs + +Это основной код, который будет работать с 3ds Max. Скопируйте следующий код в `Command.cs`. Есть три класса для обработки Design Automation. Во-первых, это `InputParams`, который будет использоваться для взаимодействия с входными данными JSON. Далее идет класс `ParameterChanger`, который используется для итерации сцены и поиска всех Casement Windows (может быть любыми типами объектов, определенными ID классов). Наконец, `RuntimeExecute` используется для ввода и управления автоматизацией. Обратите внимание, что существует специальный файл регистрации (англ logging), который выводит информацию в консоль Design Automation. См. функцию LogTrace. Обратите внимание, что для этого используется управляемый класс 3ds Max `ILogSys`, а флаги, используемые с указанным API `LogEntry`, необходимы для вывода на консоль Design Automation. + +```csharp +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; + +using Newtonsoft.Json; + +using Autodesk.Max; + +namespace Autodesk.Forge.Sample.DesignAutomation.Max +{ + /// + /// Used to hold the parameters to change + /// + public class InputParams + { + public float Width { get; set; } + public float Height { get; set; } + } + /// + /// Changes parameters in automated way. + /// Iterate entire scene to get all nodes + /// In this example we specifically find Casement Windows by object class ID + /// Then modify the width and height based on inputs. + /// + /// Could be expanded to find other window types, other objects, etc. + /// + static public class ParameterChanger + { + static List m_sceneNodes = new List { }; + + /// + /// Recursively go through the scene and get all nodes + /// Use the Autodesk.Max APIs to get the children nodes + /// + static private void GetSceneNodes(IINode node) + { + m_sceneNodes.Add(node); + + for (int i = 0; i < node.NumberOfChildren; i++) + GetSceneNodes(node.GetChildNode(i)); + } + + /// + /// Function to specifically update Case Windows with input wedth and height parameters + /// + /// The new Width to set the Window + /// The new Height to set the Window + /// window count + static public int UpdateWindowNodes(float width, float height) + { + IGlobal globalInterface = Autodesk.Max.GlobalInterface.Instance; + IInterface14 coreInterface = globalInterface.COREInterface14; + + IINode nodeRoot = coreInterface.RootNode; + m_sceneNodes.Clear(); + GetSceneNodes(nodeRoot); + + // 3ds Max uses a class ID for all object types. This is easiest way to find specific type. + // ClassID (1902665597L, 1593788199L) == 0x71685F7D, 0x5EFF4727 for casement window + IClass_ID cidCasementWindow = globalInterface.Class_ID.Create(0x71685F7D, 0x5EFF4727); + + // Use LINQ to filter for windows only - in case scene has more than one, + // but this should still give us at least one for single window scene! + var sceneWindows = from node in m_sceneNodes + where ((node.ObjectRef != null) && // In some cases the ObjectRef can be null for certain node types. + (node.ObjectRef.ClassID.PartA == cidCasementWindow.PartA) && + (node.ObjectRef.ClassID.PartB == cidCasementWindow.PartB)) + select node; + + // Iterate the casement windws and update the hight and width parameters. + foreach (IINode item in sceneWindows) + { + // window is using old-style ParamArray rather than newer ParamBlk2 + IIParamArray pb = item.ObjectRef.ParamBlock; + pb.SetValue(0, coreInterface.Time, height); // window height is at index zero. + pb.SetValue(1, coreInterface.Time, width); // window width is at index one. + } + + // If there are windows, save the window updates + int status; + if (sceneWindows.Count() > 0) + { + // The output file name must match what the Design Automation work item is specifying as output file. + string full_filename = coreInterface.CurFilePath; + string filename = coreInterface.CurFileName; + string new_filename = full_filename.Replace(filename, "outputFile.max"); + status = coreInterface.SaveToFile(new_filename, true, false); + if (status == 0) //error + return -1; + } + + // return how many windows were modified. + return sceneWindows.Count(); + } + + } + + /// + /// This class is used to execute the automation. Above class could be connected to UI elements, or run by scripts directly. + /// This class takes the input from JSON input and uses those values. This way it is more cohesive to web development. + /// + static public class RuntimeExecute + { + static public int ModifyWindowWidthHeight() + { + int count = 0; + + // Run entire code block with try/catch to help determine errors + try + { + + // read input parameters from JSON file + InputParams inputParams = JsonConvert.DeserializeObject(File.ReadAllText("params.json")); + + count = ParameterChanger.UpdateWindowNodes(inputParams.Width, inputParams.Height); + + } + catch (Exception e) + { + LogTrace("Exception Error: " + e.Message); + return -1; //fail + } + + LogTrace("Changed {0} Window objects.", count); + return count; // 0+ means success, and how many objects were changed. + } + /// + /// Information sent to this LogTrace will appear on the Design Automation output + /// + private static void LogTrace(string format, params object[] args) + { + System.Reflection.Assembly a = System.Reflection.Assembly.GetExecutingAssembly(); + string output_msg = string.Format("DLL {0} compiled on {1}; {2}", + System.IO.Path.GetFileName(a.Location), + File.GetLastWriteTime(a.Location), + string.Format(format, args)); + + IGlobal globalInterface = Autodesk.Max.GlobalInterface.Instance; + IInterface14 coreInterface = globalInterface.COREInterface14; + ILogSys log = coreInterface.Log; + // Note flags are necessary to produce Design Automation output. This is same as C++: + // SYSLOG_INFO | SYSLOG_IGNORE_VERBOSITY | SYSLOG_BROADCAST + log.LogEntry(0x00000004 | 0x00040000 | 0x00010000, false, "", output_msg); + } + } +} +``` + +## PackageContents.xml + +Создайте папку с названием `UpdateMAXParam.bundle` и, внутри этой папки, файл с названием `PackageContents.xml`, затем скопируйте туда код ниже. Узнайте больше: [PackageContents.xml Format Reference](https://knowledge.autodesk.com/search-result/caas/CloudHelp/cloudhelp/2016/ENU/AutoCAD-Customization/files/GUID-BC76355D-682B-46ED-B9B7-66C95EEF2BD0-htm.html). Больше информации об упаковке ваших плагинов 3ds Max здесь [Packaging Plugins](http://help.autodesk.com/view/3DSMAX/2019/ENU/?guid=__developer_writing_plug_ins_packaging_plugins_html). + +Этот файл сообщит 3ds Max о модулях, которые нужно загрузить (в данном случае создаваемая нами сборка плагина .NET API, но также может включать плагины MAXScripts, Python и/или C ++). Поскольку плагин загружается с помощью этой функции, вам нужно помнить об инструкциях, чтобы запустить вашу работу по автоматизации. Обратите внимание, что для правильной загрузки кода 3ds Max требуется уникальный ID для ProductCode и UpgradeCode. Подробности в документации выше. + +```xml + + + + + + + + + + + + + +``` + +Наконец, создайте подпапку `Contents` и оставьте её пустой. К этому моменту проект должен выглядеть так: + +![](_media/designautomation/max/bundle_folders.png) + +## Post-build event + +> Для Node.js необходимо настроить папку вывода ZIP AppBundle. + +Теперь нам нужно заархивировать папку .bundle. Щелкните проект правой кнопкой мыши, выберите **Properties**, затем откройте **Build Events** и скопируйте код ниже в поле **Post-build event command line**, как показано на изображении ниже. + +``` +xcopy /Y /F "$(TargetDir)*.dll" "$(ProjectDir)UpdateMAXParam.bundle\Contents\" +del /F "$(ProjectDir)..\forgesample\wwwroot\bundles\UpdateMAXParam.zip" +"C:\Program Files\7-Zip\7z.exe" a -tzip "$(ProjectDir)../forgesample/wwwroot/bundles/UpdateMAXParam.zip" "$(ProjectDir)UpdateMAXParam.bundle\" -xr0!*.pdb +``` + +Это скопирует DLL из /bin/debug/ в папку .bundle/Contents, затем используйте [7zip](https://www.7-zip.org/) для создания zip-архива, и затем, наконец, скопируйте ZIP-архив в /bundles. папки веб-приложения. + +![](_media/designautomation/max/post_build.png) +> Обратите внимание, как **Post-build event** использует имена проекта и папки. Убедитесь, что вы используете эти имена. + +Если вы сейчас собираете проект `UpdateMAXParam` , вы должны увидеть что-то подобное в окне **Output**. Обратите внимание на 2 заархивированные папки и 3 файла. ZIP-файл создается непосредственно в папке /wwwroot/bundles. Это означает, что у вас все отлично! + +![](_media/designautomation/max/build_output.png) + +На этом этапе вы можете протестировать функциональность с помощью 3ds Max batch tool. Он работает аналогично движку 3ds Max Design Automation и является хорошим способом протестировать всю вашу автоматизацию локально перед отправкой задания в облачные сервисы Forge DA. Для создания экземпляров классов .NET в среде MAXScript мы можем использовать функцию MAXScript `dotNetClass`. Для этого проекта код MAXScript будет таким: + +```MAXScript +fn UpdateParam = +( + da = dotNetClass("Autodesk.Forge.Sample.DesignAutomation.Max.RuntimeExecute") + da.ModifyWindowWidthHeight() +) + +UpdateParam() +``` + +Чтобы запустить это локально, мы могли бы протестировать в командной строке примерно так: + +```CommandLine +"%ADSK_3DSMAX_x64_2019%\3dsmaxbatch.exe" -sceneFile .max da_script.ms +``` +Позже в этом руководстве вы увидите, как эти же инструкции отправляются в движок 3ds Max Design Automation. + +Далее: [Загрузка плагина](/ru-RU/designautomation/appbundle/common) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/designautomation/appbundle/engines/max). diff --git a/docs/ru-RU/designautomation/appbundle/engines/revit.md b/docs/ru-RU/designautomation/appbundle/engines/revit.md new file mode 100644 index 0000000..2c245b0 --- /dev/null +++ b/docs/ru-RU/designautomation/appbundle/engines/revit.md @@ -0,0 +1,197 @@ +# Подготовка Revit bundle + +В этом разделе мы создадим базовый плагин Revit для Design Automation. Чтобы узнать больше информации, перейдите на сайт [My First Revit Plugin](https://knowledge.autodesk.com/support/revit-products/learn-explore/caas/simplecontent/content/my-first-revit-plug-overview.html). Версия на русском языке: [Моя первая программа для Autodesk Revit](https://www.autodesk.ru/autodesk-developer-network/api-trainings/my-first-plugin/first-prog-adsk-revit). + +> Вы можете [скачать Bundle ZIP](https://github.com/Autodesk-Forge/learn.forge.designautomation/raw/master/forgesample/wwwroot/bundles/UpdateRVTParam.zip) в папку `/public/bundles/` (Node.js) или `/forgeSample/wwwroot/bundles` (.NET Core) и [пропустить этот раздел](/ru-RU/designautomation/appbundle/common.md) + +## Создание нового проекта + +Щелкните правой кнопкой мыши на решение (англ. solution), затем выберите **Add** >> **New Project**. Выберите **Windows Desktop**, затем **Class Library** и, наконец, назовите его `UpdateRVTParam`. + +> Пожалуйста, выберите .NET Framework 4.8. Если его нет в списке, [загрузите Dev Pack](https://dotnet.microsoft.com/download/dotnet-framework/net47). + +Щелкните правой кнопкой мыши **References**, затем **Add Reference** и **Browse** для `RevitAPI.dll` (по умолчанию в папку _C:\Program Files\Autodesk\Revit 201x\_ ). кликните правой кнопкой мыши на **RevitAPI** reference, перейдите в **Properties** и настройте **Copy Local** как **False**. + +Правой кнопкой мыши нажмите на проект, перейдите в **Manage NuGet Packages...**, в разделе **Browser** вы можете выполнить поиск **DesignAutomation.Revit** и установить `Autodesk.Forge.DesignAutomation.Revit` (выберите необходимую версию Revit). Затем найдите и загрузите `Newtonsoft.Json` (который используется для анализа входных данных в формате JSON). + +![](_media/designautomation/revit/new_project.gif) + +```xml + + + + + + +``` + +В проекте должен быть класс `Class1.cs`, давайте изменим название файла на`Commands.cs` (для постоянства). + +К этому моменту проект должен выглядеть вот так: + +![](_media/designautomation/revit/project_files.png) + +## Commands.cs + +Это основной код, который будет работать с Revit. Скопируйте следующий код в`Commands.cs`. Основной интерес представляет здесь событие `DesignAutomationReadyEvent`, которое начинает работать, когда приложение готово к запуску. `HandleDesignAutomationReadyEvent` реализует наш собственный код. + +```csharp +using Autodesk.Revit.ApplicationServices; +using Autodesk.Revit.Attributes; +using Autodesk.Revit.DB; +using DesignAutomationFramework; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.IO; + +namespace Autodesk.Forge.Sample.DesignAutomation.Revit +{ + [Transaction(TransactionMode.Manual)] + [Regeneration(RegenerationOption.Manual)] + public class Commands : IExternalDBApplication + { + //Path of the project(i.e)project where your Window family files are present + string OUTPUT_FILE = "OutputFile.rvt"; + + public ExternalDBApplicationResult OnStartup(ControlledApplication application) + { + DesignAutomationBridge.DesignAutomationReadyEvent += HandleDesignAutomationReadyEvent; + return ExternalDBApplicationResult.Succeeded; + } + + private void HandleDesignAutomationReadyEvent(object sender, DesignAutomationReadyEventArgs e) + { + LogTrace("Design Automation Ready event triggered..."); + e.Succeeded = true; + EditWindowParametersMethod(e.DesignAutomationData.RevitDoc); + } + + private void EditWindowParametersMethod(Document doc) + { + InputParams inputParameters = JsonConvert.DeserializeObject(File.ReadAllText("params.json")); + + //Modifying the window parameters + //Open transaction + using (Transaction trans = new Transaction(doc)) + { + trans.Start("Update window parameters"); + + //Filter for windows + FilteredElementCollector WindowCollector = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Windows).WhereElementIsNotElementType(); + IList windowIds = WindowCollector.ToElementIds() as IList; + + foreach (ElementId windowId in windowIds) + { + Element Window = doc.GetElement(windowId); + FamilyInstance FamInst = Window as FamilyInstance; + FamilySymbol FamSym = FamInst.Symbol; + SetElementParameter(FamSym, BuiltInParameter.WINDOW_HEIGHT, inputParameters.Height); + SetElementParameter(FamSym, BuiltInParameter.WINDOW_WIDTH, inputParameters.Width); + } + + //To save all the changes commit the transaction + trans.Commit(); + } + + //Save the updated file by overwriting the existing file + ModelPath ProjectModelPath = ModelPathUtils.ConvertUserVisiblePathToModelPath(OUTPUT_FILE); + SaveAsOptions SAO = new SaveAsOptions(); + SAO.OverwriteExistingFile = true; + + //Save the project file with updated window's parameters + LogTrace("Saving file..."); + doc.SaveAs(ProjectModelPath, SAO); + } + + public ExternalDBApplicationResult OnShutdown(ControlledApplication application) + { + return ExternalDBApplicationResult.Succeeded; + } + + private void SetElementParameter(FamilySymbol FamSym, BuiltInParameter paraMeter, double parameterValue) + { + FamSym.get_Parameter(paraMeter).Set(parameterValue); + } + + public class InputParams + { + public double Width { get; set; } + public double Height { get; set; } + } + + /// + /// This will appear on the Design Automation output + /// + private static void LogTrace(string format, params object[] args) { System.Console.WriteLine(format, args); } + } +} +``` + +## PackageContents.xml + +Создайте папку с названием `UpdateRVTParam.bundle` и, внутри этой папки, файл с названием `PackageContents.xml`, затем скопируйте туда код ниже. Узнайте больше: [PackageContents.xml Format Reference](https://knowledge.autodesk.com/search-result/caas/CloudHelp/cloudhelp/2016/ENU/AutoCAD-Customization/files/GUID-BC76355D-682B-46ED-B9B7-66C95EEF2BD0-htm.html). Этот файл сообщит Revit, что нужно загрузить наш плагин `.addin`. + +```xml + + + + + + + + +``` + +## Autodesk.Forge.Sample.DesignAutomation.Revit.addin + +В папке `UpdateRVTParam.bundle` создайте подпапку с названием `Contents` и, внутри этой папки, файл с названием `Autodesk.Forge.Sample.DesignAutomation.Revit.addin`. Это указывает Revit, как загружать плагин. + +```xml + + + + Modify Window Parameters + Autodesk.Forge.Sample.DesignAutomation.Revit.Commands + Revit for Design Automation + Revit for Design Automation + AlwaysVisible + .\UpdateRVTParam.dll + 000BD853-36E4-461f-9171-C5ACEDA4E723 + ADSK + Autodesk, Inc, www.autodesk.com + + +``` + +К этому моменту проект должен выглядеть вот так: + +![](_media/designautomation/revit/bundle_folders.png) + +## Событие после сборки (англ. Post-build event) + +> Для Node.js необходимо настроить папку вывода ZIP AppBundle. + +Теперь нам нужно заархивировать папку .bundle. Щелкните проект правой кнопкой мыши, выберите **Properties**, затем откройте **Build Events** и скопируйте код ниже в поле **Post-build event command line**, как показано на изображении ниже. + +``` +xcopy /Y /F "$(TargetDir)*.dll" "$(ProjectDir)UpdateRVTParam.bundle\Contents\" +del /F "$(ProjectDir)..\forgesample\wwwroot\bundles\UpdateRVTParam.zip" +"C:\Program Files\7-Zip\7z.exe" a -tzip "$(ProjectDir)../forgesample/wwwroot/bundles/UpdateRVTParam.zip" "$(ProjectDir)UpdateRVTParam.bundle\" -xr0!*.pdb +``` + +Это скопирует DLL из /bin/debug/ в папку .bundle/Contents, затем используйте [7zip](https://www.7-zip.org/) для создания zip-архива, и затем, наконец, скопируйте ZIP-архив в /bundles. папки веб-приложения. + +![](_media/designautomation/revit/post_build.png) + +> Обратите внимание, как **Post-build event** использует имена проекта и папки. Убедитесь, что вы используете эти имена. + +Если вы работаете над проектом `UpdateRVTParam`, вы должны увидеть что-то подобное в окне **Output**. Обратите внимание на 2 заархивированные папки и 3 файла. ZIP-файл создается непосредственно в папке /wwwroot/bundles. Это означает, что у вас все отлично! + +![](_media/designautomation/revit/build_output.png) + +!> Если после сборки output содержит скопированные **2 папки, 5 файлов**, вернитесь и убедитесь, что **RevitAPI** reference настроено таким образом: **Copy Local**:**False**. Возможно, вам придется удалить все DLL в папке `UpdateRVTParam.bundle/Contents/`. + +Далее: [Загрузка плагина](/ru-RU/designautomation/appbundle/common) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/designautomation/appbundle/engines/revit). + diff --git a/docs/ru-RU/designautomation/appbundle/netcore.md b/docs/ru-RU/designautomation/appbundle/netcore.md new file mode 100644 index 0000000..cc90562 --- /dev/null +++ b/docs/ru-RU/designautomation/appbundle/netcore.md @@ -0,0 +1,213 @@ +# Код для создания App Bundle (.NET Core) + +## DesignAutomationController.cs + +В папке **Controllers** создайте `DesignAutomationController.cs` с кодом ниже. Это просто класс, мы определим конечные точки позже. This is just the class, we'll define the endpoints later, но обратите внимание на `DesignAutomationHub` в конце, который позволяет нам отправлять уведомления клиенту через [SignalR](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction?view=aspnetcore-2.2). + +```csharp +using Autodesk.Forge; +using Autodesk.Forge.DesignAutomation; +using Autodesk.Forge.DesignAutomation.Model; +using Autodesk.Forge.Model; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.SignalR; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using RestSharp; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Activity = Autodesk.Forge.DesignAutomation.Model.Activity; +using Alias = Autodesk.Forge.DesignAutomation.Model.Alias; +using AppBundle = Autodesk.Forge.DesignAutomation.Model.AppBundle; +using Parameter = Autodesk.Forge.DesignAutomation.Model.Parameter; +using WorkItem = Autodesk.Forge.DesignAutomation.Model.WorkItem; +using WorkItemStatus = Autodesk.Forge.DesignAutomation.Model.WorkItemStatus; + + +namespace forgeSample.Controllers +{ + [ApiController] + public class DesignAutomationController : ControllerBase + { + // Used to access the application folder (temp location for files & bundles) + private IWebHostEnvironment _env; + // used to access the SignalR Hub + private IHubContext _hubContext; + // Local folder for bundles + public string LocalBundlesFolder { get { return Path.Combine(_env.WebRootPath, "bundles"); } } + /// Prefix for AppBundles and Activities + public static string NickName { get { return OAuthController.GetAppSetting("FORGE_CLIENT_ID"); } } + /// Alias for the app (e.g. DEV, STG, PROD). This value may come from an environment variable + public static string Alias { get { return "dev"; } } + // Design Automation v3 API + DesignAutomationClient _designAutomation; + + // Constructor, where env and hubContext are specified + public DesignAutomationController(IWebHostEnvironment env, IHubContext hubContext, DesignAutomationClient api) + { + _designAutomation = api; + _env = env; + _hubContext = hubContext; + } + + // ********************************** + // + // Next we will add the methods here + // + // ********************************** + } + + /// + /// Class uses for SignalR + /// + public class DesignAutomationHub : Microsoft.AspNetCore.SignalR.Hub + { + public string GetConnectionId() { return Context.ConnectionId; } + } + +} +``` + +Давайте добавим несколько конечных точек к этому классу. Следующие методы должны быть скопированы внутри класса `DesignAutomationController`. + +**1. GetLocalBundles** + +Посмотрите в папку `bundles` и верните список файлов .ZIP. + +```csharp +/// +/// Names of app bundles on this project +/// +[HttpGet] +[Route("api/appbundles")] +public string[] GetLocalBundles() +{ + // this folder is placed under the public folder, which may expose the bundles + // but it was defined this way so it be published on most hosts easily + return Directory.GetFiles(LocalBundlesFolder, "*.zip").Select(Path.GetFileNameWithoutExtension).ToArray(); +} +``` + +**2. GetAvailableEngines** + +Чтобы определить bundle, нам понадобится движок. Эта конечная точка возвращает список всех доступных движков. + +```csharp +/// +/// Return a list of available engines +/// +[HttpGet] +[Route("api/forge/designautomation/engines")] +public async Task> GetAvailableEngines() +{ + dynamic oauth = await OAuthController.GetInternalAsync(); + + // define Engines API + Page engines = await _designAutomation.GetEnginesAsync(); + engines.Data.Sort(); + + return engines.Data; // return list of engines +} +``` + +**3. CreateAppBundle** + +Здесь мы определяем новый AppBundle: + +```csharp +/// +/// Define a new appbundle +/// +[HttpPost] +[Route("api/forge/designautomation/appbundles")] +public async Task CreateAppBundle([FromBody]JObject appBundleSpecs) +{ + // basic input validation + string zipFileName = appBundleSpecs["zipFileName"].Value(); + string engineName = appBundleSpecs["engine"].Value(); + + // standard name for this sample + string appBundleName = zipFileName + "AppBundle"; + + // check if ZIP with bundle is here + string packageZipPath = Path.Combine(LocalBundlesFolder, zipFileName + ".zip"); + if (!System.IO.File.Exists(packageZipPath)) throw new Exception("Appbundle not found at " + packageZipPath); + + // get defined app bundles + Page appBundles = await _designAutomation.GetAppBundlesAsync(); + + // check if app bundle is already define + dynamic newAppVersion; + string qualifiedAppBundleId = string.Format("{0}.{1}+{2}", NickName, appBundleName, Alias); + if (!appBundles.Data.Contains(qualifiedAppBundleId)) + { + // create an appbundle (version 1) + AppBundle appBundleSpec = new AppBundle() + { + Package = appBundleName, + Engine = engineName, + Id = appBundleName, + Description = string.Format("Description for {0}", appBundleName), + + }; + newAppVersion = await _designAutomation.CreateAppBundleAsync(appBundleSpec); + if (newAppVersion == null) throw new Exception("Cannot create new app"); + + // create alias pointing to v1 + Alias aliasSpec = new Alias() { Id = Alias, Version = 1 }; + Alias newAlias = await _designAutomation.CreateAppBundleAliasAsync(appBundleName, aliasSpec); + } + else + { + // create new version + AppBundle appBundleSpec = new AppBundle() + { + Engine = engineName, + Description = appBundleName + }; + newAppVersion = await _designAutomation.CreateAppBundleVersionAsync(appBundleName, appBundleSpec); + if (newAppVersion == null) throw new Exception("Cannot create new version"); + + // update alias pointing to v+1 + AliasPatch aliasSpec = new AliasPatch() + { + Version = newAppVersion.Version + }; + Alias newAlias = await _designAutomation.ModifyAppBundleAliasAsync(appBundleName, Alias, aliasSpec); + } + + // upload the zip with .bundle + RestClient uploadClient = new RestClient(newAppVersion.UploadParameters.EndpointURL); + RestRequest request = new RestRequest(string.Empty, Method.POST); + request.AlwaysMultipartFormData = true; + foreach (KeyValuePair x in newAppVersion.UploadParameters.FormData) request.AddParameter(x.Key, x.Value); + request.AddFile("file", packageZipPath); + request.AddHeader("Cache-Control", "no-cache"); + await uploadClient.ExecuteTaskAsync(request); + + return Ok(new { AppBundle = qualifiedAppBundleId, Version = newAppVersion.Version }); +} +``` + +Т.к. класс `DesignAutomationHub` определяется сейчас (внутри этого контроллера), откройте `Startup.cs` и, внутри метода `Configure`, добавьте следующую строку: + +```csharp +app.UseRouting(); +app.UseEndpoints(routes => +{ + routes.MapHub("/api/signalr/designautomation"); +}); +``` + +Если вы запустите веб-приложение сейчас и нажмете **Configure** (вверху справа), вы должны увидеть свой AppBundle и список всех доступных движков. **Кнопки пока не работают** ... продолжим. + +![](_media/designautomation/list_engines.png) + +Далее: [Определение Activity](/ru-RU/designautomation/activity/) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/designautomation/appbundle/netcore). diff --git a/docs/ru-RU/designautomation/appbundle/nodejs.md b/docs/ru-RU/designautomation/appbundle/nodejs.md new file mode 100644 index 0000000..54f9ed5 --- /dev/null +++ b/docs/ru-RU/designautomation/appbundle/nodejs.md @@ -0,0 +1,344 @@ +# Код для создания App Bundle (Node Js) + + +Внутри папки `route/` создайте файл `DesignAutomation.js`. В этом файле мы запишем все конечные точки. + +**1. Utils** + +Перед созданием конечных точек мы добавим класс Utils, состоящий из всех служебных функций (таких как создание экземпляра SDK для автоматизации проектирования, загрузка файла и еще несколько полезных функций, которые используются в этом примере). + +```javascript +const _path = require('path'); +const _fs = require('fs'); +const _url = require('url'); +const express = require('express'); +const http = require('https'); +const formdata = require('form-data'); +const bodyParser = require('body-parser'); +const multer = require('multer'); +const router = express.Router(); +const { + getClient, + getInternalToken +} = require('./common/oauth'); +const config = require('../config'); +const dav3 = require('autodesk.forge.designautomation'); +const ForgeAPI = require('forge-apis'); + +router.use(bodyParser.json()); + +// Middleware for obtaining a token for each request. +router.use(async (req, res, next) => { + req.oauth_client = await getClient(/*config.scopes.internal*/); + req.oauth_token = req.oauth_client.getCredentials(); + next(); +}); + +// Static instance of the DA API +let dav3Instance = null; + +class Utils { + + static async Instance () { + if (dav3Instance === null) { + // Here it is ok to not await since we awaited in the call router.use() + dav3Instance = new dav3.AutodeskForgeDesignAutomationClient(config.client); + let FetchRefresh = async (data) => { // data is undefined in a fetch, but contains the old credentials in a refresh + let client = await getClient(); + let credentials = client.getCredentials(); + return (credentials); + }; + dav3Instance.authManager.authentications['2-legged'].fetchToken = FetchRefresh; + dav3Instance.authManager.authentications['2-legged'].refreshToken = FetchRefresh; + } + return (dav3Instance); + } + + /// + /// Returns the directory where bindles are stored on the local machine. + /// + static get LocalBundlesFolder () { + return (_path.resolve(_path.join(__dirname, '../', 'public/bundles'))); + } + + /// + /// Prefix for AppBundles and Activities + /// + static get NickName () { + return (config.credentials.client_id); + } + + /// + /// Alias for the app (e.g. DEV, STG, PROD). This value may come from an environment variable + /// + static get Alias () { + return ('dev'); + } + + /// + /// Search files in a folder and filter them. + /// + static async findFiles (dir, filter) { + return (new Promise((fulfill, reject) => { + _fs.readdir(dir, (err, files) => { + if (err) + return (reject(err)); + if (filter !== undefined && typeof filter === 'string') + files = files.filter((file) => { + return (_path.extname(file) === filter); + }); + else if (filter !== undefined && typeof filter === 'object') + files = files.filter((file) => { + return (filter.test(file)); + }); + fulfill(files); + }); + })); + } + + /// + /// Create a new DAv3 client/API with default settings + /// + static async dav3API (/ru-RU/oauth2) { + // Auto-Refresh feature + let apiClient = await Utils.Instance(); + return (new dav3.AutodeskForgeDesignAutomationApi(apiClient)); + } + + /// + /// Helps identify the engine + /// + static EngineAttributes (engine) { + if (engine.includes('3dsMax')) + return ({ + commandLine: '$(engine.path)\\3dsmaxbatch.exe -sceneFile $(args[inputFile].path) $(settings[script].path)', + extension: 'max', + script: "da = dotNetClass(\'Autodesk.Forge.Sample.DesignAutomation.Max.RuntimeExecute\')\nda.ModifyWindowWidthHeight()\n" + }); + if (engine.includes('AutoCAD')) + return ({ + commandLine: '$(engine.path)\\accoreconsole.exe /i $(args[inputFile].path) /al $(appbundles[{0}].path) /s $(settings[script].path)', + extension: 'dwg', + script: "UpdateParam\n" + }); + if (engine.includes('Inventor')) + return ({ + commandLine: '$(engine.path)\\InventorCoreConsole.exe /i $(args[inputFile].path) /al $(appbundles[{0}].path)', + extension: 'ipt', + script: '' + }); + if (engine.includes('Revit')) + return ({ + commandLine: '$(engine.path)\\revitcoreconsole.exe /i $(args[inputFile].path) /al $(appbundles[{0}].path)', + extension: 'rvt', + script: '' + }); + + throw new Error('Invalid engine'); + } + + static FormDataLength (form) { + return (new Promise((fulfill, reject) => { + form.getLength((err, length) => { + if (err) + return (reject(err)); + fulfill(length); + }); + })); + } + + /// + /// Upload a file + /// + static uploadFormDataWithFile (filepath, endpoint, params = null) { + return (new Promise(async (fulfill, reject) => { + const fileStream = _fs.createReadStream(filepath); + + const form = new formdata(); + if (params) { + const keys = Object.keys(params); + for (let i = 0; i < keys.length; i++) + form.append(keys[i], params[keys[i]]); + } + form.append('file', fileStream); + + let headers = form.getHeaders(); + headers['Cache-Control'] = 'no-cache'; + headers['Content-Length'] = await Utils.FormDataLength(form); + + const urlinfo = _url.parse(endpoint); + const postReq = http.request({ + host: urlinfo.host, + port: (urlinfo.port || (urlinfo.protocol === 'https:' ? 443 : 80)), + path: urlinfo.pathname, + method: 'POST', + headers: headers + }, + response => { + fulfill(response.statusCode); + }, + err => { + reject(err); + } + ); + + form.pipe(postReq); + })); + } +} +``` + +**2. App Bundle** + +Перед созданием активности нам нужно определить app bundle с плагином и выбрать соответствующий движок. Скопируйте и вставьте следующую конечную точку после класса utils: + +```javascript +/// +/// Names of app bundles on this project +/// +router.get('/appbundles', async /*GetLocalBundles*/ (req, res) => { + // this folder is placed under the public folder, which may expose the bundles + // but it was defined this way so it be published on most hosts easily + let bundles = await Utils.findFiles(Utils.LocalBundlesFolder, '.zip'); + bundles = bundles.map((fn) => _path.basename(fn, '.zip')); + res.json(bundles); +}); + +/// +/// Return a list of available engines +/// +router.get('/forge/designautomation/engines', async /*GetAvailableEngines*/ (req, res) => { + let that = this; + try { + const api = await Utils.dav3API(req.oauth_token); + let engines = await api.getEngines(); + res.json(engines.data.sort()); // return list of engines + } catch (ex) { + console.error(ex); + res.json([]); + } + +}); + +/// +/// Define a new appbundle +/// +router.post('/forge/designautomation/appbundles', async /*CreateAppBundle*/ (req, res) => { + const appBundleSpecs = req.body; + + // basic input validation + const zipFileName = appBundleSpecs.zipFileName; + const engineName = appBundleSpecs.engine; + + // standard name for this sample + const appBundleName = zipFileName + 'AppBundle'; + + // check if ZIP with bundle is here + const packageZipPath = _path.join(Utils.LocalBundlesFolder, zipFileName + '.zip'); + + // get defined app bundles + const api = await Utils.dav3API(req.oauth_token); + let appBundles = null; + try { + appBundles = await api.getAppBundles(); + } catch (ex) { + console.error(ex); + return (res.status(500).json({ + diagnostic: 'Failed to get the Bundle list' + })); + } + // check if app bundle is already define + let newAppVersion = null; + const qualifiedAppBundleId = `${Utils.NickName}.${appBundleName}+${Utils.Alias}`; + if (!appBundles.data.includes(qualifiedAppBundleId)) { + const appBundleSpec = dav3.AppBundle.constructFromObject({ + package: appBundleName, + engine: engineName, + id: appBundleName, + description: `Description for ${appBundleName}` + }); + try { + newAppVersion = await api.createAppBundle(appBundleSpec); + } catch (ex) { + console.error(ex); + return (res.status(500).json({ + diagnostic: 'Cannot create new app' + })); + } + + // create alias pointing to v1 + const aliasSpec = + { + id: Utils.Alias, + version: 1 + }; + try { + const newAlias = await api.createAppBundleAlias(appBundleName, aliasSpec); + } catch (ex) { + console.error(ex); + return (res.status(500).json({ + diagnostic: 'Failed to create an alias' + })); + } + } else { + // create new version + const appBundleSpec = + { + engine: engineName, + description: appBundleName + }; + try { + newAppVersion = await api.createAppBundleVersion(appBundleName, appBundleSpec); + } catch (ex) { + console.error(ex); + return (res.status(500).json({ + diagnostic: 'Cannot create new version' + })); + } + + // update alias pointing to v+1 + const aliasSpec = + { + version: newAppVersion.Version + }; + try { + const newAlias = await api.modifyAppBundleAlias(appBundleName, Utils.Alias, aliasSpec); + } catch (ex) { + console.error(ex); + return (res.status(500).json({ + diagnostic: 'Failed to create an alias' + })); + } + } + + // upload the zip with .bundle + try { + // The ‘file’ field must be at the end, all fields after ‘file’ will be ignored. + await Utils.uploadFormDataWithFile( + packageZipPath, + newAppVersion.uploadParameters.endpointURL, + newAppVersion.uploadParameters.formData + ); + } catch (ex) { + console.error(ex); + return (res.status(500).json({ + diagnostic: 'Failed to upload bundle on s3' + })); + } + + res.status(200).json({ + appBundle: qualifiedAppBundleId, + version: newAppVersion.version + }); +}); + +module.exports = router; +``` + +Если вы запустите веб-приложение сейчас и нажмете Configure (вверху справа), вы должны увидеть свой AppBundle и список всех доступных движков. **Кнопки пока не работают** ... продолжим. + +![](_media/designautomation/list_engines.png) + +Далее: [Определение Activity](/ru-RU/designautomation/activity/) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/designautomation/appbundle/nodejs). diff --git a/docs/ru-RU/designautomation/html/README.md b/docs/ru-RU/designautomation/html/README.md new file mode 100644 index 0000000..6537fa0 --- /dev/null +++ b/docs/ru-RU/designautomation/html/README.md @@ -0,0 +1,7 @@ +# Базовый пользовательский интерфейс (UI) + +Интерфейс строится на `HTML5` и` JavaScript`. По сути, те же технологии нужны и для любой серверной части, но есть несколько отличий: реализация Websocket использует socket.io (Node.js) или SignalR (.NET Core). + +Выберите язык: [Node.js](/ru-RU/designautomation/html/nodejs.md) | [.NET Core](/ru-RU/designautomation/html/netcore.md) + +[Эта страница на английском языке](https://learnforge.autodesk.io/#/designautomation/html/). diff --git a/docs/ru-RU/designautomation/html/netcore.md b/docs/ru-RU/designautomation/html/netcore.md new file mode 100644 index 0000000..41cd43f --- /dev/null +++ b/docs/ru-RU/designautomation/html/netcore.md @@ -0,0 +1,274 @@ +# Базовый пользовательский интерфейс (UI) (.NET Core) + +Давайте начнем с файлов UI (HTML и JavaScript). В папке **wwwroot** создайте папки **bundles** и **js**. + +## index.html + +В папке **wwwroot** создайте файл **index.html** с кодом ниже: + +```html + + + + + Autodesk Forge - Design Automation + + + + + + + + + + + + + + + + +
+
+
+
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+

+
+
+

+            
+
+
+ + + + + +``` + +## ForgeDesignAutomation.js + +В папке **wwwroot/js** создайте файл **ForgeDesignAutomation.js** с кодом ниже: + +```javascript +$(document).ready(function () { + prepareLists(); + + $('#clearAccount').click(clearAccount); + $('#defineActivityShow').click(defineActivityModal); + $('#createAppBundleActivity').click(createAppBundleActivity); + $('#startWorkitem').click(startWorkitem); + + startConnection(); +}); + +function prepareLists() { + list('activity', '/api/forge/designautomation/activities'); + list('engines', '/api/forge/designautomation/engines'); + list('localBundles', '/api/appbundles'); +} + +function list(control, endpoint) { + $('#' + control).find('option').remove().end(); + jQuery.ajax({ + url: endpoint, + success: function (list) { + if (list.length === 0) + $('#' + control).append($('