Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions l10n/bundle.l10n.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
},
"Enter the environment URL": "Enter the environment URL",
"Active auth profile is not found or has expired. To create a new auth profile, enter the environment URL.": "Active auth profile is not found or has expired. To create a new auth profile, enter the environment URL.",
"Website not found in the environment. Please check the credentials and root folder path.": "Website not found in the environment. Please check the credentials and root folder path.",
"Selection is empty.": "Selection is empty.",
"PREVIEW": "PREVIEW",
"Explain the following code snippet:": "Explain the following code snippet:",
Expand Down Expand Up @@ -83,9 +84,9 @@
"Hi! Power Pages lets you build secure, professional websites that you can quickly configure and publish across web browsers and devices.\n\nTo create your website, visit the [Power Pages](https://powerpages.microsoft.com/).\nReturn to this chat and @powerpages can help you write and edit your website code.": "Hi! Power Pages lets you build secure, professional websites that you can quickly configure and publish across web browsers and devices.\n\nTo create your website, visit the [Power Pages](https://powerpages.microsoft.com/).\nReturn to this chat and @powerpages can help you write and edit your website code.",
"Checking for active auth profile...": "Checking for active auth profile...",
"@PowerPages is not yet available in your region.": "@PowerPages is not yet available in your region.",
"Failed to get site content from NL2Site service": "Failed to get site content from NL2Site service",
"Generating webpages...": "Generating webpages...",
"Generating a new Power Pages site...": "Generating a new Power Pages site...",
"Failed to create a new Power Pages site. Please try again.": "Failed to create a new Power Pages site. Please try again.",
"Select Folder for new PCF Control/Do not translate 'PCF' as it is a product name.": {
"message": "Select Folder for new PCF Control",
"comment": [
Expand Down Expand Up @@ -117,6 +118,11 @@
"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}",
"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": [
Expand Down Expand Up @@ -219,7 +225,6 @@
},
"Confirm": "Confirm",
"Are you sure you want to clear all the Auth Profiles?": "Are you sure you want to clear all the Auth Profiles?",
"Cancel": "Cancel",
"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": {
"message": "Are you sure you want to delete the Auth Profile {0}-{1}?",
"comment": [
Expand Down
19 changes: 17 additions & 2 deletions loc/translations-export/vscode-powerplatform.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ The {3} represents Solution's Type (Managed or Unmanaged), but that test is loca
<trans-unit id="++CODE++f9e17ed11037dab93f8820c30db63b2ff2a045b5761f71818b7291afae60f199">
<source xml:lang="en">Explain the following code {% include 'Page Copy'%}</source>
</trans-unit>
<trans-unit id="++CODE++b985f1515c42b4b5b0c11a3d7b3286fc9d66997d476668ab1f93a4a11499fef5">
<source xml:lang="en">Failed to create a new Power Pages site. Please try again.</source>
</trans-unit>
<trans-unit id="++CODE++2310c6b7e5953cab877859ba1fcfa98e58e1508677df9412010e9b578ea237f4">
<source xml:lang="en">Failed to create: {0}.</source>
<note>{0} will be replaced by the error message.</note>
Expand All @@ -159,8 +162,8 @@ The {3} represents Solution's Type (Managed or Unmanaged), but that test is loca
<trans-unit id="++CODE++862d6197d64601aa13ce30db5ec5b8f819ad00fe21e3b031a3e47fe22ef68fb3">
<source xml:lang="en">Failed to get file ready for edit: {0}</source>
</trans-unit>
<trans-unit id="++CODE++41405814c44fb391a3f8e31d1a3bc20299cd2e87979ebfbd1eb9488be12c617a">
<source xml:lang="en">Failed to get site content from NL2Site service</source>
<trans-unit id="++CODE++bc7c38bba120feb9d6acc70f0a26050f3ce2a70dd87afa046c3f962be7d015e3">
<source xml:lang="en">Failed to update launch.json: ${0}</source>
</trans-unit>
<trans-unit id="++CODE++a9e36b880dd45b64ae5601865540605296febf9bd855fc46d9c35c2c2ed9a7f2">
<source xml:lang="en">Feature is not enabled for this geo.</source>
Expand Down Expand Up @@ -217,6 +220,9 @@ Return to this chat and @powerpages can help you write and edit your website cod
<trans-unit id="++CODE++e992151b1efc99f93484c7d7f3076b66ab072a8af2383e96104cc597c971339c">
<source xml:lang="en">Insert code into editor</source>
</trans-unit>
<trans-unit id="++CODE++569ca49f4aaf7846e952c1d4aeca72febd0b79fa1c4f9db08fd3127551218572">
<source xml:lang="en">Install</source>
</trans-unit>
<trans-unit id="++CODE++25109e9c19daeeed3977b84ace83722ac8a4daafcfe4e3709082fcc5b228e7a8">
<source xml:lang="en">Installing Power Pages generator(v{0})...</source>
<note>{0} represents the version number</note>
Expand Down Expand Up @@ -294,6 +300,9 @@ The {3} represents Dataverse Environment's Organization ID (GUID)</note>
<trans-unit id="++CODE++6da29e062697a9f26659ef14ebda075afe939756af5d8e1d3451eb7d6d6e1a8a">
<source xml:lang="en">No workspace folder found</source>
</trans-unit>
<trans-unit id="++CODE++3c8a93afe6d6e99f8ad4b7fd72ffe91e62b1cbc29a8887d4922f80e92f4b78b2">
<source xml:lang="en">No workspace folder is open.</source>
</trans-unit>
<trans-unit id="++CODE++bda6bda1e902d120a7f4515ceac8546c3112e3cb9351df1d8b9713b8f86e0370">
<source xml:lang="en">One or more attribute names have been changed or removed. Contact your admin.</source>
</trans-unit>
Expand Down Expand Up @@ -391,6 +400,9 @@ The {3} represents Dataverse Environment's Organization ID (GUID)</note>
<trans-unit id="++CODE++618f7afd7be2f12bf2ebbaba5bc7e951d2ebfb11d4480647f991fbb664caa26e">
<source xml:lang="en">The Power Pages generator is ready for use in your VS Code extension!</source>
</trans-unit>
<trans-unit id="++CODE++538ecf1398703f8a2048b99a2b2a533012b06ee88d67a0fd6fbbcd716cfbd663">
<source xml:lang="en">The extension Microsoft Edge Tools is required to run this command. Do you want to install it now?</source>
</trans-unit>
<trans-unit id="++CODE++e4a2396fd7a366292a40abc87b18a2329458c258f4d0e0e593e6189dff19a117">
<source xml:lang="en">The name you want to give to this authentication profile</source>
</trans-unit>
Expand Down Expand Up @@ -449,6 +461,9 @@ The {3} represents Dataverse Environment's Organization ID (GUID)</note>
<trans-unit id="++CODE++00dc171124ab430bbbaae51ec39dda1c5e7d045f382f56b1767d9e733292731c">
<source xml:lang="en">Webpage names should contain only letters, numbers, hyphens, or underscores.</source>
</trans-unit>
<trans-unit id="++CODE++4ed689987736ce09e17c67eb32f441ca7d0f7ed013a282aafd1ebcf302e6386b">
<source xml:lang="en">Website not found in the environment. Please check the credentials and root folder path.</source>
</trans-unit>
<trans-unit id="++CODE++e4bb03b399c07eeda658d87305435a58818cb5e0c3b76ae054d99564cf14ef6b">
<source xml:lang="en">What do you need help with?</source>
</trans-unit>
Expand Down
52 changes: 36 additions & 16 deletions src/client/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { AadIdKey, EnvIdKey, TenantIdKey } from "../common/OneDSLoggerTelemetry/
import { PowerPagesAppName, PowerPagesClientName } from "../common/ecs-features/constants";
import { ECSFeaturesClient } from "../common/ecs-features/ecsFeatureClient";
import { getECSOrgLocationValue } from "../common/utilities/Utils";
import { PreviewSite } from "./runtime-site-preview/PreviewSite";

let client: LanguageClient;
let _context: vscode.ExtensionContext;
Expand Down Expand Up @@ -101,22 +102,6 @@ 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 () => {
Expand Down Expand Up @@ -195,6 +180,8 @@ export async function activate(
) || [];


let websiteURL = "";

_context.subscriptions.push(
orgChangeEvent(async (orgDetails: ActiveOrgOutput) => {
const orgID = orgDetails.OrgId;
Expand Down Expand Up @@ -249,6 +236,9 @@ export async function activate(
copilotNotificationShown = true;

}
if(artemisResponse !== null && PreviewSite.isSiteRuntimePreviewEnabled()) {
websiteURL = await PreviewSite.getWebSiteURL(workspaceFolders, artemisResponse?.stamp, orgDetails.EnvironmentId, _telemetry);
}

})
);
Expand All @@ -270,6 +260,36 @@ export async function activate(
vscode.commands.executeCommand('setContext', 'powerpages.websiteYmlExists', false);
}

const isEnabled = PreviewSite.isSiteRuntimePreviewEnabled();

_telemetry.sendTelemetryEvent("EnableSiteRuntimePreview", {
isEnabled: isEnabled.toString(),
websiteURL: websiteURL
});
oneDSLoggerWrapper.getLogger().traceInfo("EnableSiteRuntimePreview", {
isEnabled: isEnabled.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);
}
}
)
);

const workspaceFolderWatcher = vscode.workspace.onDidChangeWorkspaceFolders(handleWorkspaceFolderChange);
_context.subscriptions.push(workspaceFolderWatcher);

Expand Down
90 changes: 90 additions & 0 deletions src/client/runtime-site-preview/LaunchJsonHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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<void> {

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));
}
}
}
108 changes: 108 additions & 0 deletions src/client/runtime-site-preview/PreviewSite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* 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 * 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 { ServiceEndpointCategory } from '../../common/services/Constants';
import { PPAPIService } from '../../common/services/PPAPIService';
import { VSCODE_EXTENSION_GET_WEBSITE_RECORD_ID_EMPTY } from '../../common/services/TelemetryConstants';

