Skip to content

Commit d6d2968

Browse files
sanxsthamitjoshi438amitjoshiashwani123psparrow1303
authoredMar 18, 2025··
Merge commits from main (#1154)
* Refactor PowerPagesChatParticipant and add CommandRegistry (#1050) Co-authored-by: amitjoshi <[email protected]> * [PowerPage][Copilot] Nl2Site and Nl2Page Service Integration for Site Create (#1053) * Refactor PowerPagesChatParticipantConstants and add NL2PAGE and NL2SITE constants * Refactor PowerPagesChatParticipantConstants and add new page types * Update constants and move telemetry to different file * Refactor and add getCommonHeaders function --------- Co-authored-by: amitjoshi <[email protected]> * Corrected scope of PPAPI access token for preprod env (#1055) Co-authored-by: Ashwani Kumar <[email protected]> * Hook for create site command with nl2page&site (#1057) * Hook for create site command with nl2page&site * Disable any type validation * Refactor PowerPagesChatParticipantConstants and CreateSiteCommand - Add NL2SITE_GENERATING_SITE constant for generating a new Power Pages site - Update progress message in CreateSiteCommand to use NL2SITE_GENERATING_SITE constant - Remove unnecessary markdown formatting in CreateSiteHelper error handling * Refactor localization files and update Power Pages chat participant utils * Refactor NL2SiteService and Nl2PageService to include additional telemetry logging --------- Co-authored-by: amitjoshi <[email protected]> Co-authored-by: tyaginidhi <[email protected]> * Add environment list retrieval and refactor BAP endpoint logic (#1060) * Add environment list retrieval and refactor BAP endpoint logic * Add type annotation for environment list retrieval in Utils.ts * Update src/common/utilities/Utils.ts Co-authored-by: Priyanshu Agrawal <[email protected]> * Refactor BAP environment list URL to use a variable for API version and update imports in Utils.ts --------- Co-authored-by: amitjoshi <[email protected]> Co-authored-by: Priyanshu Agrawal <[email protected]> * [PowerPages][create-site] Preview and Edit Site Page and Command Registration (#1061) * Enhance CreateSiteCommand to include extension context and add ReadonlyFileSystemProvider for site page previews * Implement EditableFileSystemProvider for site page editing and update CreateSiteHelper to utilize it * Integrate CreateSiteCommand into CommandRegistry and update related components for site creation functionality * Disable copy functionality in EditableFileSystemProvider implementation * Remove ReadonlyFileSystemProvider implementation * Add telemetry constant for previewing site pages and refactor related components * Refactor CommandRegistry and add command registration utility for chat participants * Add constants for site creation parameters and refactor NL2SiteService to use them * Refactor CreateSiteCommand and CreateSiteHelper to use structured options and improve readability; add CreateSiteTypes for better type management * Add error telemetry constant for previewing site pages and handle errors in previewSitePagesContent function * Rename fileContentMap to _fileContentMap for consistency and clarity in EditableFileSystemProvider * Remove unused getUpdatedPageContent function from CreateSiteHelper to streamline code * Add ESLint disable comments for any type usage in CreateSiteHelper and CreateSiteTypes --------- Co-authored-by: amitjoshi <[email protected]> * Bump cross-spawn (#1059) Bumps and [cross-spawn](https://github.com/moxystudio/node-cross-spawn). These dependencies needed to be updated together. Updates `cross-spawn` from 7.0.3 to 7.0.6 - [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md) - [Commits](moxystudio/node-cross-spawn@v7.0.3...v7.0.6) Updates `cross-spawn` from 6.0.5 to 7.0.6 - [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md) - [Commits](moxystudio/node-cross-spawn@v7.0.3...v7.0.6) --- updated-dependencies: - dependency-name: cross-spawn dependency-type: indirect - dependency-name: cross-spawn dependency-type: indirect ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump CLI version from 1.35.1 to 1.37.4 (#1065) Co-authored-by: amitjoshi <[email protected]> * December Release Notes (#1066) * add release note * fix typo --------- Co-authored-by: Sandeep Satheesh <[email protected]> * Remove unused command from package.json (#1070) Removing the command `extension.createChatView` from package.json as it is registered but there is no handler associated with it. * Fix error type for concurrency handler (#1071) Fixes the error throws in case of bulk header rejection * Add code coverage output folder in gitignore (#1072) Adding the code coverage output folder `.nyc_ouput` folder in `.gitignore` so that it doesn't show up in `git diff` after running code coverage command. * Refactor CLI Acquisition Context (#1074) * Refactor CLI Acquisition Context * Fix import * Add tests * [PowerPages] [Create-Site] Implement multi-step site creation with enhanced input handling (#1063) * Enhance CreateSiteCommand to include extension context and add ReadonlyFileSystemProvider for site page previews * Implement EditableFileSystemProvider for site page editing and update CreateSiteHelper to utilize it * Integrate CreateSiteCommand into CommandRegistry and update related components for site creation functionality * Disable copy functionality in EditableFileSystemProvider implementation * Remove ReadonlyFileSystemProvider implementation * Add telemetry constant for previewing site pages and refactor related components * Refactor CommandRegistry and add command registration utility for chat participants * Add constants for site creation parameters and refactor NL2SiteService to use them * Refactor CreateSiteCommand and CreateSiteHelper to use structured options and improve readability; add CreateSiteTypes for better type management * Add error telemetry constant for previewing site pages and handle errors in previewSitePagesContent function * Rename fileContentMap to _fileContentMap for consistency and clarity in EditableFileSystemProvider * Implement multi-step input for site creation and register command for user inputs in CreateSiteHelper * Enhance copilot availability checks and update response structure in ArtemisService * Add site creation inputs and environment info interfaces; refactor CreateSiteCommand * Update src/common/chat-participants/powerpages/commands/create-site/CreateSiteTypes.ts Co-authored-by: Priyanshu Agrawal <[email protected]> * Update src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts Co-authored-by: Priyanshu Agrawal <[email protected]> * Update src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts Co-authored-by: Priyanshu Agrawal <[email protected]> * Add eslint directives to suppress no-explicit-any warnings in site page handling --------- Co-authored-by: amitjoshi <[email protected]> Co-authored-by: Priyanshu Agrawal <[email protected]> * Fix accessibility issue in Pages Copilot pane (#1076) Fix accessibility issue in `Send` button in Power Pages Copilot pane. Updated the `aria-label` to `Send` instead of `Match Case` * Fix casing of OrgUrl to orgUrl in site creation input handling (#1077) Co-authored-by: amitjoshi <[email protected]> * Add test scripts in .vscode\tasks.json (#1078) To be able to easily run tests from VS Code, added test scripts in `tasks.json` * Remove redundant step from gulp file (#1079) The step `testWeb` is redundant as the step `testUnitTests` already includes the test pattern defined in `testWeb` step. Hence, removing it from gulp config. * Added site runtime preview code behind ECS Config (#1052) * Added site runtime preview code behind ECS Config * localization added and moved preview site code to separate file * fixed build failures and handled empty website recordID * Corrected website details ppapi service for runtime preview * merged main branch to runtimePreviewECS * correcting function call for runtime preview * corrected localised error * removed bug of no show command found * Launch Edge new tab instead of launching project --------- Co-authored-by: Ashwani Kumar <[email protected]> Co-authored-by: Priyanshu Agrawal <[email protected]> * Hide preview site command from palette when feature is disabled (#1080) * Enhance feedback interaction by adding keyboard accessibility for thumbs up/down buttons (#1082) Co-authored-by: amitjoshi <[email protected]> * Add conditional visibility for Current Active Users command in web virtual workspace and remove alt cmds (#1083) Co-authored-by: amitjoshi <[email protected]> * Handle null language code for content snippets and update query parameters in portal schema (#1086) Co-authored-by: amitjoshi <[email protected]> * Enhance accessibility by adding ARIA attributes and keyboard navigation support (#1085) Co-authored-by: amitjoshi <[email protected]> * Enhance accessibility by adding ARIA attributes and making thinking message focusable (#1087) Co-authored-by: amitjoshi <[email protected]> * Add sovereign cloud endpoints for PPAPI service (#1081) * Site Preview Enhancements: Clear cache and telemetry logging (#1088) * Enhance accessibility by improving focus styles and adding ARIA attributes in notification panel (#1089) Co-authored-by: amitjoshi <[email protected]> * Actions Hub: Implement base classes to show tree structure (#1090) * Add models for Actions Hub * Basic wireup * Add models * ToDo * Remove foo environment * Remove error * Refactor * Add translations * Add tests * Add tree item for No sites found * Add translations * Added changes for orgGeo (#1093) * Refactor: Move actions-hub and preview-site folder under power-pages (#1092) * Refactor: Move actions-hub and preview-site folder under power-pages * Remove unwanted file * Refactor * Add PPAPI to known words * Disable actions hub by default * Web Extension: Remove dependency on website preview URL (#1091) * Web Extension: Remove dependency on website preview URL * Translations * Web Extension: Fix file opening for media files (#1094) Using `vscode.open` API to open the default file instead of `window.showTextDocument` because `showTextDocument` doesn't open binary files to it was failing to open non-text files. * Bump CLI version from 1.37.4 to 1.39.3 (#1095) * Fix environment icon for dark mode (#1097) * Fix environment icon for dark mode * Refactor * Fix test * fix screen reader issue in copilot chat (#1100) Co-authored-by: Sandeep Satheesh <[email protected]> * add focus ring (#1099) Co-authored-by: Sandeep Satheesh <[email protected]> * add release notes (#1101) Co-authored-by: Sandeep Satheesh <[email protected]> * Site Preview: Initialize after ECS fetch (#1102) * Site Preview: Initialize after ECS fetch * Set context after loading website URL * Add source attribute handling and telemetry for invalid attributes in web extension (#1105) * Add source attribute handling and telemetry for invalid attributes in web extension * Refactor source attribute handling in processDataAndCreateFile function --------- Co-authored-by: amitjoshi <[email protected]> * Enhance Actions Hub with environment support and unit tests (#1098) * Enhance Actions Hub: Add support for environment retrieval and localization updates * Add unit tests for ActionsHubTreeDataProvider functionality --------- Co-authored-by: amitjoshi <[email protected]> * Fix A11y bug (#1108) * Actions Hub: Add toggle based on ECS (#1107) * Add feature flag for Actions Hub * Refactor * Fix errors * Fix errors * Add unit tests * [PowerPages] [Site Preview] Simplify preview functionality (#1109) * Refactor telemetry handling by removing ITelemetry parameter from functions (#1110) * Refactor telemetry handling by removing ITelemetry parameter from sendTelemetryEvent and related functions * Refactor: Remove telemetry parameter from getEndpoint and related functions * Refactor: Remove telemetry parameter from authentication functions and related calls * Refactor: Remove telemetry parameter from getArtemisResponse call in activate function * Refactor: Remove telemetry parameter from various authentication and service calls --------- Co-authored-by: amitjoshi <[email protected]> * Webpack: Ignore warnings (#1113) * Run desktop integration tests in CI (#1114) * Run desktop integration tests in CI * Run desktop test only on Windows * Enhance Actions Hub with environment retrieval and refresh functionality (#1111) * Enhance Actions Hub: Add support for environment retrieval and localization updates * Add unit tests for ActionsHubTreeDataProvider functionality * Add AuthManager class and AuthInfo interface for authentication handling * Enhance authentication handling: Add auth info extraction and update environment info retrieval in Actions Hub * Add refresh command and event handling for Actions Hub environment changes * Refactor ActionsHubTreeDataProvider: Manual refresh should trigger Pac * Remove AuthManager class and related authentication logic * Add error handling to refresh command in ActionsHubTreeDataProvider * RemoveDependencyOnOldTelemetryCluster * Refactor ActionsHubTreeDataProvider tests to include PacTerminal in initialization * Remove telemetry dependency from intelligence API endpoint retrieval * Refactor telemetry handling by removing ITelemetry parameter from sendTelemetryEvent and related functions * Refactor: Remove telemetry parameter from getEndpoint and related functions * Refactor: Remove telemetry parameter from authentication functions and related calls * Refactor telemetry event assertion in AuthenticationProvider tests * Refactor: Remove telemetry parameter from getArtemisResponse call in activate function * Refactor: Remove telemetry parameter from various authentication and service calls * Refactor: Rename AuthManager to PacAuthManager for consistency * Refactor: Use pacWrapper for activeAuth retrieval in ActionsHubTreeDataProvider * Add ACTIONS_HUB_REFRESH_FAILED constant and update error logging in ActionsHubTreeDataProvider * Remove unused AuthInfo interface from Constants.ts * Refactor ActionsHub initialization by removing authentication handling and simplifying the process * Add localization for expired environment message and update refresh title --------- Co-authored-by: amitjoshi <[email protected]> * Remove old telemetry usage (#1112) * Remove old telemetry usage * Remove more usage * Remove more usage * Remove more usage * Remove more usage * Fix tests * Fix typo * Comment tests * Refactor Actions Hub and Preview Site initialization logic (#1115) * [PowerPages][Actions Hub] Toggle to Change Environment (#1119) * Add switch environment functionality to Actions Hub * Update icon for switch environment command in Actions Hub * Update translation for switch environment title to "Change Environment" * Add tests for switch environment command registration and error handling * Remove redundant error handling tests for switch environment and refresh commands * Refactor command names to include namespace for actions in Actions Hub * Update refresh command registration to include namespace in Actions Hub tests --------- Co-authored-by: amitjoshi <[email protected]> * Bump serialize-javascript, gulp-mocha and mocha (#1120) Bumps [serialize-javascript](https://github.com/yahoo/serialize-javascript) to 6.0.2 and updates ancestor dependencies [serialize-javascript](https://github.com/yahoo/serialize-javascript), [gulp-mocha](https://github.com/sindresorhus/gulp-mocha) and [mocha](https://github.com/mochajs/mocha). These dependencies need to be updated together. Updates `serialize-javascript` from 5.0.1 to 6.0.2 - [Release notes](https://github.com/yahoo/serialize-javascript/releases) - [Commits](yahoo/serialize-javascript@v5.0.1...v6.0.2) Updates `gulp-mocha` from 8.0.0 to 10.0.1 - [Release notes](https://github.com/sindresorhus/gulp-mocha/releases) - [Commits](sindresorhus/gulp-mocha@v8.0.0...v10.0.1) Updates `mocha` from 9.2.2 to 11.1.0 - [Release notes](https://github.com/mochajs/mocha/releases) - [Changelog](https://github.com/mochajs/mocha/blob/main/CHANGELOG.md) - [Commits](mochajs/mocha@v9.2.2...v11.1.0) --- updated-dependencies: - dependency-name: serialize-javascript dependency-type: indirect - dependency-name: gulp-mocha dependency-type: direct:development - dependency-name: mocha dependency-type: direct:development ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * [Power Pages][Actions Hub] Add "Show Environment Details" to Actions Hub (#1118) * [Power Pages][Actions Hub] Add "Show Environment Details" to Actions Hub * [PowerPages][Actions Hub] Update icon for switch environment command * [PowerPages][Actions Hub] Remove refresh command from Actions Hub navigation * Fix order * Update translation identifiers and fix newline in localization files * Refactor Actions Hub commands and extract command handlers into a new file * Add tests for switchEnvironment command * [PowerPages][ActionsHub] - Show Active and Inactive Sites (#1117) * show active and inactive sites in actions hub view * add integration tests for actions hub * [Power Pages][Actions Hub] Add NoSitesTreeItem to handle empty site groups * [Power Pages][Actions Hub] Update website details property names to PascalCase and change collapsible state to expanded * Refactor environment handling: rename method for clarity, improve error handling, and streamline environment selection process * Update localization strings for environment selection and session details * Refactor authentication and organization data structures; update TypeScript configuration and remove unused PacAuthManager * Add ArtemisContext for managing service responses; update ActionsHub and related components to utilize new context --------- Co-authored-by: Sandeep Satheesh <[email protected]> Co-authored-by: Priyanshu Agrawal <[email protected]> * [PowerPages][Actions Hub] Add Instance URL to Environment Details (#1122) * Add 'isCurrent' property and description to site tree items (#1123) * Update ArtemisService URLs to use production segment for GCC, high, mooncake, and DOD environments (#1121) Co-authored-by: amitjoshi <[email protected]> * [PowerPages][Actions Hub] Add commands to open active and inactive site in PP Studio (#1124) * [PowerPages][Actions Hub] Add commands to open active and inactive sites in Power Pages Studio * Refactor studio URL handling to use constants for endpoints * Add localization entries for "Current" and "Instance url" in JSON and XLF files * Refactor studio URL generation to include environment ID and add openInactiveSitesInStudio function * [Power Pages][Actions Hub] Add ability to preview active sites in VS Code (#1127) * Add preview command and siteInfo property to SiteTreeItem * Add unit tests for PreviewSite.isSiteRuntimePreviewEnabled method * Refactor PreviewSite initialization and loadSiteUrl method; update tests for loadSiteUrl functionality * Add tests for ActionsHubTreeDataProvider.getChildren method; mock website data for active and inactive sites * Add mocks for active and all websites in ActionsHubTreeDataProvider tests * Refactor site context values to distinguish between current and non-current active sites; update related tests * Add websiteId property to IWebsiteInfo interface and update related tree item implementations * Update walkthrough title to use h4 and enhance focus indicator for accessibility (#1125) Co-authored-by: amitjoshi <[email protected]> * Add new command to create authentication profile and handle errors in actions hub (#1128) * Add new command to create authentication profile and handle errors in actions hub * Refactor tests for ActionsHubCommandHandlers and ActionsHubTreeDataProvider to improve argument validation and remove unnecessary assertions * Stub AuthInfo and OrgInfo getters in ActionsHubTreeDataProvider tests to improve test reliability * Remove redundant test for creating new auth profile in ActionsHubCommandHandlers * Fix async initialization in ActionsHub and streamline environment info handling in ActionsHubTreeDataProvider * Implement createAuthProfileExp function and add tests for authentication profile creation * Refactor createAuthProfileExp function import path and add new utility file for authentication profile creation * Add tests for ActionsHubTreeDataProvider to validate site retrieval and error handling * Remove redundant tests from ActionsHubTreeDataProvider for getChildren method * Refactor import path for createAuthProfileExp to new utility file in PacAuthUtil * Remove tests for PacAuthUtils as part of code cleanup * Add comment to clarify DV authentication requirement in createNewAuthProfile --------- Co-authored-by: amitjoshi <[email protected]> * [Power Pages][Actions Hub] Show current site (#1129) * Refactor site context handling and update site data types for improved clarity * Refactor website data handling to use IWebsiteDetails and streamline site fetching logic * Refactor import statements in ActiveGroupTreeItem, EnvironmentGroupTreeItem, and InactiveGroupTreeItem for improved organization and clarity * Refactor import statements in InactiveGroupTreeItem for improved organization * Add tests for PacContext and improve context update logic * Add tests for previewSite functionality in ActionsHubCommandHandlers * Add fetchWebsites function and related tests for website retrieval logic * Add tests for ActiveGroupTreeItem and update siteInfo assertions * Add tests for ActionsHubTreeDataProvider to verify website loading logic * Add websiteid to VSCode settings for improved configuration * Refactor ActionsHubTreeDataProvider to simplify loadWebsites method and improve refresh logic * Refactor CurrentSiteContext to utilize utility functions for website YAML handling and improve code clarity * Refactor ActionsHubTreeDataProvider to streamline website loading logic and improve refresh handling * [Power Pages][Actions Hub] Update "Change environment" option to match Figma (#1130) * [Power Pages][Actions Hub] Update "Change environment" option to match Figma * Add test for environment switching in Actions Hub command handlers * Refactor environment switching to use detailed description and enhance test coverage for environment selection * Refactor tests for ActionsHubTreeDataProvider by removing redundant cases and ensuring proper stubbing of fetchWebsites * Disable website loading in ActionsHubTreeDataProvider tests to ensure empty result for specific scenarios * [Power Pages][Actions Hub] Add revealInOS command for current active site and update translations (#1131) * Refactor website details to use camelCase properties and add siteVisibility field (#1133) * [Power Pages][Actions Hub] Update command references for authentication profile (#1132) * [Power Pages][Actions Hub] Update command references for authentication profile * Update command reference for new authentication profile in ActionsHubTreeDataProvider test --------- Co-authored-by: amitjoshi <[email protected]> Co-authored-by: Priyanshu Agrawal <[email protected]> Co-authored-by: Priyanshu Agrawal <[email protected]> * [Power Pages][Actions Hub] Add open site management functionality (#1136) * [Power Pages][Actions Hub] Add open site management functionality * Update translation for "Open Site Management" to use lowercase in localization files * [PowerPages][ActionsHub] Upload site command with visibility confirmation (#1134) * Refactor website details to use camelCase properties and add siteVisibility field * [Power Pages][Actions Hub] Add upload site command with confirmation dialog * [Power Pages][Actions Hub] Implement upload site command with confirmation dialog and tests * [Power Pages][Actions Hub] Add site visibility check before upload confirmation dialog * [Power Pages][Actions Hub] Enhance upload site command to handle public and private site visibility * Update login message links and adjust Actions Hub feature gate settings * [Power Pages][Actions Hub] Update upload site tests to use warning message and reset stubs * Refactor upload site tests to use showInformationMessage and remove unnecessary stubs * Refactor uploadSite tests to use showInformationMessage and handle site visibility for public and private sites * Refactor uploadSite test to remove unused showInformationMessage stub * ``` Refactor uploadSite tests to improve clarity and maintainability ``` * Stub showInformationMessage in ActionsHubCommandHandlers tests for improved test isolation * Refactor ActionsHubCommandHandlers tests to improve test isolation by removing unused stubs and enhancing setup * Refactor test setup in ActionsHubCommandHandlers for improved isolation --------- Co-authored-by: Priyanshu Agrawal <[email protected]> Co-authored-by: amitjoshi <[email protected]> * Update login command reference and add upload site title in localization file (#1138) Co-authored-by: amitjoshi <[email protected]> * [Power Pages] Add title for Copilot feedback Submit button (#1139) * [PowerPages] [Actions Hub] Add support for other sites in Actions Hub (#1140) * Add support for other sites in Actions Hub and update related interfaces * Update tests for OtherSitesGroupTreeItem and ActionsHubTreeDataProvider to include handling of other sites * Refactor findOtherSites function to accept fs and yaml modules as parameters for improved testability * Add mockFs and mockYaml to ActionsHubCommandHandlers tests for improved isolation * Remove test for YAML parsing errors in ActionsHubCommandHandlers to streamline error handling * Disable Actions Hub Panel by default in fallback configuration * Clarify comment on dataModelVersion in OtherSitesGroupTreeItem to specify defaulting behavior for other sites * Add js-yaml dependency and update YAML parsing in WorkspaceInfoFinderUtil * Remove yaml dependency from package.json and package-lock.json * Replace js-yaml with yaml package and update YAML parsing methods * Refactor findOtherSites and createKnownSiteIdsSet to use Set for known site IDs * Refactor findOtherSites and createKnownSiteIdsMap to use Set for known site IDs --------- Co-authored-by: amitjoshi <[email protected]> * [Power Pages][Actions Hub] Add site details command (#1141) * [Power Pages][Actions Hub] Add site details command * Add openSiteManagement function with error handling and tests * Fix menu item order * [Power Pages] [Actions Hub] Add download site command to Actions Hub (#1143) * [Power Pages][Actions Hub] Add download site command * Add tests * [PowerPages][ActionsHub] Upload functionality for other sites (#1144) * [Power Pages] Enhance upload functionality to support other sites in Actions Hub * [Power Pages] Enhance uploadSite functionality with confirmation handling and error management * [Power Pages][Actions Hub] Refactor test setup by removing unused stubs in ActionsHubCommandHandlers tests * [Power Pages][Actions Hub] Remove unused error handling test for uploadSite and update data model version for other sites * [Power Pages][Actions Hub] Remove unused test for upload handling of other sites in ActionsHubCommandHandlers --------- Co-authored-by: amitjoshi <[email protected]> * [Power Pages] [Actions Hub] Add 'Open in Power Pages Studio' command (#1145) * [Power Pages][Preview Site] Show notification when previewing site with pending changes (#1146) * [Power Pages][Preview Site] Show notification when previewing site with pending changes * [Power Pages][Localization] Update localization strings for site preview notifications * [Power Pages][Preview Site] Enhance site preview functionality with site visibility and upload path adjustments * [Power Pages][PacInterop] Update PAC executable path to use dynamic CLI path * Refactor uploadSite function calls to include an empty string parameter for consistency * Refactor uploadSite to use logical OR for websitePath fallback * [Power Pages] [Actions Hub] Enhance revealInOS command to support other sites (#1147) * [Power Pages] [Actions Hub] Initialize OrgChangeNotifier instance in activate function for all workspaces (#1148) Co-authored-by: amitjoshi <[email protected]> * [Power Pages] [Actions Hub] Handle null or undefined labels in ActionsHubTreeItem (#1149) * [Power Pages] [Actions Hub] Handle null or undefined labels in ActionsHubTreeItem * [Power Pages] [Actions Hub] Refactor isCurrent logic and enhance tests for ActiveGroupTreeItem * Fix typos and improve file path handling in tests (#1151) - ✏️ Corrected spelling of "Pattern" in test names - 🔧 Updated file path handling to use vscode.Uri.file - 📂 Simplified file path construction for better readability -Priyanshu * Remove outline from feedback button styles (#1150) - ✨ Removed the outline property from button styles for improved accessibility. -Priyanshu * [Power Pages] [Copilot] Add localization for Copilot response and code block messages; enhance accessibility with live regions for screen readers (#1152) Co-authored-by: amitjoshi <[email protected]> * Fix typos in OneDSLogger and oneDSLoggerWrapper (#1153) - ✏️ Corrected spelling of 'initialize' in OneDSLogger and oneDSLoggerWrapper classes - 🔧 Updated variable name from 'oneDSLoggerIntance' to 'oneDSLoggerInstance' for consistency -Priyanshu --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: amitjoshi438 <[email protected]> Co-authored-by: amitjoshi <[email protected]> Co-authored-by: Ashwani Kumar <[email protected]> Co-authored-by: Ashwani Kumar <[email protected]> Co-authored-by: tyaginidhi <[email protected]> Co-authored-by: Priyanshu Agrawal <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sandeep Satheesh <[email protected]> Co-authored-by: Priyanshu Agrawal <[email protected]> Co-authored-by: BidishaMS <[email protected]>
1 parent 5db25ce commit d6d2968

35 files changed

+1612
-172
lines changed
 

‎l10n/bundle.l10n.json

+30-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@
5757
"Working on it...": "Working on it...",
5858
"You can use this in <a href=\"#\" id=\"github-copilot-link\">GitHub Copilot with @powerpages</a> and leverage best of both world.": "You can use this in <a href=\"#\" id=\"github-copilot-link\">GitHub Copilot with @powerpages</a> and leverage best of both world.",
5959
"NEW": "NEW",
60+
"Copilot Response": "Copilot Response",
61+
"Code Block ": "Code Block ",
62+
" End of Code Block ": " End of Code Block ",
6063
"Copied to clipboard!": "Copied to clipboard!",
6164
"What do you need help with?": "What do you need help with?",
6265
"Make sure AI-generated content is accurate and appropriate before using. <a href=\"https://go.microsoft.com/fwlink/?linkid=2240145\">Learn more</a> | <a href=\"https://go.microsoft.com/fwlink/?linkid=2189520\">View\n terms</a>": "Make sure AI-generated content is accurate and appropriate before using. <a href=\"https://go.microsoft.com/fwlink/?linkid=2240145\">Learn more</a> | <a href=\"https://go.microsoft.com/fwlink/?linkid=2189520\">View\n terms</a>",
@@ -109,7 +112,6 @@
109112
]
110113
},
111114
"Opening site preview...": "Opening site preview...",
112-
"The preview shown is for published changes.": "The preview shown is for published changes.",
113115
"Site runtime preview feature is not enabled.": "Site runtime preview feature is not enabled.",
114116
"No workspace folder opened. Please open a site folder to preview.": "No workspace folder opened. Please open a site folder to preview.",
115117
"Initializing site preview. Please try again after few seconds.": "Initializing site preview. Please try again after few seconds.",
@@ -129,6 +131,8 @@
129131
},
130132
"Authenticating...": "Authenticating...",
131133
"Unable to clear cache": "Unable to clear cache",
134+
"Your preview isn't updated. Please upload your site to see the latest changes.": "Your preview isn't updated. Please upload your site to see the latest changes.",
135+
"Upload changes": "Upload changes",
132136
"Enter the name of the web template": "Enter the name of the web template",
133137
"Please enter a name for the web template.": "Please enter a name for the web template.",
134138
"A webtemplate with the same name already exists. Please enter a different name.": "A webtemplate with the same name already exists. Please enter a different name.",
@@ -193,6 +197,13 @@
193197
"Changing environment...": "Changing environment...",
194198
"Current": "Current",
195199
"Site management URL not found for the selected site. Please try again after refreshing the environment.": "Site management URL not found for the selected site. Please try again after refreshing the environment.",
200+
"Be careful when you're updating public sites. The changes you make are visible to anyone immediately. Do you want to continue?": "Be careful when you're updating public sites. The changes you make are visible to anyone immediately. Do you want to continue?",
201+
"Yes": "Yes",
202+
"Current site path not found.": "Current site path not found.",
203+
"Site Details": "Site Details",
204+
"Browse...": "Browse...",
205+
"Select the folder that will contain your project root for your site": "Select the folder that will contain your project root for your site",
206+
"Select Folder": "Select Folder",
196207
"Timestamp: {0}/{0} is the timestamp": {
197208
"message": "Timestamp: {0}",
198209
"comment": [
@@ -253,6 +264,24 @@
253264
"{0} is the cluster geo name"
254265
]
255266
},
267+
"Friendly name: {0}/{0} is the website name": {
268+
"message": "Friendly name: {0}",
269+
"comment": [
270+
"{0} is the website name"
271+
]
272+
},
273+
"Website ID: {0}/{0} is the website ID": {
274+
"message": "Website ID: {0}",
275+
"comment": [
276+
"{0} is the website ID"
277+
]
278+
},
279+
"Data model version: v{0}/{0} is the data model version": {
280+
"message": "Data model version: v{0}",
281+
"comment": [
282+
"{0} is the data model version"
283+
]
284+
},
256285
"PAC Telemetry enabled": "PAC Telemetry enabled",
257286
"Failed to enable PAC telemetry.": "Failed to enable PAC telemetry.",
258287
"PAC Telemetry disabled": "PAC Telemetry disabled",

