diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json
index 4215ccd5..02f57d61 100644
--- a/l10n/bundle.l10n.json
+++ b/l10n/bundle.l10n.json
@@ -93,36 +93,13 @@
"Do not translate 'PCF' as it is a product name."
]
},
- "Preparing pac CLI (v{0}).../{0} represents the version number": {
- "message": "Preparing pac CLI (v{0})...",
- "comment": [
- "{0} represents the version number"
- ]
- },
- "The pac CLI is ready for use in your VS Code terminal!": "The pac CLI is ready for use in your VS Code terminal!",
- "Cannot install pac CLI: {0}/{0} represents the error message returned from the exception": {
- "message": "Cannot install pac CLI: {0}",
- "comment": [
- "{0} represents the error message returned from the exception"
- ]
- },
- "Installing Power Pages generator(v{0}).../{0} represents the version number": {
- "message": "Installing Power Pages generator(v{0})...",
- "comment": [
- "{0} represents the version number"
- ]
- },
- "dotnet sdk 6.0 or greater must be installed/Do not translate 'dotnet' or 'sdk'": {
- "message": "dotnet sdk 6.0 or greater must be installed",
- "comment": [
- "Do not translate 'dotnet' or 'sdk'"
- ]
- },
"The extension Microsoft Edge Tools is required to run this command. Do you want to install it now?": "The extension Microsoft Edge Tools is required to run this command. Do you want to install it now?",
"Install": "Install",
"Cancel": "Cancel",
- "No workspace folder is open.": "No workspace folder is open.",
- "Failed to update launch.json: ${0}": "Failed to update launch.json: ${0}",
+ "Site runtime preview feature is not enabled.": "Site runtime preview feature is not enabled.",
+ "No workspace folder opened. Please open a site folder to preview.": "No workspace folder opened. Please open a site folder to preview.",
+ "Website URL not found.": "Website URL not found.",
+ "Opening site preview...": "Opening site preview...",
"File might be referenced by name {0} here./{0} represents the name of the file": {
"message": "File might be referenced by name {0} here.",
"comment": [
@@ -194,6 +171,12 @@
"Do not translate 'npm'"
]
},
+ "Installing Power Pages generator(v{0}).../{0} represents the version number": {
+ "message": "Installing Power Pages generator(v{0})...",
+ "comment": [
+ "{0} represents the version number"
+ ]
+ },
"Cannot install Power Pages generator: {0}/{0} represents the error message returned from the exception": {
"message": "Cannot install Power Pages generator: {0}",
"comment": [
@@ -223,6 +206,25 @@
"The {3} represents Dataverse Environment's Organization ID (GUID)"
]
},
+ "Preparing pac CLI (v{0}).../{0} represents the version number": {
+ "message": "Preparing pac CLI (v{0})...",
+ "comment": [
+ "{0} represents the version number"
+ ]
+ },
+ "The pac CLI is ready for use in your VS Code terminal!": "The pac CLI is ready for use in your VS Code terminal!",
+ "Cannot install pac CLI: {0}/{0} represents the error message returned from the exception": {
+ "message": "Cannot install pac CLI: {0}",
+ "comment": [
+ "{0} represents the error message returned from the exception"
+ ]
+ },
+ "dotnet sdk 6.0 or greater must be installed/Do not translate 'dotnet' or 'sdk'": {
+ "message": "dotnet sdk 6.0 or greater must be installed",
+ "comment": [
+ "Do not translate 'dotnet' or 'sdk'"
+ ]
+ },
"Confirm": "Confirm",
"Are you sure you want to clear all the Auth Profiles?": "Are you sure you want to clear all the Auth Profiles?",
"Are you sure you want to delete the Auth Profile {0}-{1}?/{0} is the user name, {1} is the URL of environment of the auth profile": {
diff --git a/loc/translations-export/vscode-powerplatform.xlf b/loc/translations-export/vscode-powerplatform.xlf
index 9f9c6f0f..ca709b39 100644
--- a/loc/translations-export/vscode-powerplatform.xlf
+++ b/loc/translations-export/vscode-powerplatform.xlf
@@ -162,9 +162,6 @@ The {3} represents Solution's Type (Managed or Unmanaged), but that test is loca
-
-
-
@@ -300,8 +297,8 @@ The {3} represents Dataverse Environment's Organization ID (GUID)
-
-
+
+
@@ -312,6 +309,9 @@ The {3} represents Dataverse Environment's Organization ID (GUID)
+
+
+
@@ -385,6 +385,9 @@ The {3} represents Dataverse Environment's Organization ID (GUID)
+
+
+
@@ -461,6 +464,9 @@ The {3} represents Dataverse Environment's Organization ID (GUID)
+
+
+
@@ -632,6 +638,9 @@ The second line should be '[TRANSLATION HERE](command:powerplatform-walkthrough.
+
+
+
diff --git a/package.json b/package.json
index ecadf0b2..40939fad 100644
--- a/package.json
+++ b/package.json
@@ -370,6 +370,10 @@
"command": "powerPlatform.previewCurrentActiveUsers",
"title": "Current Active Users",
"icon": "$(person)"
+ },
+ {
+ "command": "microsoft.powerplatform.pages.preview-site",
+ "title": "%powerplatform.pages.previewSite.title%"
}
],
"configuration": {
diff --git a/package.nls.json b/package.nls.json
index 70304aba..e4affd92 100644
--- a/package.nls.json
+++ b/package.nls.json
@@ -97,5 +97,6 @@
"The fifth line should be '[TRANSLATION HERE](command:powerplatform-walkthrough.saveConflict-learn-more).', keeping brackets and the text in the parentheses unmodified"
]
},
- "microsoft-powerplatform-portals.navigation-loop.powerPagesFileExplorer.title": "POWER PAGES ACTIONS"
+ "microsoft-powerplatform-portals.navigation-loop.powerPagesFileExplorer.title": "POWER PAGES ACTIONS",
+ "powerplatform.pages.previewSite.title": "Preview Site"
}
diff --git a/src/client/extension.ts b/src/client/extension.ts
index 0a49712a..1feac8c7 100644
--- a/src/client/extension.ts
+++ b/src/client/extension.ts
@@ -46,7 +46,7 @@ import { PowerPagesAppName, PowerPagesClientName } from "../common/ecs-features/
import { ECSFeaturesClient } from "../common/ecs-features/ecsFeatureClient";
import { getECSOrgLocationValue } from "../common/utilities/Utils";
import { CliAcquisitionContext } from "./lib/CliAcquisitionContext";
-import { PreviewSite } from "./runtime-site-preview/PreviewSite";
+import { PreviewSite, SITE_PREVIEW_COMMAND_ID } from "./runtime-site-preview/PreviewSite";
let client: LanguageClient;
let _context: vscode.ExtensionContext;
@@ -102,6 +102,22 @@ export async function activate(
);
}
+ // portal web view panel
+ _context.subscriptions.push(
+ vscode.commands.registerCommand(
+ "microsoft-powerapps-portals.preview-show",
+ () => {
+ _telemetry.sendTelemetryEvent("StartCommand", {
+ commandId: "microsoft-powerapps-portals.preview-show",
+ });
+ oneDSLoggerWrapper.getLogger().traceInfo("StartCommand", {
+ commandId: "microsoft-powerapps-portals.preview-show"
+ });
+ PortalWebView.createOrShow();
+ }
+ )
+ );
+
// registering bootstrapdiff command
_context.subscriptions.push(
vscode.commands.registerCommand('microsoft-powerapps-portals.bootstrap-diff', async () => {
@@ -180,7 +196,8 @@ export async function activate(
) || [];
- let websiteURL = "";
+ let websiteURL: string | undefined = "";
+ const isSiteRuntimePreviewEnabled = PreviewSite.isSiteRuntimePreviewEnabled();
_context.subscriptions.push(
orgChangeEvent(async (orgDetails: ActiveOrgOutput) => {
@@ -236,7 +253,8 @@ export async function activate(
copilotNotificationShown = true;
}
- if(artemisResponse !== null && PreviewSite.isSiteRuntimePreviewEnabled()) {
+
+ if (artemisResponse !== null && isSiteRuntimePreviewEnabled) {
websiteURL = await PreviewSite.getWebSiteURL(workspaceFolders, artemisResponse?.stamp, orgDetails.EnvironmentId, _telemetry);
}
@@ -260,33 +278,19 @@ export async function activate(
vscode.commands.executeCommand('setContext', 'powerpages.websiteYmlExists', false);
}
- const isEnabled = PreviewSite.isSiteRuntimePreviewEnabled();
-
_telemetry.sendTelemetryEvent("EnableSiteRuntimePreview", {
- isEnabled: isEnabled.toString(),
+ isEnabled: isSiteRuntimePreviewEnabled.toString(),
websiteURL: websiteURL
});
oneDSLoggerWrapper.getLogger().traceInfo("EnableSiteRuntimePreview", {
- isEnabled: isEnabled.toString(),
+ isEnabled: isSiteRuntimePreviewEnabled.toString(),
websiteURL: websiteURL
});
_context.subscriptions.push(
vscode.commands.registerCommand(
- "microsoft-powerapps-portals.preview-show",
- () => {
- if (!isEnabled) {
- _telemetry.sendTelemetryEvent("StartCommand", {
- commandId: "microsoft-powerapps-portals.preview-show",
- });
- oneDSLoggerWrapper.getLogger().traceInfo("StartCommand", {
- commandId: "microsoft-powerapps-portals.preview-show"
- });
- PortalWebView.createOrShow();
- } else {
- PreviewSite.launchBrowserAndDevToolsWithinVsCode(websiteURL);
- }
- }
+ SITE_PREVIEW_COMMAND_ID,
+ async () => await PreviewSite.handlePreviewRequest(isSiteRuntimePreviewEnabled, websiteURL, _telemetry)
)
);
diff --git a/src/client/runtime-site-preview/LaunchJsonHelper.ts b/src/client/runtime-site-preview/LaunchJsonHelper.ts
deleted file mode 100644
index 9fc29b20..00000000
--- a/src/client/runtime-site-preview/LaunchJsonHelper.ts
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- */
-
-import * as vscode from 'vscode';
-
-export async function updateLaunchJsonConfig(url: string): Promise {
-
- const workspaceFolders = vscode.workspace.workspaceFolders;
- if (!workspaceFolders) {
- vscode.window.showErrorMessage(
- vscode.l10n.t('No workspace folder is open.'));
- return;
- }
-
- const launchJsonPath = vscode.Uri.joinPath(workspaceFolders[0].uri, '.vscode', 'launch.json');
- try {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- let launchJson: any;
- let launchJsonDoc: vscode.TextDocument | undefined;
-
- try {
- launchJsonDoc = await vscode.workspace.openTextDocument(launchJsonPath);
- const launchJsonText = launchJsonDoc.getText();
- launchJson = launchJsonText ? JSON.parse(launchJsonText) : { configurations: [], compounds: [] };
- } catch (error) {
- // If the file does not exist or is empty, initialize it
- launchJson = { configurations: [], compounds: [] };
- }
-
- // Update or add the configurations for Microsoft Edge
- const edgeConfigurations = [
- {
- type: 'pwa-msedge',
- name: 'Launch Microsoft Edge',
- request: 'launch',
- runtimeArgs: ['--remote-debugging-port=9222'],
- url: url,
- presentation: {
- hidden: true
- }
- },
- {
- type: 'pwa-msedge',
- name: 'Launch Microsoft Edge in headless mode',
- request: 'launch',
- runtimeArgs: ['--headless', '--remote-debugging-port=9222'],
- url: url,
- presentation: {
- hidden: true
- }
- },
- {
- type: 'vscode-edge-devtools.debug',
- name: 'Open Edge DevTools',
- request: 'attach',
- url: url,
- presentation: {
- hidden: true
- }
- }
- ];
-
- // Update or add the compounds for Microsoft Edge
- const edgeCompounds = [
- {
- name: 'Launch Edge Headless and attach DevTools',
- configurations: ['Launch Microsoft Edge in headless mode', 'Open Edge DevTools']
- },
- {
- name: 'Launch Edge and attach DevTools',
- configurations: ['Launch Microsoft Edge', 'Open Edge DevTools']
- }
- ];
-
- // Merge the new configurations and compounds with the existing ones
- launchJson.configurations = [...launchJson.configurations, ...edgeConfigurations];
- launchJson.compounds = [...launchJson.compounds, ...edgeCompounds];
-
- // Write the updated launch.json file
- const launchJsonContent = JSON.stringify(launchJson, null, 4);
- await vscode.workspace.fs.writeFile(launchJsonPath, Buffer.from(launchJsonContent, 'utf8'));
- } catch (e) {
- if(e instanceof Error) {
- vscode.window.showErrorMessage(
- vscode.l10n.t("Failed to update launch.json: ${0}", e.message));
- }
- }
-}
diff --git a/src/client/runtime-site-preview/PreviewSite.ts b/src/client/runtime-site-preview/PreviewSite.ts
index 2fd34e54..dc5cbe13 100644
--- a/src/client/runtime-site-preview/PreviewSite.ts
+++ b/src/client/runtime-site-preview/PreviewSite.ts
@@ -4,33 +4,33 @@
*/
import * as vscode from 'vscode';
-import * as path from 'path';
-import * as fs from 'fs';
-import { updateLaunchJsonConfig } from './LaunchJsonHelper';
import { ECSFeaturesClient } from '../../common/ecs-features/ecsFeatureClient';
import { EnableSiteRuntimePreview } from '../../common/ecs-features/ecsFeatureGates';
import { ITelemetry } from '../../common/OneDSLoggerTelemetry/telemetry/ITelemetry';
import { WorkspaceFolder } from 'vscode-languageclient/node';
-import { getWebsiteRecordID } from '../../common/utilities/WorkspaceInfoFinderUtil';
+import { getWebsiteRecordId } from '../../common/utilities/WorkspaceInfoFinderUtil';
import { ServiceEndpointCategory } from '../../common/services/Constants';
import { PPAPIService } from '../../common/services/PPAPIService';
import { VSCODE_EXTENSION_GET_WEBSITE_RECORD_ID_EMPTY } from '../../common/services/TelemetryConstants';
+import { EDGE_TOOLS_EXTENSION_ID } from '../../common/constants';
+import { oneDSLoggerWrapper } from "../../common/OneDSLoggerTelemetry/oneDSLoggerWrapper";
+import { showProgressNotification } from '../../common/controls/ShowProgressNotification';
-export class PreviewSite {
+export const SITE_PREVIEW_COMMAND_ID = "microsoft.powerplatform.pages.preview-site";
+export class PreviewSite {
static isSiteRuntimePreviewEnabled(): boolean {
const enableSiteRuntimePreview = ECSFeaturesClient.getConfig(EnableSiteRuntimePreview).enableSiteRuntimePreview
- if(enableSiteRuntimePreview === undefined) {
+ if (enableSiteRuntimePreview === undefined) {
return false;
}
- return enableSiteRuntimePreview;
+ return true;
}
static async getWebSiteURL(workspaceFolders: WorkspaceFolder[], stamp: ServiceEndpointCategory, envId: string, telemetry: ITelemetry): Promise {
-
- const websiteRecordId = getWebsiteRecordID(workspaceFolders, telemetry);
+ const websiteRecordId = getWebsiteRecordId(workspaceFolders, telemetry);
if (!websiteRecordId) {
telemetry.sendTelemetryEvent(VSCODE_EXTENSION_GET_WEBSITE_RECORD_ID_EMPTY, {
websiteRecordId: websiteRecordId
@@ -41,54 +41,14 @@ export class PreviewSite {
return websiteDetails?.websiteUrl || "";
}
- static async launchBrowserAndDevToolsWithinVsCode(webSitePreviewURL: string): Promise {
-
- const edgeToolsExtensionId = 'ms-edgedevtools.vscode-edge-devtools';
- const edgeToolsExtension = vscode.extensions.getExtension(edgeToolsExtensionId);
-
- if (edgeToolsExtension) {
- // Preserve the original state of the launch.json file and .vscode folder
- const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
- const vscodeFolderPath = workspaceFolder ? path.join(workspaceFolder.uri.fsPath, '.vscode') : null;
- const launchJsonPath = vscodeFolderPath ? path.join(vscodeFolderPath, 'launch.json') : null;
- let originalLaunchJsonContent: string | null = null;
- let vscodeFolderExisted = false;
+ static async launchBrowserAndDevToolsWithinVsCode(webSitePreviewURL: string | undefined): Promise {
+ if (!webSitePreviewURL || webSitePreviewURL === "") {
+ return;
+ }
- if (vscodeFolderPath && fs.existsSync(vscodeFolderPath)) {
- vscodeFolderExisted = true;
- if (launchJsonPath && fs.existsSync(launchJsonPath)) {
- originalLaunchJsonContent = fs.readFileSync(launchJsonPath, 'utf8');
- }
- }
+ const edgeToolsExtension = vscode.extensions.getExtension(EDGE_TOOLS_EXTENSION_ID);
- await updateLaunchJsonConfig(webSitePreviewURL);
-
- try {
- // Added a 2-second delay before executing the launchProject command to handle the case where the launch.json file is not saved yet
- await new Promise(resolve => setTimeout(resolve, 2000));
- await vscode.commands.executeCommand('vscode-edge-devtools-view.launchProject');
-
- } finally {
- // Revert the changes made to the launch.json file and remove the .vscode folder if it was created
-
- // Added a 2-second delay to ensure that debug session is closed and then launch.json file is removed
- await new Promise(resolve => setTimeout(resolve, 2000));
- if (launchJsonPath) {
- if (originalLaunchJsonContent !== null) {
- fs.writeFileSync(launchJsonPath, originalLaunchJsonContent, 'utf8');
- } else if (fs.existsSync(launchJsonPath)) {
- fs.unlinkSync(launchJsonPath);
- }
- }
-
- if (vscodeFolderPath && !vscodeFolderExisted && fs.existsSync(vscodeFolderPath)) {
- const files = fs.readdirSync(vscodeFolderPath);
- if (files.length === 0) {
- fs.rmdirSync(vscodeFolderPath);
- }
- }
- }
- } else {
+ if (!edgeToolsExtension) {
const install = await vscode.window.showWarningMessage(
vscode.l10n.t(
`The extension Microsoft Edge Tools is required to run this command. Do you want to install it now?`
@@ -98,11 +58,45 @@ export class PreviewSite {
);
if (install === vscode.l10n.t('Install')) {
- // Open the Extensions view with the specific extension
- vscode.commands.executeCommand('workbench.extensions.search', edgeToolsExtensionId);
+ await vscode.commands.executeCommand('workbench.extensions.search', EDGE_TOOLS_EXTENSION_ID);
}
return;
}
+
+ const settings = vscode.workspace.getConfiguration('vscode-edge-devtools');
+ const currentDefaultUrl = await settings.get('defaultUrl');
+ await settings.update('defaultUrl', webSitePreviewURL);
+ await vscode.commands.executeCommand('vscode-edge-devtools-view.launch');
+ await settings.update('defaultUrl', currentDefaultUrl);
+ }
+
+ static async handlePreviewRequest(isSiteRuntimePreviewEnabled: boolean, websiteURL: string | undefined, telemetry: ITelemetry) {
+ telemetry.sendTelemetryEvent("StartCommand", {
+ commandId: SITE_PREVIEW_COMMAND_ID,
+ });
+ oneDSLoggerWrapper.getLogger().traceInfo("StartCommand", {
+ commandId: SITE_PREVIEW_COMMAND_ID
+ });
+
+ if (!isSiteRuntimePreviewEnabled) {
+ await vscode.window.showInformationMessage(vscode.l10n.t("Site runtime preview feature is not enabled."));
+ return;
+ }
+
+ if (!vscode.workspace.workspaceFolders) {
+ await vscode.window.showErrorMessage(vscode.l10n.t("No workspace folder opened. Please open a site folder to preview."));
+ return;
+ }
+
+ if (!websiteURL || websiteURL === "") {
+ await vscode.window.showErrorMessage(vscode.l10n.t("Website URL not found."));
+ return;
+ }
+
+ await showProgressNotification(
+ vscode.l10n.t('Opening site preview...'),
+ async () => await PreviewSite.launchBrowserAndDevToolsWithinVsCode(websiteURL)
+ );
}
}
diff --git a/src/common/OneDSLoggerTelemetry/telemetry/telemetry.ts b/src/common/OneDSLoggerTelemetry/telemetry/telemetry.ts
index 1975fb82..0eeaaa85 100644
--- a/src/common/OneDSLoggerTelemetry/telemetry/telemetry.ts
+++ b/src/common/OneDSLoggerTelemetry/telemetry/telemetry.ts
@@ -18,7 +18,7 @@ export const CleanupRelatedFilesEvent = 'CleanupRelatedFilesEvent';
export const UpdateEntityNameInYmlEvent = 'UpdateEntityNameInYmlEvent';
export const UserFileCreateEvent = 'UserFileCreateEvent';
export const FileCreateEvent = 'FileCreateEvent';
-export const GetWebsiteRecordID = 'getWebsiteRecordID';
+export const GetWebsiteRecordID = 'getWebsiteRecordId';
interface ITelemetryData {
eventName: string,
@@ -69,4 +69,3 @@ export function sendTelemetryEvent(telemetry: ITelemetry, telemetryData: ITeleme
oneDSLoggerWrapper.getLogger().traceInfo(telemetryData.eventName, telemetryDataProperties, telemetryDataMeasurements);
}
}
-
diff --git a/src/common/constants.ts b/src/common/constants.ts
index b7c36538..410a0aac 100644
--- a/src/common/constants.ts
+++ b/src/common/constants.ts
@@ -94,3 +94,4 @@ export const VSCODE_EXTENSION_COPILOT_CONTEXT_RELATED_FILES_FETCH_FAILED = "VSCo
export const ADX_WEBPAGE = 'adx_webpage'
export const HTML_FILE_EXTENSION = '.html';
export const UTF8_ENCODING = 'utf8';
+export const EDGE_TOOLS_EXTENSION_ID = 'ms-edgedevtools.vscode-edge-devtools';
diff --git a/src/common/controls/ShowProgressNotification.ts b/src/common/controls/ShowProgressNotification.ts
new file mode 100644
index 00000000..5a7c606f
--- /dev/null
+++ b/src/common/controls/ShowProgressNotification.ts
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ */
+
+import * as vscode from 'vscode';
+import { Progress } from 'vscode';
+import { CancellationToken } from 'vscode-languageserver';
+
+export async function showProgressNotification(title: string, task: (progress: Progress<{
+ message: string;
+ increment?: number;
+}>, token: CancellationToken) => Thenable) {
+ await vscode.window.withProgress({
+ location: vscode.ProgressLocation.Notification,
+ title,
+ cancellable: false
+ }, async (progress, token) => await task(progress, token));
+}
diff --git a/src/common/utilities/WorkspaceInfoFinderUtil.ts b/src/common/utilities/WorkspaceInfoFinderUtil.ts
index 4d60e91a..e0ed9944 100644
--- a/src/common/utilities/WorkspaceInfoFinderUtil.ts
+++ b/src/common/utilities/WorkspaceInfoFinderUtil.ts
@@ -40,7 +40,7 @@ export function getPortalsOrgURLs(workspaceRootFolders: WorkspaceFolder[] | null
return output;
}
-export function getWebsiteRecordID(workspaceFolders: { uri: string }[], telemetry: ITelemetry): string {
+export function getWebsiteRecordId(workspaceFolders: { uri: string }[], telemetry: ITelemetry): string {
try {
if (!workspaceFolders || workspaceFolders.length === 0) {
return "";
@@ -56,7 +56,7 @@ export function getWebsiteRecordID(workspaceFolders: { uri: string }[], telemetr
}
}
} catch (exception) {
- sendTelemetryEvent(telemetry, { methodName: getWebsiteRecordID.name, eventName: GetWebsiteRecordID, exception: exception as Error });
+ sendTelemetryEvent(telemetry, { methodName: getWebsiteRecordId.name, eventName: GetWebsiteRecordID, exception: exception as Error });
}
return "";
}