export class PreviewSite {

static isSiteRuntimePreviewEnabled(): boolean {
const enableSiteRuntimePreview = ECSFeaturesClient.getConfig(EnableSiteRuntimePreview).enableSiteRuntimePreview

if(enableSiteRuntimePreview === undefined) {
return false;
}

return enableSiteRuntimePreview;
}

static async getWebSiteURL(workspaceFolders: WorkspaceFolder[], stamp: ServiceEndpointCategory, envId: string, telemetry: ITelemetry): Promise<string> {

const websiteRecordId = getWebsiteRecordID(workspaceFolders, telemetry);
if (!websiteRecordId) {
telemetry.sendTelemetryEvent(VSCODE_EXTENSION_GET_WEBSITE_RECORD_ID_EMPTY, {
websiteRecordId: websiteRecordId
});
return "";
}
const websiteDetails = await PPAPIService.getWebsiteDetailsByWebsiteRecordId(stamp, envId, websiteRecordId, telemetry);
return websiteDetails?.websiteUrl || "";
}

static async launchBrowserAndDevToolsWithinVsCode(webSitePreviewURL: string): Promise<void> {

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;

if (vscodeFolderPath && fs.existsSync(vscodeFolderPath)) {
vscodeFolderExisted = true;
if (launchJsonPath && fs.existsSync(launchJsonPath)) {
originalLaunchJsonContent = fs.readFileSync(launchJsonPath, 'utf8');
}
}

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 {
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?`
),
vscode.l10n.t('Install'),
vscode.l10n.t('Cancel')
);

if (install === vscode.l10n.t('Install')) {
// Open the Extensions view with the specific extension
vscode.commands.executeCommand('workbench.extensions.search', edgeToolsExtensionId);
}

return;
}
}
}
1 change: 1 addition & 0 deletions src/common/OneDSLoggerTelemetry/telemetry/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const CleanupRelatedFilesEvent = 'CleanupRelatedFilesEvent';
export const UpdateEntityNameInYmlEvent = 'UpdateEntityNameInYmlEvent';
export const UserFileCreateEvent = 'UserFileCreateEvent';
export const FileCreateEvent = 'FileCreateEvent';
export const GetWebsiteRecordID = 'getWebsiteRecordID';

interface ITelemetryData {
eventName: string,
Expand Down
Loading