‎loc/translations-export/vscode-powerplatform.xlf

+61-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
33
<file original="bundle" source-language="en" datatype="plaintext"><body>
4+
<trans-unit id="++CODE++08833dda22312dacb09f1d185caedf751289ee89a4c75682a09f4a262bf67ac4">
5+
<source xml:lang="en"> End of Code Block </source>
6+
</trans-unit>
47
<trans-unit id="++CODE++29bb69945e50cb632fabf16f3f89a25d813910c19059d8a792f66262a952039d">
58
<source xml:lang="en">@PowerPages is not yet available in your region.</source>
69
</trans-unit>
@@ -60,6 +63,12 @@
6063
<trans-unit id="++CODE++bf27ec2e917b9f2578f8e6372f8656370d24cffe2c5a7b900d0b3280f8ec69fb">
6164
<source xml:lang="en">Be careful making changes. Anyone can see the changes you make immediately. Choose Edit the site to make edits, or close the editor tab to cancel without editing.</source>
6265
</trans-unit>
66+
<trans-unit id="++CODE++6fab20ee9c8b4cb1fa10d787f3cd4eac13d34d27050fd193162a2399f690f59c">
67+
<source xml:lang="en">Be careful when you're updating public sites. The changes you make are visible to anyone immediately. Do you want to continue?</source>
68+
</trans-unit>
69+
<trans-unit id="++CODE++c58a6bd402efb06c3e7dfd6d68910412fee2dfc1788ae384dc123089ce912285">
70+
<source xml:lang="en">Browse...</source>
71+
</trans-unit>
6372
<trans-unit id="++CODE++19766ed6ccb2f4a32778eed80d1928d2c87a18d7c275ccb163ec6709d3eb2e27">
6473
<source xml:lang="en">Cancel</source>
6574
</trans-unit>
@@ -114,6 +123,9 @@ The second line should be '[TRANSLATION HERE](https://aka.ms/pages-clear-cache).
114123
<source xml:lang="en">Cluster geo name: {0}</source>
115124
<note>{0} is the cluster geo name</note>
116125
</trans-unit>
126+
<trans-unit id="++CODE++51e495f295ba55f724b77298746e3284ff9fc8e1e582828a9a3f4051fc577cb0">
127+
<source xml:lang="en">Code Block </source>
128+
</trans-unit>
117129
<trans-unit id="++CODE++eebdd24a77d9ad32222660c07777163bf5f6732df2b172351f3f8d5783e4f529">
118130
<source xml:lang="en">Confirm</source>
119131
</trans-unit>
@@ -123,6 +135,9 @@ The second line should be '[TRANSLATION HERE](https://aka.ms/pages-clear-cache).
123135
<trans-unit id="++CODE++916fa4a51e4d90e09a42289aa7961a6aff3401424f633240c630ac47f58a6d30">
124136
<source xml:lang="en">Copied to clipboard!</source>
125137
</trans-unit>
138+
<trans-unit id="++CODE++9b44302f1c5e52f926415b952b83efce47fce76164fa64f1a42d1a1606efa11d">
139+
<source xml:lang="en">Copilot Response</source>
140+
</trans-unit>
126141
<trans-unit id="++CODE++b04e6767bf84555605710be1a94e48ddcbcb820353b859da427d189cf25395b4">
127142
<source xml:lang="en">Copilot illustration</source>
128143
</trans-unit>
@@ -136,6 +151,13 @@ The second line should be '[TRANSLATION HERE](https://aka.ms/pages-clear-cache).
136151
<trans-unit id="++CODE++e0d1b68224bf0b31ef16b206c65b5f8f6b89d18161f4a7cdcb8d0ac8d952549a">
137152
<source xml:lang="en">Current</source>
138153
</trans-unit>
154+
<trans-unit id="++CODE++fc47ac56c8a4446c02d8470168a888d8e0040cfc0bb5ce6173e324dccc72f24c">
155+
<source xml:lang="en">Current site path not found.</source>
156+
</trans-unit>
157+
<trans-unit id="++CODE++1da89bd5f5df020ca47c79dfdbc1517a299ca77570df2de4132f53797d5f2412">
158+
<source xml:lang="en">Data model version: v{0}</source>
159+
<note>{0} is the data model version</note>
160+
</trans-unit>
139161
<trans-unit id="++CODE++5bd78fcb75f98d65ab8f89a8f0b6681c25c13308fa8643c44ab459b7783c06c9">
140162
<source xml:lang="en">Default Environment: {0}</source>
141163
<note>The {0} represents profile's resource/environment URL</note>
@@ -223,6 +245,10 @@ The {3} represents Solution's Type (Managed or Unmanaged), but that test is loca
223245
<trans-unit id="++CODE++1b93e507157d91755f08e1abfe8a33d725b637a32ddadf149420b25907d440a0">
224246
<source xml:lang="en">File(s) already exist. No new files to add</source>
225247
</trans-unit>
248+
<trans-unit id="++CODE++56281cd1d0090cc4c7c38634a5738f51769927efda74805ba19e30e35868fd4e">
249+
<source xml:lang="en">Friendly name: {0}</source>
250+
<note>{0} is the website name</note>
251+
</trans-unit>
226252
<trans-unit id="++CODE++7184a5edf42e6fc9ef8f53fc1dd73f10b3f6196a23f6d340c4f434e60c366e45">
227253
<source xml:lang="en">GETTING STARTED</source>
228254
</trans-unit>
@@ -450,6 +476,9 @@ The {3} represents Dataverse Environment's Organization ID (GUID)</note>
450476
<trans-unit id="++CODE++0134bc37f2fdeb11daa9e760ecee0ef9706989b33f77a38daadb8b7aae685749">
451477
<source xml:lang="en">Saving your file ...</source>
452478
</trans-unit>
479+
<trans-unit id="++CODE++e597da9c984517f802536a83c825cd4f017898c5f28030fe1f62fb94f5228828">
480+
<source xml:lang="en">Select Folder</source>
481+
</trans-unit>
453482
<trans-unit id="++CODE++6e26632772595a9d2fdf17191f6d7b59983c308d884deed6ba317054f7d30688">
454483
<source xml:lang="en">Select Folder for new PCF Control</source>
455484
<note>Do not translate 'PCF' as it is a product name.</note>
@@ -460,6 +489,9 @@ The {3} represents Dataverse Environment's Organization ID (GUID)</note>
460489
<trans-unit id="++CODE++87468e6a845b82466220c11dd0ca315a4389dc1cb8e0ee102035bb4ce5cb7e39">
461490
<source xml:lang="en">Select an environment</source>
462491
</trans-unit>
492+
<trans-unit id="++CODE++26297069e403c73e147151f2964757b7e9149762cf3a8e04ffd833907fb979e4">
493+
<source xml:lang="en">Select the folder that will contain your project root for your site</source>
494+
</trans-unit>
463495
<trans-unit id="++CODE++4d6f03fa73020c945e651c34d40c5529f3477ac02df254c1764a68d576575e14">
464496
<source xml:lang="en">Selection is empty.</source>
465497
</trans-unit>
@@ -472,6 +504,9 @@ The {3} represents Dataverse Environment's Organization ID (GUID)</note>
472504
<trans-unit id="++CODE++899fb203e6c2faac8093e21a2fa8db0d4b13d16ea5492461d8b72dbcee3ecf2a">
473505
<source xml:lang="en">Show Output Panel</source>
474506
</trans-unit>
507+
<trans-unit id="++CODE++524bca0347e50e3801bebf27374124b3babf072729b4da4bcc45ef5c385ee8f4">
508+
<source xml:lang="en">Site Details</source>
509+
</trans-unit>
475510
<trans-unit id="++CODE++a72357d268bce5e1643bfd74563495784179f75ace0d652db8f808f293586b63">
476511
<source xml:lang="en">Site management URL not found for the selected site. Please try again after refreshing the environment.</source>
477512
</trans-unit>
@@ -557,6 +592,9 @@ The {3} represents Dataverse Environment's Organization ID (GUID)</note>
557592
<trans-unit id="++CODE++4b8d64fae5d1cbf5f7324782e9b5c430db7c218771e84b0b402a9a4d0ec4bf05">
558593
<source xml:lang="en">Unmanaged</source>
559594
</trans-unit>
595+
<trans-unit id="++CODE++7fce0124a7cb74955461f08e39ceaaed1e2850492f077f4e6defd82ef3ca6b0e">
596+
<source xml:lang="en">Upload changes</source>
597+
</trans-unit>
560598
<trans-unit id="++CODE++696d5f7b79a75961bc5d99ecbbea7b535acc73160e0b6422daa45b0ae7c04368">
561599
<source xml:lang="en">User: {0}</source>
562600
<note>The {0} represents auth profile's user name (email address))</note>
@@ -573,6 +611,10 @@ The {3} represents Dataverse Environment's Organization ID (GUID)</note>
573611
<trans-unit id="++CODE++00dc171124ab430bbbaae51ec39dda1c5e7d045f382f56b1767d9e733292731c">
574612
<source xml:lang="en">Webpage names should contain only letters, numbers, hyphens, or underscores.</source>
575613
</trans-unit>
614+
<trans-unit id="++CODE++091327257429303159b8580005b5b5a1e05fd028030817edaaa835cfcb0b3293">
615+
<source xml:lang="en">Website ID: {0}</source>
616+
<note>{0} is the website ID</note>
617+
</trans-unit>
576618
<trans-unit id="++CODE++aa64cfbb63d3382bc0530c6e599467b1cc5c1ef8ff3b573d37ad628f9058b2aa">
577619
<source xml:lang="en">Website not found in the environment. Please check the credentials and login with correct account.</source>
578620
</trans-unit>
@@ -594,12 +636,18 @@ The {3} represents Dataverse Environment's Organization ID (GUID)</note>
594636
<trans-unit id="++CODE++9265d4c9f6b10b1a95877bcccee77e9d164e8a51ab17104b14ba19e7935aef0e">
595637
<source xml:lang="en">Write web API code to query active contact records.</source>
596638
</trans-unit>
639+
<trans-unit id="++CODE++85a39ab345d672ff8ca9b9c6876f3adcacf45ee7c1e2dbd2408fd338bd55e07e">
640+
<source xml:lang="en">Yes</source>
641+
</trans-unit>
597642
<trans-unit id="++CODE++3c65297a1fc0860c6993f5c8beb4341dd13b71d7f3957daba611adf04a6c971b">
598643
<source xml:lang="en">You are editing a live, public site </source>
599644
</trans-unit>
600645
<trans-unit id="++CODE++beb45bfdd4c3f6da9bb3687e4d9275790d086c1dffaec84839fa9ec9ce4e9ee2">
601646
<source xml:lang="en">You can use this in &lt;a href=&quot;#&quot; id=&quot;github-copilot-link&quot;&gt;GitHub Copilot with @powerpages&lt;/a&gt; and leverage best of both world.</source>
602647
</trans-unit>
648+
<trans-unit id="++CODE++eeb25aaca6a9e3f6c670334480b8e56e2a362ecfec23f8e63617915b22ddc8a0">
649+
<source xml:lang="en">Your preview isn't updated. Please upload your site to see the latest changes.</source>
650+
</trans-unit>
603651
<trans-unit id="++CODE++3a928d0a6dd0e0c6b56eb3bf08b20e6361e876fd6b2eeca59f718cbfdc310472">
604652
<source xml:lang="en">dotnet sdk 6.0 or greater must be installed</source>
605653
<note>Do not translate 'dotnet' or 'sdk'</note>
@@ -674,6 +722,9 @@ The fifth line should be '[TRANSLATION HERE](command:powerplatform-walkthrough.s
674722
<trans-unit id="pacCLI.openDocumentation.title">
675723
<source xml:lang="en">Documentation</source>
676724
</trans-unit>
725+
<trans-unit id="microsoft.powerplatform.pages.actionsHub.activeSite.downloadSite.title">
726+
<source xml:lang="en">Download Site</source>
727+
</trans-unit>
677728
<trans-unit id="microsoft-powerapps-portals.walkthrough.title">
678729
<source xml:lang="en">Edit Power Pages code</source>
679730
</trans-unit>
@@ -702,7 +753,7 @@ The eighth line should be '[TRANSLATION HERE](command:powerplatform-walkthrough.
702753
<source xml:lang="en">Initialize Web Extension</source>
703754
</trans-unit>
704755
<trans-unit id="microsoft.powerplatform.pages.actionsHub.login">
705-
<source xml:lang="en">Login and connect to a Power Pages environment to use Power Pages actions. [Learn more](https://example.com/learn-more).
756+
<source xml:lang="en">Login and connect to a Power Pages environment to use Power Pages actions. [Learn more](https://go.microsoft.com/fwlink/?linkid=2305702).
706757
[Login](command:microsoft.powerplatform.pages.actionsHub.newAuthProfile)</source>
707758
<note>This is a Markdown formatted string, and the formatting must persist across translations.
708759
The second line should be '[TRANSLATION HERE](command:microsoft.powerplatform.pages.actionsHub.newAuthProfile).', keeping brackets and the text in the parentheses unmodified</note>
@@ -743,6 +794,9 @@ The second line should be '[TRANSLATION HERE](command:powerplatform-walkthrough.
743794
<trans-unit id="pacCLI.openPacLab.title">
744795
<source xml:lang="en">Open PAC Lab</source>
745796
</trans-unit>
797+
<trans-unit id="microsoft.powerplatform.pages.actionsHub.activeSite.openInStudio.title">
798+
<source xml:lang="en">Open in Power Pages Studio</source>
799+
</trans-unit>
746800
<trans-unit id="microsoft.powerplatform.pages.actionsHub.openSitesInStudio.title">
747801
<source xml:lang="en">Open in Power Pages Studio</source>
748802
</trans-unit>
@@ -816,6 +870,12 @@ The second line should be '[TRANSLATION HERE](command:powerplatform-walkthrough.
816870
<trans-unit id="pacCLI.pacSolutionHelp.title">
817871
<source xml:lang="en">Show PAC Solution Help</source>
818872
</trans-unit>
873+
<trans-unit id="microsoft.powerplatform.pages.actionsHub.siteDetails.title">
874+
<source xml:lang="en">Site Details</source>
875+
</trans-unit>
876+
<trans-unit id="microsoft.powerplatform.pages.actionsHub.activeSite.uploadSite.title">
877+
<source xml:lang="en">Upload Site</source>
878+
</trans-unit>
819879
<trans-unit id="microsoft-powerapps-portals.walkthrough.advancedCapabilities.description">
820880
<source xml:lang="en">Visual Studio Code for Web enables editing and publishing of web pages on your website.
821881

‎package-lock.json

+7-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+62-10
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@
167167
"when": "!virtualWorkspace && !pacCLI.authPanel.interactiveLoginSupported"
168168
},
169169
{
170-
"view":"microsoft.powerplatform.pages.actionsHub",
170+
"view": "microsoft.powerplatform.pages.actionsHub",
171171
"contents": "%microsoft.powerplatform.pages.actionsHub.login%",
172172
"when": "!virtualWorkspace && pacCLI.authPanel.interactiveLoginSupported"
173173
}
@@ -435,6 +435,22 @@
435435
{
436436
"command": "microsoft.powerplatform.pages.actionsHub.inactiveSite.openSiteManagement",
437437
"title": "%microsoft.powerplatform.pages.actionsHub.inactiveSite.openSiteManagement.title%"
438+
},
439+
{
440+
"command": "microsoft.powerplatform.pages.actionsHub.activeSite.uploadSite",
441+
"title": "%microsoft.powerplatform.pages.actionsHub.activeSite.uploadSite.title%"
442+
},
443+
{
444+
"command": "microsoft.powerplatform.pages.actionsHub.siteDetails",
445+
"title": "%microsoft.powerplatform.pages.actionsHub.siteDetails.title%"
446+
},
447+
{
448+
"command": "microsoft.powerplatform.pages.actionsHub.activeSite.downloadSite",
449+
"title": "%microsoft.powerplatform.pages.actionsHub.activeSite.downloadSite.title%"
450+
},
451+
{
452+
"command": "microsoft.powerplatform.pages.actionsHub.activeSite.openInStudio",
453+
"title": "%microsoft.powerplatform.pages.actionsHub.activeSite.openInStudio.title%"
438454
}
439455
],
440456
"configuration": {
@@ -915,6 +931,22 @@
915931
{
916932
"command": "microsoft.powerplatform.pages.actionsHub.inactiveSite.openSiteManagement",
917933
"when": "never"
934+
},
935+
{
936+
"command": "microsoft.powerplatform.pages.actionsHub.activeSite.uploadSite",
937+
"when": "never"
938+
},
939+
{
940+
"command": "microsoft.powerplatform.pages.actionsHub.siteDetails",
941+
"when": "never"
942+
},
943+
{
944+
"command": "microsoft.powerplatform.pages.actionsHub.activeSite.downloadSite",
945+
"when": "never"
946+
},
947+
{
948+
"command": "microsoft.powerplatform.pages.actionsHub.activeSite.openInStudio",
949+
"when": "never"
918950
}
919951
],
920952
"view/title": [
@@ -1041,27 +1073,47 @@
10411073
{
10421074
"command": "microsoft.powerplatform.pages.actionsHub.activeSite.preview",
10431075
"when": "view == microsoft.powerplatform.pages.actionsHub && (viewItem == currentActiveSite || viewItem == nonCurrentActiveSite)",
1044-
"group": "activeSiteContextMenuGroup@1"
1076+
"group": "siteAction@1"
1077+
},
1078+
{
1079+
"command": "microsoft.powerplatform.pages.actionsHub.activeSite.uploadSite",
1080+
"when": "view == microsoft.powerplatform.pages.actionsHub && (viewItem == currentActiveSite || viewItem == otherSite)",
1081+
"group": "siteAction@2"
1082+
},
1083+
{
1084+
"command": "microsoft.powerplatform.pages.actionsHub.activeSite.downloadSite",
1085+
"when": "view == microsoft.powerplatform.pages.actionsHub && (viewItem == currentActiveSite || viewItem == nonCurrentActiveSite)",
1086+
"group": "siteAction@3"
1087+
},
1088+
{
1089+
"command": "microsoft.powerplatform.pages.actionsHub.siteDetails",
1090+
"when": "view == microsoft.powerplatform.pages.actionsHub && (viewItem == currentActiveSite || viewItem == nonCurrentActiveSite || viewItem == inactiveSite)",
1091+
"group": "siteAction@4"
10451092
},
10461093
{
10471094
"command": "microsoft.powerplatform.pages.actionsHub.currentActiveSite.revealInOS.windows",
1048-
"when": "view == microsoft.powerplatform.pages.actionsHub && viewItem == currentActiveSite && isWindows",
1049-
"group": "activeSiteContextMenuGroup@2"
1095+
"when": "view == microsoft.powerplatform.pages.actionsHub && (viewItem == currentActiveSite || viewItem == otherSite) && isWindows",
1096+
"group": "siteAction@5"
10501097
},
10511098
{
10521099
"command": "microsoft.powerplatform.pages.actionsHub.currentActiveSite.revealInOS.mac",
1053-
"when": "view == microsoft.powerplatform.pages.actionsHub && viewItem == currentActiveSite && isMac",
1054-
"group": "activeSiteContextMenuGroup@2"
1100+
"when": "view == microsoft.powerplatform.pages.actionsHub && (viewItem == currentActiveSite || viewItem == otherSite) && isMac",
1101+
"group": "siteAction@5"
10551102
},
10561103
{
10571104
"command": "microsoft.powerplatform.pages.actionsHub.currentActiveSite.revealInOS.linux",
1058-
"when": "view == microsoft.powerplatform.pages.actionsHub && viewItem == currentActiveSite && isLinux",
1059-
"group": "activeSiteContextMenuGroup@2"
1105+
"when": "view == microsoft.powerplatform.pages.actionsHub && (viewItem == currentActiveSite || viewItem == otherSite) && isLinux",
1106+
"group": "siteAction@5"
10601107
},
10611108
{
10621109
"command": "microsoft.powerplatform.pages.actionsHub.inactiveSite.openSiteManagement",
10631110
"when": "view == microsoft.powerplatform.pages.actionsHub && viewItem == inactiveSite",
1064-
"group": "inactiveSiteContextMenuGroup@1"
1111+
"group": "siteAction@3"
1112+
},
1113+
{
1114+
"command": "microsoft.powerplatform.pages.actionsHub.activeSite.openInStudio",
1115+
"when": "view == microsoft.powerplatform.pages.actionsHub && (viewItem == currentActiveSite || viewItem == nonCurrentActiveSite)",
1116+
"group": "siteAction@6"
10651117
}
10661118
]
10671119
},
@@ -1283,7 +1335,7 @@
12831335
"vscode-languageserver": "^7.0.0",
12841336
"vscode-languageserver-textdocument": "^1.0.1",
12851337
"worker-loader": "^3.0.8",
1286-
"yaml": "^2.2.2"
1338+
"yaml": "^2.7.0"
12871339
},
12881340
"__metadata": {
12891341
"id": "b8680bb6-eaa9-481a-ae0b-83574fa58620",

‎package.nls.json

+6-2
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@
9999
"microsoft.powerplatform.pages.actionsHub.openSitesInStudio.title": "Open in Power Pages Studio",
100100
"microsoft.powerplatform.pages.actionsHub.activeSite.previewSite.title": "Preview",
101101
"microsoft.powerplatform.pages.actionsHub.login":{
102-
"message": "Login and connect to a Power Pages environment to use Power Pages actions. [Learn more](https://example.com/learn-more).\n[Login](command:microsoft.powerplatform.pages.actionsHub.newAuthProfile)",
102+
"message": "Login and connect to a Power Pages environment to use Power Pages actions. [Learn more](https://go.microsoft.com/fwlink/?linkid=2305702).\n[Login](command:microsoft.powerplatform.pages.actionsHub.newAuthProfile)",
103103
"comment": [
104104
"This is a Markdown formatted string, and the formatting must persist across translations.",
105105
"The second line should be '[TRANSLATION HERE](command:microsoft.powerplatform.pages.actionsHub.newAuthProfile).', keeping brackets and the text in the parentheses unmodified"
@@ -109,5 +109,9 @@
109109
"microsoft.powerplatform.pages.actionsHub.currentActiveSite.revealInOS.windows.title": "Reveal in Explorer",
110110
"microsoft.powerplatform.pages.actionsHub.currentActiveSite.revealInOS.mac.title": "Reveal in Finder",
111111
"microsoft.powerplatform.pages.actionsHub.currentActiveSite.revealInOS.linux.title": "Open Containing Folder",
112-
"microsoft.powerplatform.pages.actionsHub.inactiveSite.openSiteManagement.title": "Open site management"
112+
"microsoft.powerplatform.pages.actionsHub.inactiveSite.openSiteManagement.title": "Open site management",
113+
"microsoft.powerplatform.pages.actionsHub.activeSite.uploadSite.title": "Upload Site",
114+
"microsoft.powerplatform.pages.actionsHub.siteDetails.title": "Site Details",
115+
"microsoft.powerplatform.pages.actionsHub.activeSite.downloadSite.title": "Download Site",
116+
"microsoft.powerplatform.pages.actionsHub.activeSite.openInStudio.title": "Open in Power Pages Studio"
113117
}

‎serve.json

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"headers": [
3+
{
4+
"source": "**/*",
5+
"headers": [
6+
{
7+
"key": "Cross-Origin-Resource-Policy",
8+
"value": "cross-origin"
9+
}
10+
]
11+
}
12+
]
13+
}

‎src/client/extension.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,9 @@ export async function activate(
163163

164164
const workspaceFolders = getWorkspaceFolders();
165165

166+
// Init OrgChangeNotifier instance
167+
OrgChangeNotifier.createOrgChangeNotifierInstance(pacTerminal.getWrapper());
168+
166169
_context.subscriptions.push(
167170
orgChangeEvent(async (orgDetails: ActiveOrgOutput) => {
168171
const orgID = orgDetails.OrgId;
@@ -211,7 +214,7 @@ export async function activate(
211214
oneDSLoggerWrapper.getLogger().traceInfo(desktopTelemetryEventNames.DESKTOP_EXTENSION_INIT_CONTEXT, initContext);
212215
}
213216

214-
if (!copilotNotificationShown) {
217+
if (!copilotNotificationShown && workspaceContainsPortalConfigFolder(workspaceFolders)) {
215218
let telemetryData = '';
216219
let listOfActivePortals = [];
217220
try {
@@ -230,7 +233,7 @@ export async function activate(
230233
}
231234

232235
await Promise.allSettled([
233-
PreviewSite.initialize(context, workspaceFolders),
236+
PreviewSite.initialize(context, workspaceFolders, pacTerminal),
234237
ActionsHub.initialize(context, pacTerminal)
235238
]);
236239
}),
@@ -243,9 +246,6 @@ export async function activate(
243246

244247
if (workspaceContainsPortalConfigFolder(workspaceFolders)) {
245248

246-
// Init OrgChangeNotifier instance
247-
OrgChangeNotifier.createOrgChangeNotifierInstance(pacTerminal.getWrapper());
248-
249249
vscode.workspace.onDidOpenTextDocument(didOpenTextDocument);
250250
vscode.workspace.textDocuments.forEach(didOpenTextDocument);
251251

‎src/client/lib/PacTerminal.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export class PacTerminal implements vscode.Disposable {
7676
vscode.env.openExternal(vscode.Uri.parse('https://aka.ms/powerplatform-vscode-lab'));
7777
}
7878

79-
private static getTerminal(): vscode.Terminal {
79+
public static getTerminal(): vscode.Terminal {
8080
const terminal = vscode.window.activeTerminal ?
8181
vscode.window.activeTerminal as vscode.Terminal :
8282
vscode.window.createTerminal();

‎src/client/pac/PacWrapper.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ export class PacWrapper {
164164
return this.executeCommandAndParseResults<PacOrgWhoOutput>(new PacArguments("org", "who"));
165165
}
166166

167-
public async activeAuth(): Promise <PacAuthWhoOutput> {
167+
public async activeAuth(): Promise<PacAuthWhoOutput> {
168168
return this.executeCommandAndParseResults<PacAuthWhoOutput>(new PacArguments("auth", "who"));
169169
}
170170

@@ -180,6 +180,10 @@ export class PacWrapper {
180180
return this.executeCommandAndParseResults<PacOutput>(new PacArguments("telemetry", "disable"));
181181
}
182182

183+
public async pendingChanges(websitePath: string, dataModelVersion: 1 | 2): Promise<PacOutput> {
184+
return this.executeCommandAndParseResults<PacOutput>(new PacArguments("pages", "pending-changes", "-p", websitePath, "-mv", dataModelVersion.toString()));
185+
}
186+
183187
public exit(): void {
184188
this.pacInterop.exit();
185189
}

‎src/client/power-pages/actions-hub/ActionsHubCommandHandlers.ts

+359-32
Large diffs are not rendered by default.

‎src/client/power-pages/actions-hub/ActionsHubTreeDataProvider.ts

+18-4
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import { oneDSLoggerWrapper } from "../../../common/OneDSLoggerTelemetry/oneDSLo
1111
import { EnvironmentGroupTreeItem } from "./tree-items/EnvironmentGroupTreeItem";
1212
import { IEnvironmentInfo } from "./models/IEnvironmentInfo";
1313
import { PacTerminal } from "../../lib/PacTerminal";
14-
import { fetchWebsites, openActiveSitesInStudio, openInactiveSitesInStudio, previewSite, createNewAuthProfile, refreshEnvironment, showEnvironmentDetails, switchEnvironment, revealInOS, openSiteManagement } from "./ActionsHubCommandHandlers";
14+
import { fetchWebsites, openActiveSitesInStudio, openInactiveSitesInStudio, previewSite, createNewAuthProfile, refreshEnvironment, showEnvironmentDetails, switchEnvironment, revealInOS, openSiteManagement, uploadSite, showSiteDetails, downloadSite, openInStudio } from "./ActionsHubCommandHandlers";
1515
import PacContext from "../../pac/PacContext";
1616
import CurrentSiteContext from "./CurrentSiteContext";
17-
import { IWebsiteDetails } from "../../../common/services/Interfaces";
17+
import { IOtherSiteInfo, IWebsiteDetails } from "../../../common/services/Interfaces";
1818
import { orgChangeErrorEvent } from "../../OrgChangeNotifier";
1919

2020
export class ActionsHubTreeDataProvider implements vscode.TreeDataProvider<ActionsHubTreeItem> {
@@ -25,6 +25,7 @@ export class ActionsHubTreeDataProvider implements vscode.TreeDataProvider<Actio
2525

2626
private _activeSites: IWebsiteDetails[] = [];
2727
private _inactiveSites: IWebsiteDetails[] = [];
28+
private _otherSites: IOtherSiteInfo[] = [];
2829
private _loadWebsites = true;
2930

3031
private constructor(context: vscode.ExtensionContext, private readonly _pacTerminal: PacTerminal) {
@@ -53,6 +54,7 @@ export class ActionsHubTreeDataProvider implements vscode.TreeDataProvider<Actio
5354
const websites = await fetchWebsites();
5455
this._activeSites = websites.activeSites;
5556
this._inactiveSites = websites.inactiveSites;
57+
this._otherSites = websites.otherSites;
5658
this._loadWebsites = false;
5759
}
5860
}
@@ -78,9 +80,13 @@ export class ActionsHubTreeDataProvider implements vscode.TreeDataProvider<Actio
7880
const currentEnvInfo: IEnvironmentInfo = {
7981
currentEnvironmentName: authInfo.OrganizationFriendlyName
8082
};
83+
84+
if(!this._otherSites.length){
85+
return [new EnvironmentGroupTreeItem(currentEnvInfo, this._context, this._activeSites, this._inactiveSites)];
86+
}
8187
return [
8288
new EnvironmentGroupTreeItem(currentEnvInfo, this._context, this._activeSites, this._inactiveSites),
83-
new OtherSitesGroupTreeItem()
89+
new OtherSitesGroupTreeItem(this._otherSites)
8490
];
8591
} else {
8692
// Login experience scenario
@@ -120,7 +126,15 @@ export class ActionsHubTreeDataProvider implements vscode.TreeDataProvider<Actio
120126
vscode.commands.registerCommand("microsoft.powerplatform.pages.actionsHub.currentActiveSite.revealInOS.mac", revealInOS),
121127
vscode.commands.registerCommand("microsoft.powerplatform.pages.actionsHub.currentActiveSite.revealInOS.linux", revealInOS),
122128

123-
vscode.commands.registerCommand("microsoft.powerplatform.pages.actionsHub.inactiveSite.openSiteManagement", openSiteManagement)
129+
vscode.commands.registerCommand("microsoft.powerplatform.pages.actionsHub.inactiveSite.openSiteManagement", openSiteManagement),
130+
131+
vscode.commands.registerCommand("microsoft.powerplatform.pages.actionsHub.activeSite.uploadSite", uploadSite),
132+
133+
vscode.commands.registerCommand("microsoft.powerplatform.pages.actionsHub.siteDetails", showSiteDetails),
134+
135+
vscode.commands.registerCommand("microsoft.powerplatform.pages.actionsHub.activeSite.downloadSite", downloadSite),
136+
137+
vscode.commands.registerCommand("microsoft.powerplatform.pages.actionsHub.activeSite.openInStudio", openInStudio)
124138
];
125139
}
126140
}

‎src/client/power-pages/actions-hub/Constants.ts

+20-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export const Constants = {
1515
INACTIVE_SITE: "inactiveSite",
1616
OTHER_SITE: "otherSite",
1717
OTHER_SITES_GROUP: "otherSitesGroup",
18-
NO_SITES: "noSites"
18+
NO_SITES: "noSites",
1919
},
2020
Icons: {
2121
SITE: new vscode.ThemeIcon('globe'),
@@ -33,7 +33,14 @@ export const Constants = {
3333
SESSION_DETAILS: vscode.l10n.t("Session Details"),
3434
CHANGING_ENVIRONMENT: vscode.l10n.t("Changing environment..."),
3535
CURRENT: vscode.l10n.t("Current"),
36-
SITE_MANAGEMENT_URL_NOT_FOUND: vscode.l10n.t("Site management URL not found for the selected site. Please try again after refreshing the environment.")
36+
SITE_MANAGEMENT_URL_NOT_FOUND: vscode.l10n.t("Site management URL not found for the selected site. Please try again after refreshing the environment."),
37+
SITE_UPLOAD_CONFIRMATION: vscode.l10n.t(`Be careful when you're updating public sites. The changes you make are visible to anyone immediately. Do you want to continue?`),
38+
YES: vscode.l10n.t("Yes"),
39+
CURRENT_SITE_PATH_NOT_FOUND: vscode.l10n.t("Current site path not found."),
40+
SITE_DETAILS: vscode.l10n.t("Site Details"),
41+
BROWSE: vscode.l10n.t("Browse..."),
42+
SELECT_DOWNLOAD_FOLDER: vscode.l10n.t("Select the folder that will contain your project root for your site"),
43+
SELECT_FOLDER: vscode.l10n.t("Select Folder")
3744
},
3845
EventNames: {
3946
ACTIONS_HUB_INITIALIZED: "actionsHubInitialized",
@@ -44,7 +51,13 @@ export const Constants = {
4451
ORGANIZATION_URL_MISSING: "Organization URL is missing in the results.",
4552
EMPTY_RESULTS_ARRAY: "Results array is empty or not an array.",
4653
PAC_AUTH_OUTPUT_FAILURE: "pacAuthCreateOutput is missing or unsuccessful.",
47-
SITE_MANAGEMENT_URL_NOT_FOUND: "siteManagementUrlNotFound"
54+
SITE_MANAGEMENT_URL_NOT_FOUND: "siteManagementUrlNotFound",
55+
ACTIONS_HUB_UPLOAD_SITE: "actionsHubUploadSite",
56+
ACTIONS_HUB_UPLOAD_SITE_CANCELLED: "actionsHubUploadSiteCancelled",
57+
OTHER_SITES_YAML_PARSE_FAILED: "OtherSitesYamlParseFailed",
58+
OTHER_SITES_FILESYSTEM_ERROR: "OtherSitesFilesystemError",
59+
ACTIONS_HUB_UPLOAD_OTHER_SITE: "ActionsHubUploadOtherSite",
60+
ACTIONS_HUB_UPLOAD_SITE_FAILED: "ActionsHubUploadSiteFailed",
4861
},
4962
StudioEndpoints: {
5063
TEST: "https://make.test.powerpages.microsoft.com",
@@ -55,6 +68,10 @@ export const Constants = {
5568
HIGH: "https://make.high.powerpages.microsoft.us",
5669
MOONCAKE: "https://make.powerpages.microsoft.cn"
5770
},
71+
SiteVisibility : {
72+
PUBLIC: "public",
73+
PRIVATE: "private"
74+
},
5875
AppNames: {
5976
POWER_PAGES_MANAGEMENT: 'mspp_powerpagemanagement',
6077
PORTAL_MANAGEMENT: 'dynamics365portals'

‎src/client/power-pages/actions-hub/models/IWebsiteInfo.ts

+1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ export interface IWebsiteInfo {
1414
isCurrent: boolean;
1515
siteVisibility: string;
1616
siteManagementUrl: string;
17+
folderPath?: string;
1718
}

‎src/client/power-pages/actions-hub/tree-items/ActionsHubTreeItem.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ import * as vscode from "vscode";
77

88
export abstract class ActionsHubTreeItem extends vscode.TreeItem {
99
constructor(
10-
public readonly label: string,
10+
label: string | null | undefined,
1111
public readonly collapsibleState: vscode.TreeItemCollapsibleState,
1212
public readonly iconPath: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri } | vscode.ThemeIcon,
1313
public readonly contextValue: string,
1414
public readonly description: string = "",
1515
) {
16-
super(label, collapsibleState);
16+
super(label || "", collapsibleState);
1717

18-
this.tooltip = this.label;
18+
this.tooltip = label || "";
1919
}
2020

2121
public getChildren(): ActionsHubTreeItem[] {

‎src/client/power-pages/actions-hub/tree-items/ActiveGroupTreeItem.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,20 @@ export class ActiveGroupTreeItem extends ActionsHubTreeItem {
3939
dataModelVersion: site.dataModel == WebsiteDataModel.Standard ? 1 : 2,
4040
websiteUrl: site.websiteUrl,
4141
status: WebsiteStatus.Active,
42-
isCurrent: CurrentSiteContext.currentSiteId === site.websiteRecordId,
42+
isCurrent: this.isCurrentSite(site.websiteRecordId),
4343
siteVisibility: site.siteVisibility ?? "",
4444
siteManagementUrl: site.siteManagementUrl
4545
};
4646

4747
return new SiteTreeItem(siteInfo);
4848
});
4949
}
50+
51+
private isCurrentSite(siteId: string): boolean {
52+
if (!CurrentSiteContext.currentSiteId || !siteId) {
53+
return false;
54+
}
55+
56+
return CurrentSiteContext.currentSiteId === siteId;
57+
}
5058
}

‎src/client/power-pages/actions-hub/tree-items/OtherSitesGroupTreeItem.ts

+27-2
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,39 @@
66
import * as vscode from "vscode";
77
import { Constants } from "../Constants"
88
import { ActionsHubTreeItem } from "./ActionsHubTreeItem"
9+
import { IOtherSiteInfo } from "../../../../common/services/Interfaces";
10+
import { IWebsiteInfo } from "../models/IWebsiteInfo";
11+
import { SiteTreeItem } from "./SiteTreeItem";
912

1013
export class OtherSitesGroupTreeItem extends ActionsHubTreeItem {
11-
constructor() {
14+
private _otherSites: IOtherSiteInfo[] = [];
15+
16+
17+
constructor(otherSites: IOtherSiteInfo[]) {
1218
super(
1319
Constants.Strings.OTHER_SITES,
1420
vscode.TreeItemCollapsibleState.Collapsed,
1521
Constants.Icons.OTHER_SITES,
1622
Constants.ContextValues.OTHER_SITES_GROUP
17-
)
23+
);
24+
this._otherSites = otherSites;
25+
}
26+
27+
public getChildren(): ActionsHubTreeItem[] {
28+
return this._otherSites.map(site => {
29+
const siteInfo: IWebsiteInfo = {
30+
name: site.name,
31+
websiteId: site.websiteId || '',
32+
dataModelVersion: 2, // For other sites, we don't have this information. Defaulting to 2 and will rely on current sites to determine the data model for operations.
33+
status: undefined,
34+
isCurrent: false,
35+
websiteUrl: "",
36+
siteVisibility: "",
37+
siteManagementUrl: "",
38+
folderPath : site.folderPath
39+
};
40+
return new SiteTreeItem(siteInfo);
41+
});
42+
1843
}
1944
}

‎src/client/power-pages/preview-site/Constants.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export const Messages = {
1111
INSTALL: vscode.l10n.t("Install"),
1212
EDGE_DEV_TOOLS_NOT_INSTALLED_MESSAGE: vscode.l10n.t({ message: "The extension 'Microsoft Edge Tools' is required to run this command. Do you want to install it now?", comment: ["Do not translate 'Microsoft Edge Tools' "] }),
1313
OPENING_SITE_PREVIEW: vscode.l10n.t("Opening site preview..."),
14-
PREVIEW_SHOWN_FOR_PUBLISHED_CHANGES: vscode.l10n.t("The preview shown is for published changes."),
1514
SITE_PREVIEW_FEATURE_NOT_ENABLED: vscode.l10n.t("Site runtime preview feature is not enabled."),
1615
NO_FOLDER_OPENED: vscode.l10n.t("No workspace folder opened. Please open a site folder to preview."),
1716
INITIALIZING_PREVIEW_TRY_AGAIN: vscode.l10n.t("Initializing site preview. Please try again after few seconds."),
@@ -31,9 +30,12 @@ export const Messages = {
3130
}),
3231
AUTHENTICATING: vscode.l10n.t("Authenticating..."),
3332
UNABLE_TO_CLEAR_CACHE: vscode.l10n.t("Unable to clear cache"),
33+
PREVIEW_WARNING: vscode.l10n.t("Your preview isn't updated. Please upload your site to see the latest changes."),
34+
UPLOAD_CHANGES: vscode.l10n.t("Upload changes")
3435
};
3536

3637
export const Events = {
3738
PREVIEW_SITE_INITIALIZED: "PreviewSiteInitialized",
38-
PREVIEW_SITE_INITIALIZATION_FAILED: "PreviewSiteInitializationFailed"
39+
PREVIEW_SITE_INITIALIZATION_FAILED: "PreviewSiteInitializationFailed",
40+
PREVIEW_SITE_UPLOAD_WARNING_FAILED: "PreviewSiteUploadWarningFailed"
3941
}

‎src/client/power-pages/preview-site/PreviewSite.ts

+60-23
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,28 @@ import { ECSFeaturesClient } from '../../../common/ecs-features/ecsFeatureClient
88
import { EnableSiteRuntimePreview } from '../../../common/ecs-features/ecsFeatureGates';
99
import { WorkspaceFolder } from 'vscode-languageclient/node';
1010
import { getWebsiteRecordId } from '../../../common/utilities/WorkspaceInfoFinderUtil';
11-
import { PROVIDER_ID } from '../../../common/services/Constants';
11+
import { PROVIDER_ID, WebsiteDataModel } from '../../../common/services/Constants';
1212
import { PPAPIService } from '../../../common/services/PPAPIService';
1313
import { VSCODE_EXTENSION_GET_WEBSITE_RECORD_ID_EMPTY, VSCODE_EXTENSION_SITE_PREVIEW_ERROR } from '../../../common/services/TelemetryConstants';
14-
import { EDGE_TOOLS_EXTENSION_ID } from '../../../common/constants';
14+
import { EDGE_TOOLS_EXTENSION_ID, SUCCESS, TRUE } from '../../../common/constants';
1515
import { oneDSLoggerWrapper } from "../../../common/OneDSLoggerTelemetry/oneDSLoggerWrapper";
1616
import { getWorkspaceFolders, showProgressWithNotification } from '../../../common/utilities/Utils';
1717
import { Events, Messages } from './Constants';
1818
import { dataverseAuthentication } from '../../../common/services/AuthenticationProvider';
1919
import PacContext from '../../pac/PacContext';
2020
import ArtemisContext from '../../ArtemisContext';
21+
import { PacTerminal } from '../../lib/PacTerminal';
22+
import CurrentSiteContext from '../actions-hub/CurrentSiteContext';
23+
import { IWebsiteDetails } from '../../../common/services/Interfaces';
24+
import { uploadSite } from '../actions-hub/ActionsHubCommandHandlers';
25+
import { SiteTreeItem } from '../actions-hub/tree-items/SiteTreeItem';
2126

2227
export const SITE_PREVIEW_COMMAND_ID = "microsoft.powerplatform.pages.preview-site";
2328

2429
export class PreviewSite {
25-
private static _websiteUrl: string | undefined = undefined;
30+
private static _websiteDetails: IWebsiteDetails | undefined = undefined;
2631
private static _isInitialized = false;
32+
private static _pacTerminal: PacTerminal;
2733

2834

2935
static isSiteRuntimePreviewEnabled(): boolean {
@@ -36,11 +42,13 @@ export class PreviewSite {
3642
return enableSiteRuntimePreview;
3743
}
3844

39-
static async initialize(context: vscode.ExtensionContext, workspaceFolders: WorkspaceFolder[]): Promise<void> {
45+
static async initialize(context: vscode.ExtensionContext, workspaceFolders: WorkspaceFolder[], pacTerminal: PacTerminal): Promise<void> {
4046
if (PreviewSite._isInitialized) {
4147
return;
4248
}
4349

50+
PreviewSite._pacTerminal = pacTerminal;
51+
4452
try {
4553
const isSiteRuntimePreviewEnabled = PreviewSite.isSiteRuntimePreviewEnabled();
4654

@@ -52,16 +60,16 @@ export class PreviewSite {
5260
const orgDetails = PacContext.OrgInfo;
5361

5462
if (artemisResponse && orgDetails && isSiteRuntimePreviewEnabled) {
55-
PacContext.onChanged(async () => await PreviewSite.loadSiteUrl(workspaceFolders));
63+
PacContext.onChanged(async () => await PreviewSite.loadSiteDetails(workspaceFolders));
5664

5765
context.subscriptions.push(
5866
vscode.commands.registerCommand(
5967
SITE_PREVIEW_COMMAND_ID,
60-
async () => await PreviewSite.handlePreviewRequest()
68+
PreviewSite.handlePreviewRequest
6169
)
6270
);
6371

64-
await PreviewSite.loadSiteUrl(workspaceFolders);
72+
await PreviewSite.loadSiteDetails(workspaceFolders);
6573
await vscode.commands.executeCommand("setContext", "microsoft.powerplatform.pages.siteRuntimePreviewEnabled", true);
6674
}
6775

@@ -72,29 +80,29 @@ export class PreviewSite {
7280
}
7381
}
7482

75-
static async loadSiteUrl(workspaceFolders: WorkspaceFolder[]): Promise<void> {
76-
const websiteUrl = await PreviewSite.getWebSiteUrl(workspaceFolders);
83+
static async loadSiteDetails(workspaceFolders: WorkspaceFolder[]): Promise<void> {
84+
const websiteDetails = await PreviewSite.getWebsiteDetails(workspaceFolders);
7785

78-
this._websiteUrl = websiteUrl;
86+
PreviewSite._websiteDetails = websiteDetails;
7987
}
8088

81-
private static async getWebSiteUrl(workspaceFolders: WorkspaceFolder[]): Promise<string> {
89+
private static async getWebsiteDetails(workspaceFolders: WorkspaceFolder[]): Promise<IWebsiteDetails> {
8290
const websiteRecordId = getWebsiteRecordId(workspaceFolders);
8391
if (!websiteRecordId) {
8492
oneDSLoggerWrapper.getLogger().traceInfo(VSCODE_EXTENSION_GET_WEBSITE_RECORD_ID_EMPTY, {
8593
websiteRecordId: websiteRecordId
8694
});
87-
return "";
95+
return {} as IWebsiteDetails;
8896
}
8997

9098
const orgDetails = PacContext.OrgInfo;
9199

92100
if (!orgDetails) {
93-
return "";
101+
return {} as IWebsiteDetails;
94102
}
95103

96104
const websiteDetails = await PPAPIService.getWebsiteDetailsByWebsiteRecordId(ArtemisContext.ServiceResponse.stamp, orgDetails.EnvironmentId, websiteRecordId);
97-
return websiteDetails?.websiteUrl || "";
105+
return websiteDetails ?? {} as IWebsiteDetails;
98106
}
99107

100108
private static async promptInstallEdgeTools(): Promise<void> {
@@ -108,15 +116,15 @@ export class PreviewSite {
108116
}
109117
}
110118

111-
public static async launchBrowserAndDevToolsWithinVsCode(webSitePreviewURL: string | undefined): Promise<void> {
119+
public static async launchBrowserAndDevToolsWithinVsCode(webSitePreviewURL: string | undefined, dataModelVersion: 1 | 2, siteVisibility: string): Promise<void> {
112120
if (!webSitePreviewURL || webSitePreviewURL === "") {
113121
return;
114122
}
115123

116124
const edgeToolsExtension = vscode.extensions.getExtension(EDGE_TOOLS_EXTENSION_ID);
117125

118126
if (!edgeToolsExtension) {
119-
await this.promptInstallEdgeTools();
127+
await PreviewSite.promptInstallEdgeTools();
120128
return;
121129
}
122130

@@ -125,7 +133,31 @@ export class PreviewSite {
125133
async () => await vscode.commands.executeCommand('vscode-edge-devtools.launch', { launchUrl: webSitePreviewURL })
126134
);
127135

128-
await vscode.window.showInformationMessage(Messages.PREVIEW_SHOWN_FOR_PUBLISHED_CHANGES);
136+
const websitePath = CurrentSiteContext.currentSiteFolderPath;
137+
if (websitePath) {
138+
await PreviewSite.showUploadWarning(websitePath, dataModelVersion, siteVisibility);
139+
}
140+
}
141+
142+
private static async showUploadWarning(websitePath: string, dataModelVersion: 1 | 2, siteVisibility: string) {
143+
const pendingChangesResult = await PreviewSite._pacTerminal.getWrapper().pendingChanges(websitePath, dataModelVersion);
144+
145+
try {
146+
if (pendingChangesResult.Status === SUCCESS && pendingChangesResult.Information[pendingChangesResult.Information.length - 1].includes(TRUE)) {
147+
const result = await vscode.window.showInformationMessage(Messages.PREVIEW_WARNING, Messages.UPLOAD_CHANGES, Messages.CANCEL);
148+
if (result === Messages.UPLOAD_CHANGES) {
149+
await uploadSite({
150+
siteInfo: {
151+
dataModelVersion: dataModelVersion,
152+
siteVisibility: siteVisibility
153+
}
154+
} as SiteTreeItem, websitePath);
155+
}
156+
}
157+
} catch (exception) {
158+
const exceptionError = exception as Error;
159+
oneDSLoggerWrapper.getLogger().traceError(Events.PREVIEW_SITE_UPLOAD_WARNING_FAILED, exceptionError.message, exceptionError);
160+
}
129161
}
130162

131163
static async handlePreviewRequest() {
@@ -146,7 +178,7 @@ export class PreviewSite {
146178
return;
147179
}
148180

149-
if (this._websiteUrl === undefined) {
181+
if (PreviewSite._websiteDetails === undefined) {
150182
oneDSLoggerWrapper.getLogger().traceInfo(VSCODE_EXTENSION_SITE_PREVIEW_ERROR, { error: Messages.INITIALIZING_PREVIEW_TRY_AGAIN });
151183
await vscode.window.showWarningMessage(Messages.INITIALIZING_PREVIEW_TRY_AGAIN);
152184
return;
@@ -160,17 +192,22 @@ export class PreviewSite {
160192
return;
161193
}
162194

163-
if (this._websiteUrl === "") {
195+
if (!PreviewSite._websiteDetails.websiteUrl) {
164196
let shouldRepeatLoginFlow = true;
165197

166198
while (shouldRepeatLoginFlow) {
167199
shouldRepeatLoginFlow = await PreviewSite.handleEmptyWebsiteUrl();
168200
}
169201
}
170202

171-
await PreviewSite.clearCache(this._websiteUrl);
172203

173-
await PreviewSite.launchBrowserAndDevToolsWithinVsCode(this._websiteUrl);
204+
await PreviewSite.clearCache(PreviewSite._websiteDetails.websiteUrl);
205+
206+
await PreviewSite.launchBrowserAndDevToolsWithinVsCode(
207+
PreviewSite._websiteDetails.websiteUrl,
208+
PreviewSite._websiteDetails.dataModel === WebsiteDataModel.Standard ? 1 : 2,
209+
PreviewSite._websiteDetails.siteVisibility ?? ""
210+
);
174211
}
175212

176213
public static async clearCache(websiteUrl: string | undefined): Promise<void> {
@@ -237,9 +274,9 @@ export class PreviewSite {
237274

238275
progress.report({ message: Messages.GETTING_WEBSITE_ENDPOINT });
239276

240-
await PreviewSite.loadSiteUrl(getWorkspaceFolders());
277+
await PreviewSite.loadSiteDetails(getWorkspaceFolders());
241278

242-
if (this._websiteUrl === "") {
279+
if (!PreviewSite._websiteDetails?.websiteUrl) {
243280
shouldRepeatLoginFlow = true;
244281
}
245282
}

‎src/client/test/Integration/power-pages/actions-hub/ActionsHubCommandHandlers.test.ts

+636-12
Large diffs are not rendered by default.

‎src/client/test/Integration/power-pages/actions-hub/ActionsHubTreeDataProvider.test.ts

+63-5
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { SiteTreeItem } from "../../../../power-pages/actions-hub/tree-items/Sit
1919
import { IWebsiteInfo } from "../../../../power-pages/actions-hub/models/IWebsiteInfo";
2020
import PacContext from "../../../../pac/PacContext";
2121
import { CloudInstance, EnvironmentType } from "../../../../pac/PacTypes";
22-
import { IWebsiteDetails } from "../../../../../common/services/Interfaces";
22+
import { IOtherSiteInfo, IWebsiteDetails } from "../../../../../common/services/Interfaces";
2323
import * as CommandHandlers from "../../../../power-pages/actions-hub/ActionsHubCommandHandlers";
2424

2525
// Add global type declaration for ArtemisContext
@@ -166,6 +166,55 @@ describe("ActionsHubTreeDataProvider", () => {
166166
await registerCommandStub.getCall(10).args[1]();
167167
expect(mockCommandHandler.calledOnce).to.be.true;
168168
});
169+
170+
//command "microsoft.powerplatform.pages.actionsHub.activeSite.uploadSite"
171+
it("should register uploadSite command", async () => {
172+
const mockCommandHandler = sinon.stub(CommandHandlers, 'uploadSite');
173+
mockCommandHandler.resolves();
174+
const actionsHubTreeDataProvider = ActionsHubTreeDataProvider.initialize(context, pacTerminal);
175+
actionsHubTreeDataProvider["registerPanel"](pacTerminal);
176+
177+
expect(registerCommandStub.calledWith("microsoft.powerplatform.pages.actionsHub.activeSite.uploadSite")).to.be.true;
178+
179+
await registerCommandStub.getCall(11).args[1]();
180+
expect(mockCommandHandler.calledOnce).to.be.true;
181+
});
182+
183+
it("should register siteDetails command", async () => {
184+
const mockCommandHandler = sinon.stub(CommandHandlers, 'showSiteDetails');
185+
mockCommandHandler.resolves();
186+
const actionsHubTreeDataProvider = ActionsHubTreeDataProvider.initialize(context, pacTerminal);
187+
actionsHubTreeDataProvider["registerPanel"](pacTerminal);
188+
189+
expect(registerCommandStub.calledWith("microsoft.powerplatform.pages.actionsHub.siteDetails")).to.be.true;
190+
191+
await registerCommandStub.getCall(12).args[1]();
192+
expect(mockCommandHandler.calledOnce).to.be.true;
193+
});
194+
195+
it("should register downloadSite command", async () => {
196+
const mockCommandHandler = sinon.stub(CommandHandlers, 'downloadSite');
197+
mockCommandHandler.resolves();
198+
const actionsHubTreeDataProvider = ActionsHubTreeDataProvider.initialize(context, pacTerminal);
199+
actionsHubTreeDataProvider["registerPanel"](pacTerminal);
200+
201+
expect(registerCommandStub.calledWith("microsoft.powerplatform.pages.actionsHub.activeSite.downloadSite")).to.be.true;
202+
203+
await registerCommandStub.getCall(13).args[1]();
204+
expect(mockCommandHandler.calledOnce).to.be.true;
205+
});
206+
207+
it('should register openSiteInStudio command', async () => {
208+
const mockCommandHandler = sinon.stub(CommandHandlers, 'openInStudio');
209+
mockCommandHandler.resolves();
210+
const actionsHubTreeDataProvider = ActionsHubTreeDataProvider.initialize(context, pacTerminal);
211+
actionsHubTreeDataProvider["registerPanel"](pacTerminal);
212+
213+
expect(registerCommandStub.calledWith("microsoft.powerplatform.pages.actionsHub.activeSite.openInStudio")).to.be.true;
214+
215+
await registerCommandStub.getCall(14).args[1]();
216+
expect(mockCommandHandler.calledOnce).to.be.true;
217+
});
169218
});
170219

171220
describe('getTreeItem', () => {
@@ -184,7 +233,10 @@ describe("ActionsHubTreeDataProvider", () => {
184233
const mockInactiveSites = [
185234
{ name: "Bar", websiteRecordId: 'Bar', websiteUrl: "https://bar.com" }
186235
] as IWebsiteDetails[];
187-
sinon.stub(CommandHandlers, 'fetchWebsites').resolves({ activeSites: mockActiveSites, inactiveSites: mockInactiveSites });
236+
const otherSites = [
237+
{ name: "Baz", websiteId: 'baz' }
238+
] as IOtherSiteInfo[];
239+
sinon.stub(CommandHandlers, 'fetchWebsites').resolves({ activeSites: mockActiveSites, inactiveSites: mockInactiveSites, otherSites: otherSites });
188240

189241
PacContext['_authInfo'] = null;
190242
const provider = ActionsHubTreeDataProvider.initialize(context, pacTerminal);
@@ -202,7 +254,10 @@ describe("ActionsHubTreeDataProvider", () => {
202254
const mockInactiveSites = [
203255
{ name: "Bar", websiteRecordId: 'Bar', websiteUrl: "https://bar.com" }
204256
] as IWebsiteDetails[];
205-
sinon.stub(CommandHandlers, 'fetchWebsites').resolves({ activeSites: mockActiveSites, inactiveSites: mockInactiveSites });
257+
const otherSites = [
258+
{ name: "Baz", websiteId: 'baz' }
259+
] as IOtherSiteInfo[];
260+
sinon.stub(CommandHandlers, 'fetchWebsites').resolves({ activeSites: mockActiveSites, inactiveSites: mockInactiveSites, otherSites: otherSites });
206261

207262
sinon.stub(PacContext, "AuthInfo").get(() => ({
208263
OrganizationFriendlyName: "TestOrg",
@@ -256,7 +311,7 @@ describe("ActionsHubTreeDataProvider", () => {
256311
it("should call element.getChildren when an element is passed", async () => {
257312
const element = new SiteTreeItem({} as IWebsiteInfo);
258313
const provider = ActionsHubTreeDataProvider.initialize(context, pacTerminal);
259-
sinon.stub(CommandHandlers, 'fetchWebsites').resolves({ activeSites: [], inactiveSites: [] });
314+
sinon.stub(CommandHandlers, 'fetchWebsites').resolves({ activeSites: [], inactiveSites: [], otherSites: [] });
260315
provider["_loadWebsites"] = false;
261316
const getChildrenStub = sinon.stub(element, "getChildren").resolves([]);
262317

@@ -273,7 +328,10 @@ describe("ActionsHubTreeDataProvider", () => {
273328
const mockInactiveSites = [
274329
{ name: "Bar", websiteRecordId: 'Bar', websiteUrl: "https://bar.com" }
275330
] as IWebsiteDetails[];
276-
const mockFetchWebsites = sinon.stub(CommandHandlers, 'fetchWebsites').resolves({ activeSites: mockActiveSites, inactiveSites: mockInactiveSites });
331+
const otherSites = [
332+
{ name: "Baz", websiteId: 'baz' }
333+
] as IOtherSiteInfo[];
334+
const mockFetchWebsites = sinon.stub(CommandHandlers, 'fetchWebsites').resolves({ activeSites: mockActiveSites, inactiveSites: mockInactiveSites, otherSites: otherSites });
277335

278336
PacContext['_authInfo'] = {
279337
OrganizationFriendlyName: "TestOrg",

‎src/client/test/Integration/power-pages/actions-hub/tree-items/ActionsHubTreeItem.test.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import { expect } from "chai";
88
import { ActionsHubTreeItem } from "../../../../../power-pages/actions-hub/tree-items/ActionsHubTreeItem";
99

1010
class MockTreeItem extends ActionsHubTreeItem {
11-
constructor() {
11+
constructor(label: string | null = "Foo") {
1212
super(
13-
"Foo",
13+
label,
1414
vscode.TreeItemCollapsibleState.Collapsed,
1515
"iconPath",
1616
"contextValue",
@@ -32,6 +32,12 @@ describe('ActionsHubTreeItem', () => {
3232
expect(treeItem.label).to.be.equal("Foo");
3333
});
3434

35+
it('should have expected label when null is passed', () => {
36+
const treeItem = new MockTreeItem(null);
37+
38+
expect(treeItem.tooltip).to.be.equal("");
39+
});
40+
3541
it('should have the expected collapsible state', () => {
3642
const treeItem = new MockTreeItem();
3743

‎src/client/test/Integration/power-pages/actions-hub/tree-items/ActiveGroupTreeItem.test.ts

+67-5
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,8 @@ describe('ActiveGroupTreeItem', () => {
5959
});
6060

6161
describe('when active sites is not empty', () => {
62-
beforeEach(() => {
63-
sinon.stub(CurrentSiteContext, 'currentSiteId').value('1');
64-
});
65-
6662
it('should return an array of SiteTreeItem', () => {
63+
sinon.stub(CurrentSiteContext, 'currentSiteId').value('1');
6764
const activeWebsites: IWebsiteDetails[] = [
6865
{
6966
websiteRecordId: "1",
@@ -85,7 +82,7 @@ describe('ActiveGroupTreeItem', () => {
8582
dataModel: WebsiteDataModel.Enhanced,
8683
environmentId: "env2",
8784
siteVisibility: "private",
88-
siteManagementUrl: "http://site1.com/manage"
85+
siteManagementUrl: "http://site2.com/manage"
8986
}
9087
];
9188

@@ -113,6 +110,71 @@ describe('ActiveGroupTreeItem', () => {
113110
status: WebsiteStatus.Active,
114111
isCurrent: false,
115112
siteVisibility: "private",
113+
siteManagementUrl: "http://site2.com/manage"
114+
});
115+
});
116+
117+
it('should return an array of SiteTreeItem when site id is empty', () => {
118+
sinon.stub(CurrentSiteContext, 'currentSiteId').value('');
119+
const activeWebsites: IWebsiteDetails[] = [
120+
{
121+
websiteRecordId: "",
122+
name: "Site 1",
123+
websiteUrl: "http://site1.com",
124+
dataverseInstanceUrl: "http://dataverse1.com",
125+
dataverseOrganizationId: "org1",
126+
dataModel: WebsiteDataModel.Standard,
127+
environmentId: "env1",
128+
siteVisibility: "public",
129+
siteManagementUrl: "http://site1.com/manage"
130+
}
131+
];
132+
133+
const treeItem = new ActiveGroupTreeItem(activeWebsites);
134+
const children = treeItem.getChildren();
135+
136+
const site1 = children[0] as SiteTreeItem;
137+
expect(site1.siteInfo).to.deep.equal({
138+
name: 'Site 1',
139+
websiteId: '',
140+
dataModelVersion: 1,
141+
websiteUrl: 'http://site1.com',
142+
status: WebsiteStatus.Active,
143+
isCurrent: false,
144+
siteVisibility: "public",
145+
siteManagementUrl: "http://site1.com/manage"
146+
});
147+
});
148+
149+
it('should return an array of SiteTreeItem when site id is empty', () => {
150+
sinon.stub(CurrentSiteContext, 'currentSiteId').value(null);
151+
const activeWebsites: IWebsiteDetails[] = [
152+
{
153+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
154+
websiteRecordId: null!,
155+
name: "Site 1",
156+
websiteUrl: "http://site1.com",
157+
dataverseInstanceUrl: "http://dataverse1.com",
158+
dataverseOrganizationId: "org1",
159+
dataModel: WebsiteDataModel.Standard,
160+
environmentId: "env1",
161+
siteVisibility: "public",
162+
siteManagementUrl: "http://site1.com/manage"
163+
}
164+
];
165+
166+
const treeItem = new ActiveGroupTreeItem(activeWebsites);
167+
const children = treeItem.getChildren();
168+
169+
const site1 = children[0] as SiteTreeItem;
170+
expect(site1.siteInfo).to.deep.equal({
171+
name: 'Site 1',
172+
websiteId: null,
173+
dataModelVersion: 1,
174+
websiteUrl: 'http://site1.com',
175+
status: WebsiteStatus.Active,
176+
isCurrent: false,
177+
siteVisibility: "public",
116178
siteManagementUrl: "http://site1.com/manage"
117179
});
118180
});

‎src/client/test/Integration/power-pages/actions-hub/tree-items/OtherSitesGroupTreeItem.test.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,31 @@ import { OtherSitesGroupTreeItem } from "../../../../../power-pages/actions-hub/
1010

1111
describe('OtherSitesGroupTreeItem', () => {
1212
it('should be of type ActionsHubTreeItem', () => {
13-
const treeItem = new OtherSitesGroupTreeItem();
13+
const treeItem = new OtherSitesGroupTreeItem([]);
1414

1515
expect(treeItem).to.be.instanceOf(ActionsHubTreeItem);
1616
});
1717

1818
it('should have the expected label', () => {
19-
const treeItem = new OtherSitesGroupTreeItem();
19+
const treeItem = new OtherSitesGroupTreeItem([]);
2020

2121
expect(treeItem.label).to.be.equal("Other Sites");
2222
});
2323

2424
it('should have the expected collapsibleState', () => {
25-
const treeItem = new OtherSitesGroupTreeItem();
25+
const treeItem = new OtherSitesGroupTreeItem([]);
2626

2727
expect(treeItem.collapsibleState).to.be.equal(vscode.TreeItemCollapsibleState.Collapsed);
2828
});
2929

3030
it('should have the expected icon', () => {
31-
const treeItem = new OtherSitesGroupTreeItem();
31+
const treeItem = new OtherSitesGroupTreeItem([]);
3232

3333
expect((treeItem.iconPath as vscode.ThemeIcon).id).to.be.equal('archive');
3434
});
3535

3636
it('should have the expected contextValue', () => {
37-
const treeItem = new OtherSitesGroupTreeItem();
37+
const treeItem = new OtherSitesGroupTreeItem([]);
3838

3939
expect(treeItem.contextValue).to.be.equal("otherSitesGroup");
4040
});

‎src/client/test/Integration/power-pages/preview-site/PreviewSite.test.ts

+10-10
Original file line numberDiff line numberDiff line change
@@ -72,35 +72,35 @@ describe('PreviewSite', () => {
7272
const mockWorkspaceFolders = [{ uri: 'test/path', name: 'test', index: 0 }];
7373

7474
beforeEach(() => {
75-
PreviewSite['_websiteUrl'] = undefined;
75+
PreviewSite['_websiteDetails'] = undefined;
7676
});
7777

7878
it('should set website URL when getWebSiteUrl returns valid URL', async () => {
7979
// Arrange
8080
const expectedUrl = 'https://test-website.com';
8181
const getWebSiteUrlStub = sandbox.stub();
82-
getWebSiteUrlStub.resolves(expectedUrl);
83-
PreviewSite['getWebSiteUrl'] = getWebSiteUrlStub;
82+
getWebSiteUrlStub.resolves({ url: expectedUrl, dataModel: 'Enhanced'});
83+
PreviewSite['getWebsiteDetails'] = getWebSiteUrlStub;
8484

8585
// Act
86-
await PreviewSite.loadSiteUrl(mockWorkspaceFolders);
86+
await PreviewSite.loadSiteDetails(mockWorkspaceFolders);
8787

8888
// Assert
89-
expect(PreviewSite['_websiteUrl']).to.equal(expectedUrl);
89+
expect(PreviewSite['_websiteDetails']).to.deep.equal({ url: expectedUrl, dataModel: 'Enhanced' })
9090
expect(getWebSiteUrlStub.calledOnceWith(mockWorkspaceFolders)).to.be.true;
9191
});
9292

93-
it('should set empty string when getWebSiteUrl returns empty string', async () => {
93+
it('should set details when getWebSiteUrl returns empty URL string', async () => {
9494
// Arrange
9595
const getWebSiteUrlStub = sandbox.stub();
96-
getWebSiteUrlStub.resolves('');
97-
PreviewSite['getWebSiteUrl'] = getWebSiteUrlStub;
96+
getWebSiteUrlStub.resolves({ url: '', dataModel: 'Enhanced'});
97+
PreviewSite['getWebsiteDetails'] = getWebSiteUrlStub;
9898

9999
// Act
100-
await PreviewSite.loadSiteUrl(mockWorkspaceFolders);
100+
await PreviewSite.loadSiteDetails(mockWorkspaceFolders);
101101

102102
// Assert
103-
expect(PreviewSite['_websiteUrl']).to.equal('');
103+
expect(PreviewSite['_websiteDetails']).to.deep.equal({ url: '', dataModel: 'Enhanced' });
104104
expect(getWebSiteUrlStub.calledOnceWith(mockWorkspaceFolders)).to.be.true;
105105
});
106106
});

‎src/client/test/Integration/validationDiagnostics.test.ts

+32-16
Original file line numberDiff line numberDiff line change
@@ -19,72 +19,84 @@ describe("validationDiagnostics", () => {
1919
sinon.restore();
2020
});
2121

22-
it("validateTextDocument_whenPattrenDoesNot_shouldNotSetValueInDiagnostics", async () => {
22+
it("validateTextDocument_whenPatternDoesNot_shouldNotSetValueInDiagnostics", async () => {
2323
//Act
2424
const filePath = path.join(
2525
__dirname,
2626
"..",
2727
"..",
2828
"..",
2929
"..",
30-
"/src/client/test/Integration/MockValidationDiagnosticsTextDoc.txt"
30+
"src",
31+
"client",
32+
"test",
33+
"Integration",
34+
"MockValidationDiagnosticsTextDoc.txt"
3135
);
3236

33-
const uri = vscode.Uri.parse(path.join("file:///", filePath));
37+
const uri = vscode.Uri.file(filePath);
3438
const patterns = [/z/g];
3539
const searchByName = true;
3640
//Action
3741
await validateTextDocument(uri, patterns, searchByName);
3842
//Assert
3943
const connection = await vscode.languages.getDiagnostics();
4044
expect(connection[0][0].scheme).eq("file");
41-
expect(connection[0][0].path).eq("/\\" + filePath);
45+
expect(connection[0][0].path.replace(/[/\\]/g, '')).eq(filePath.replace(/[/\\]/g, ''));
4246
expect(connection[0][1]).empty;
4347
});
4448

45-
it("validateTextDocument_whenPattrenIsEmptyArray_shouldNotSetValueInDiagnostics", async () => {
49+
it("validateTextDocument_whenPatternIsEmptyArray_shouldNotSetValueInDiagnostics", async () => {
4650
//Act
4751
const filePath = path.join(
4852
__dirname,
4953
"..",
5054
"..",
5155
"..",
5256
"..",
53-
"/src/client/test/Integration/MockValidationDiagnosticsTextDoc.txt"
57+
"src",
58+
"client",
59+
"test",
60+
"Integration",
61+
"MockValidationDiagnosticsTextDoc.txt"
5462
);
5563

56-
const uri = vscode.Uri.parse(path.join("file:///", filePath));
64+
const uri = vscode.Uri.file(filePath);
5765
const patterns: RegExp[] = [];
5866
const searchByName = true;
5967
//Action
6068
await validateTextDocument(uri, patterns, searchByName);
6169
//Assert
6270
const connection = await vscode.languages.getDiagnostics();
6371
expect(connection[0][0].scheme).eq("file");
64-
expect(connection[0][0].path).eq("/\\" + filePath);
72+
expect(connection[0][0].path.replace(/[/\\]/g, '')).eq(filePath.replace(/[/\\]/g, ''));
6573
expect(connection[0][1]).empty;
6674
});
6775

68-
it("validateTextDocument_whenPattrenMatchAndSearchByNameIsTrue_shouldSetValueInDiagnostics", async () => {
76+
it("validateTextDocument_whenPatternMatchAndSearchByNameIsTrue_shouldSetValueInDiagnostics", async () => {
6977
//Act
7078
const filePath = path.join(
7179
__dirname,
7280
"..",
7381
"..",
7482
"..",
7583
"..",
76-
"/src/client/test/Integration/MockValidationDiagnosticsTextDoc.txt"
84+
"src",
85+
"client",
86+
"test",
87+
"Integration",
88+
"MockValidationDiagnosticsTextDoc.txt"
7789
);
7890

79-
const uri = vscode.Uri.parse(path.join("file:///", filePath));
91+
const uri = vscode.Uri.file(filePath);
8092
const patterns = [/keyword/g, /contains/g];
8193
const searchByName = true;
8294
//Action
8395
await validateTextDocument(uri, patterns, searchByName);
8496
//Assert
8597
const connection = await vscode.languages.getDiagnostics();
8698
expect(connection[0][0].scheme).eq("file");
87-
expect(connection[0][0].path).eq("/\\" + filePath);
99+
expect(connection[0][0].path.replace(/[/\\]/g, '')).eq(filePath.replace(/[/\\]/g, ''));
88100

89101
expect(connection[0][1][0].message).eq(
90102
'PowerPages: File might be referenced by name keyword here.'
@@ -107,26 +119,30 @@ describe("validationDiagnostics", () => {
107119
expect(connection[0][1][1].range).not.undefined;
108120
});
109121

110-
it("validateTextDocument_whenPattrenMatchAndSearchByNameIsFalse_shouldSetValueInDiagnostics", async () => {
122+
it("validateTextDocument_whenPatternMatchAndSearchByNameIsFalse_shouldSetValueInDiagnostics", async () => {
111123
//Act
112124
const filePath = path.join(
113125
__dirname,
114126
"..",
115127
"..",
116128
"..",
117129
"..",
118-
"/src/client/test/Integration/MockValidationDiagnosticsTextDoc.txt"
130+
"src",
131+
"client",
132+
"test",
133+
"Integration",
134+
"MockValidationDiagnosticsTextDoc.txt"
119135
);
120136

121-
const uri = vscode.Uri.parse(path.join("file:///", filePath));
137+
const uri = vscode.Uri.file(filePath);
122138
const patterns = [/keyword/g, /contains/g];
123139
const searchByName = false;
124140
//Action
125141
await validateTextDocument(uri, patterns, searchByName);
126142
//Assert
127143
const connection = await vscode.languages.getDiagnostics();
128144
expect(connection[0][0].scheme).eq("file");
129-
expect(connection[0][0].path).eq("/\\" + filePath);
145+
expect(connection[0][0].path.replace(/[/\\]/g, '')).eq(filePath.replace(/[/\\]/g, ''));
130146

131147
expect(connection[0][1][0].message).eq("PowerPages: ");
132148
expect(connection[0][1][1].message).eq("PowerPages: ");

‎src/common/OneDSLoggerTelemetry/oneDSLogger.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,12 @@ export class OneDSLogger implements ITelemetryLogger {
116116
if ((coreConfig.instrumentationKey ?? "") !== "") {
117117
this.appInsightsCore.initialize(coreConfig, []);
118118
}
119-
this.intitializeContextInfo();
119+
this.initializeContextInfo();
120120
// eslint-disable-next-line @typescript-eslint/no-explicit-any
121121
this.appInsightsCore.addTelemetryInitializer(this.populateCommonAttributes());
122122
}
123123

124-
private intitializeContextInfo() {
124+
private initializeContextInfo() {
125125
OneDSLogger.contextInfo = {
126126
orgId: "",
127127
portalId: "",

‎src/common/OneDSLoggerTelemetry/oneDSLoggerWrapper.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ import { OneDSLogger } from "./oneDSLogger";
1212

1313
export class oneDSLoggerWrapper {
1414
private static instance: oneDSLoggerWrapper;
15-
private static oneDSLoggerIntance: OneDSLogger;
15+
private static oneDSLoggerInstance: OneDSLogger;
1616

1717
private constructor(geo?: string, geoLongName?: string) {
18-
oneDSLoggerWrapper.oneDSLoggerIntance = new OneDSLogger(geo, geoLongName);
18+
oneDSLoggerWrapper.oneDSLoggerInstance = new OneDSLogger(geo, geoLongName);
1919
}
2020

2121

@@ -31,7 +31,7 @@ export class oneDSLoggerWrapper {
3131
public traceInfo(eventName: string, eventInfo?: object, measurement?: object) {
3232
try {
3333
if (!isCustomTelemetryEnabled()) return;
34-
oneDSLoggerWrapper.oneDSLoggerIntance.traceInfo(eventName, eventInfo, measurement);
34+
oneDSLoggerWrapper.oneDSLoggerInstance.traceInfo(eventName, eventInfo, measurement);
3535
} catch (exception) {
3636
console.warn(exception);
3737
}
@@ -41,7 +41,7 @@ export class oneDSLoggerWrapper {
4141
public traceWarning(eventName: string, eventInfo?: object, measurement?: object) {
4242
try {
4343
if (!isCustomTelemetryEnabled()) return;
44-
oneDSLoggerWrapper.oneDSLoggerIntance.traceWarning(eventName, eventInfo, measurement);
44+
oneDSLoggerWrapper.oneDSLoggerInstance.traceWarning(eventName, eventInfo, measurement);
4545
} catch (exception) {
4646
console.warn(exception);
4747
}
@@ -51,7 +51,7 @@ export class oneDSLoggerWrapper {
5151
public traceError(eventName: string, errorMessage: string, exception: Error, eventInfo?: object, measurement?: object) {
5252
try {
5353
if (!isCustomTelemetryEnabled()) return;
54-
oneDSLoggerWrapper.oneDSLoggerIntance.traceError(eventName, errorMessage, exception, eventInfo, measurement);
54+
oneDSLoggerWrapper.oneDSLoggerInstance.traceError(eventName, errorMessage, exception, eventInfo, measurement);
5555
} catch (exception) {
5656
console.warn("Caught exception processing the telemetry event: " + exception);
5757
console.warn(exception);
@@ -62,7 +62,7 @@ export class oneDSLoggerWrapper {
6262
public featureUsage(featureName: string, eventName: string, customDimensions?: object) {
6363
try {
6464
if (!isCustomTelemetryEnabled()) return;
65-
oneDSLoggerWrapper.oneDSLoggerIntance.featureUsage(featureName, eventName, customDimensions);
65+
oneDSLoggerWrapper.oneDSLoggerInstance.featureUsage(featureName, eventName, customDimensions);
6666
} catch (exception) {
6767
console.warn(exception);
6868
}

‎src/common/constants.ts

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ export const PORTAL_YEOMAN_GENERATOR_PACKAGE_TARBALL_NAME = "microsoft-generator
4141

4242
export const SUCCESS = "Success";
4343

44+
export const TRUE = "True";
45+
4446
// Define the schema for file extensions
4547
export const componentTypeSchema: { [key: string]: { [key: string]: string } } = {
4648
'adx_webpage': {

‎src/common/copilot/PowerPagesCopilot.ts

+3
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,9 @@ export class PowerPagesCopilot implements vscode.WebviewViewProvider {
195195
WORKING_ON_IT_MESSAGE: vscode.l10n.t("Working on it..."),
196196
GITHUB_COPILOT_CHAT: vscode.l10n.t('You can use this in <a href="#" id="github-copilot-link">GitHub Copilot with @powerpages</a> and leverage best of both world.'),
197197
NEW_BADGE: vscode.l10n.t("NEW"),
198+
COPILOT_RESPONSE: vscode.l10n.t("Copilot Response"),
199+
CODE_BLOCK: vscode.l10n.t("Code Block "),
200+
CODE_BLOCK_END: vscode.l10n.t(" End of Code Block "),
198201
};
199202

200203
this.sendMessageToWebview({

‎src/common/copilot/assets/scripts/copilot.js

+26-10
Original file line numberDiff line numberDiff line change
@@ -299,13 +299,19 @@
299299
const messageWrapper = document.createElement("div");
300300
messageWrapper.classList.add("message-wrapper");
301301

302+
// Add a visually hidden live region for screen readers
303+
const liveRegion = document.createElement("div");
304+
liveRegion.setAttribute("aria-live", "polite");
305+
liveRegion.setAttribute("role", "status");
306+
liveRegion.setAttribute("aria-atomic", "true");
307+
liveRegion.classList.add("sr-only"); //corresponding CSS
308+
messageWrapper.appendChild(liveRegion);
309+
302310
const messageElement = document.createElement("div");
303311
const makerElement = createCopilotSection();
304312
messageElement.appendChild(makerElement);
305313
messageElement.appendChild(document.createElement("br"));
306-
307314
messageElement.classList.add("message", "api-response");
308-
309315
messageWrapper.appendChild(messageElement);
310316

311317
if (!chatMessages) {
@@ -317,15 +323,15 @@
317323
updateThinking: function (thinkingMessage) {
318324
const thinking = document.createElement("div");
319325
thinking.classList.add("thinking");
320-
thinking.setAttribute("tabindex", "0"); // Make the element focusable
321-
thinking.setAttribute("role", "status"); // Add ARIA role
326+
thinking.setAttribute("tabindex", "0");
327+
thinking.setAttribute("role", "status");
322328
messageElement.appendChild(thinking);
323329
chatMessages.scrollTop = chatMessages.scrollHeight - chatMessages.clientHeight;
324330

325-
//This is necessary because
326-
// screen readers may not immediately detect content updates if they occur synchronously
331+
// Update both visual and screen reader elements
327332
setTimeout(() => {
328333
thinking.innerText = thinkingMessage;
334+
liveRegion.innerText = thinkingMessage; // Announce to screen readers
329335
}, 0);
330336
},
331337
updateResponse: function (apiResponse) {
@@ -344,18 +350,28 @@
344350
}
345351

346352
messages.push(message);
347-
348353
messageIndex++;
349354

350355
const apiResponseElement = parseCodeBlocks(apiResponse);
351356
apiResponseElement.setAttribute("aria-live", "assertive"); // Add aria-live attribute to response
352357
messageElement.appendChild(apiResponseElement);
353358

354-
messageWrapper.dataset.id = message.id;
359+
// Create screen reader friendly version
360+
let screenReaderText = copilotStrings.COPILOT_RESPONSE;
361+
apiResponse.forEach(item => {
362+
screenReaderText += item.displayText + " ";
363+
if (item.code && !skipCodes.includes(item.code)) {
364+
const codeBlockStart = copilotStrings.CODE_BLOCK;
365+
const codeBlockEnd = copilotStrings.CODE_BLOCK_END;
366+
screenReaderText += codeBlockStart + item.code + codeBlockEnd;
367+
}
368+
});
355369

356-
messageWrapper.appendChild(document.createElement("hr"));
370+
// Update the live region for screen readers
371+
liveRegion.innerText = screenReaderText;
357372

358-
// Add feedback session for the API response
373+
messageWrapper.dataset.id = message.id;
374+
messageWrapper.appendChild(document.createElement("hr"));
359375
const feedback = createFeedbackDiv();
360376
messageWrapper.appendChild(feedback);
361377
chatMessages.scrollTop = chatMessages.scrollHeight - chatMessages.clientHeight;

‎src/common/copilot/assets/styles/copilot.css

+13-1
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ a {
247247
}
248248

249249
a:focus {
250-
color: var(--vscode-textLink-foreground);
250+
color: var(--vscode-textLink-foreground);
251251
outline: 2px solid var(--vscode-textLink-activeForeground);
252252
outline-offset: 2px;
253253
}
@@ -354,3 +354,15 @@ hr {
354354
font-size: 9px;
355355
/* margin-right: 2px; */
356356
}
357+
358+
.sr-only {
359+
position: absolute;
360+
width: 1px;
361+
height: 1px;
362+
padding: 0;
363+
margin: -1px;
364+
overflow: hidden;
365+
clip: rect(0, 0, 0, 0);
366+
white-space: nowrap;
367+
border-width: 0;
368+
}

‎src/common/copilot/dataverseMetadata.ts

+28
Original file line numberDiff line numberDiff line change
@@ -218,3 +218,31 @@ function parseYamlContent(content: string, sessionID: string, dataverseEntity: s
218218
return {};
219219
}
220220
}
221+
222+
223+
//make call to https://org955590f3.crm10.dynamics.com/api/data/v9.2/EntityDefinitions(LogicalName='powerpagesite') to check if the response object contains MetadataId
224+
export async function isEdmEnvironment(orgUrl: string, apiToken: string): Promise<boolean> {
225+
try {
226+
const dataverseURL = `${orgUrl.endsWith('/') ? orgUrl : orgUrl.concat('/')}api/data/v9.2/EntityDefinitions(LogicalName='powerpagesite')`;
227+
const requestInit: RequestInit = {
228+
method: "GET",
229+
headers: {
230+
'Content-Type': "application/json",
231+
Authorization: `Bearer ${apiToken}`,
232+
"x-ms-user-agent": getUserAgent()
233+
},
234+
};
235+
236+
const startTime = performance.now();
237+
const jsonResponse = await fetchJsonResponse(dataverseURL, requestInit);
238+
const endTime = performance.now();
239+
const responseTime = endTime - startTime || 0;
240+
241+
sendTelemetryEvent({ eventName: isEdmEnvironment.name, durationInMills: responseTime, orgUrl: orgUrl })
242+
return jsonResponse.MetadataId !== undefined
243+
244+
} catch (error) {
245+
sendTelemetryEvent({ eventName: isEdmEnvironment.name, error: error as Error, orgUrl: orgUrl})
246+
return false;
247+
}
248+
}

‎src/common/copilot/user-feedback/CESSurvey.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ function getWebviewContent(feedbackCssUri: vscode.Uri, feedbackJsUri: vscode.Uri
171171
<textarea id="feedbackText" name="feedbackText" rows="5" required></textarea>
172172
<br/>
173173
<p class="privacy-statement">"${vscode.l10n.t('Try and be as specific as possible. Your feedback will be used to improve Copilot. <a href="https://privacy.microsoft.com/en-US/data-privacy-notice"> View privacy details </a>')}"</p>
174-
<button tabindex="0" type="submit" class="submit-feedback">"${vscode.l10n.t('Submit')}"</button>
174+
<button tabindex="0" type="submit" title="${vscode.l10n.t('Submit')}" class="submit-feedback">"${vscode.l10n.t('Submit')}"</button>
175175
</form>
176176
<script type="module" nonce="${nonce}" src="${feedbackJsUri}"></script>
177177
</body>

‎src/common/copilot/user-feedback/feedback.css

+1-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ body {
3030
color: var(--vscode-button-foreground);
3131
cursor: pointer;
3232
padding: 6px 11px;
33-
outline: 1px solid transparent;
3433
outline-offset: 2px !important;
3534
border-radius: 2px;
36-
}
35+
}

‎src/common/services/Interfaces.ts

+18
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,21 @@ export interface IWebsiteDetails {
4646
type?: WebsiteApplicationType;
4747
siteManagementUrl: string;
4848
}
49+
50+
export interface IOtherSiteInfo {
51+
name: string;
52+
websiteId: string;
53+
folderPath: string;
54+
}
55+
56+
export interface WebsiteYaml {
57+
adx_defaultbotconsumerid?: string;
58+
adx_defaultlanguage?: string;
59+
adx_footerwebtemplateid?: string;
60+
adx_headerwebtemplateid?: string;
61+
adx_name?: string;
62+
adx_statecode?: number;
63+
adx_statuscode?: number;
64+
adx_website_language?: number | string;
65+
adx_websiteid?: string;
66+
}

0 commit comments

Comments
 (0)
Please sign in to comment.