diff --git a/package-lock.json b/package-lock.json index 9c0a76b2..b02fa115 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "glob": "^7.1.7", "gpt-tokenizer": "^2.1.1", "https-browserify": "^1.0.0", + "js-yaml": "^4.1.0", "liquidjs": "^10.2.0", "n-readlines": "^1.0.1", "puppeteer-core": "^14.4.1", @@ -71,7 +72,7 @@ "eslint": "^8.11.0", "eslint-plugin-header": "^3.1.1", "fancy-log": "^1.3.3", - "fs-extra": "^9.0.1", + "fs-extra": "^9.1.0", "get-func-name": "^2.0.2", "gulp": "^4.0.2", "gulp-eslint": "^6.0.0", @@ -8960,6 +8961,7 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, + "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -12271,6 +12273,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, diff --git a/package.json b/package.json index 27d4e510..74aff018 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,9 @@ "onStartupFinished", "workspaceContains:**/**.portalconfig", "onDebug", - "onFileSystem:powerplatform-vfs" + "onFileSystem:powerplatform-vfs", + "onCommand:extension.openWebpage", + "onCommand:extension.openFile" ], "capabilities": { "untrustedWorkspaces": { @@ -167,6 +169,18 @@ "dark": "src/web/client/assets/microsoftTeamsIcon/dark/microsoftTeams.svg" } }, + { + "command": "extension.openWebpage", + "title": "Open Webpage" + }, + { + "command": "extension.openFile", + "title": "Open File" + }, + { + "command": "extension.activateDirectory", + "title": "Activate Directory" + }, { "command": "powerpages.collaboration.openMail", "title": "Open Mail", @@ -944,6 +958,10 @@ } ], "explorer": [ + { + "id": "exampleView", + "name": "Example View" + }, { "id": "powerpages.powerPagesFileExplorer", "name": "%microsoft-powerplatform-portals.navigation-loop.powerPagesFileExplorer.title%", @@ -1053,7 +1071,7 @@ "eslint": "^8.11.0", "eslint-plugin-header": "^3.1.1", "fancy-log": "^1.3.3", - "fs-extra": "^9.0.1", + "fs-extra": "^9.1.0", "get-func-name": "^2.0.2", "gulp": "^4.0.2", "gulp-eslint": "^6.0.0", @@ -1088,6 +1106,7 @@ "@fluidframework/azure-client": "^1.2.0", "@microsoft/1ds-core-js": "4.0.5", "@microsoft/1ds-post-js": "4.0.5", + "@maker-studio/powerportals-preview-engine": "^3.7.24", "@microsoft/generator-powerpages": "1.21.19", "@types/jwt-decode": "2.2.0", "@types/node-fetch": "^2.6.2", @@ -1100,6 +1119,7 @@ "glob": "^7.1.7", "gpt-tokenizer": "^2.1.1", "https-browserify": "^1.0.0", + "js-yaml": "^4.1.0", "liquidjs": "^10.2.0", "n-readlines": "^1.0.1", "puppeteer-core": "^14.4.1", diff --git a/src/client/PortalWebView.ts b/src/client/PortalWebView.ts index cc0669c5..ca02d24d 100644 --- a/src/client/PortalWebView.ts +++ b/src/client/PortalWebView.ts @@ -170,7 +170,7 @@ export class PortalWebView { return html; } - private static getPortalRootFolder(): vscode.Uri | null { + public static getPortalRootFolder(): vscode.Uri | null { const fileBeingEdited = vscode.window.activeTextEditor as vscode.TextEditor; if (fileBeingEdited) { for (let i = 0; !!(vscode.workspace.workspaceFolders) && (i < vscode.workspace.workspaceFolders?.length); i++) { diff --git a/src/client/extension.ts b/src/client/extension.ts index 0a8e6871..43c95407 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -41,6 +41,7 @@ import { ActiveOrgOutput } from "./pac/PacTypes"; import { telemetryEventNames } from "./telemetry/TelemetryEventNames"; import { IArtemisAPIOrgResponse } from "../common/services/Interfaces"; import { ArtemisService } from "../common/services/ArtemisService"; +import { treeView } from "../common/TreeStructure/DataMapper"; let client: LanguageClient; let _context: vscode.ExtensionContext; @@ -69,6 +70,10 @@ export async function activate( context.subscriptions.push(_telemetry); // Logging telemetry in US cluster for unauthenticated scenario oneDSLoggerWrapper.instantiate("us"); + oneDSLoggerWrapper.getLogger().traceInfo("Instantiating tree view", { + "instantiate": performance.now() + }); + await treeView(); _telemetry.sendTelemetryEvent("Start", { "pac.userId": readUserSettings().uniqueId, @@ -483,4 +488,4 @@ class CliAcquisitionContext implements ICliAcquisitionContext { comment: ["Do not translate 'dotnet' or 'sdk'"] }); } -} +} \ No newline at end of file diff --git a/src/client/portal_fileicons/icons/dark/book.svg b/src/client/portal_fileicons/icons/dark/book.svg new file mode 100644 index 00000000..669db404 --- /dev/null +++ b/src/client/portal_fileicons/icons/dark/book.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/client/portal_fileicons/icons/dark/css.svg b/src/client/portal_fileicons/icons/dark/css.svg new file mode 100644 index 00000000..ec75da91 --- /dev/null +++ b/src/client/portal_fileicons/icons/dark/css.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/client/portal_fileicons/icons/dark/file-submodule.svg b/src/client/portal_fileicons/icons/dark/file-submodule.svg new file mode 100644 index 00000000..79018e08 --- /dev/null +++ b/src/client/portal_fileicons/icons/dark/file-submodule.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/client/portal_fileicons/icons/dark/file-symlink-file.svg b/src/client/portal_fileicons/icons/dark/file-symlink-file.svg new file mode 100644 index 00000000..2aff0ee4 --- /dev/null +++ b/src/client/portal_fileicons/icons/dark/file-symlink-file.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/client/portal_fileicons/icons/dark/html.svg b/src/client/portal_fileicons/icons/dark/html.svg new file mode 100644 index 00000000..af331070 --- /dev/null +++ b/src/client/portal_fileicons/icons/dark/html.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/client/portal_fileicons/icons/dark/js.svg b/src/client/portal_fileicons/icons/dark/js.svg new file mode 100644 index 00000000..d6ced9e7 --- /dev/null +++ b/src/client/portal_fileicons/icons/dark/js.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/client/portal_fileicons/icons/dark/json.svg b/src/client/portal_fileicons/icons/dark/json.svg new file mode 100644 index 00000000..af583f76 --- /dev/null +++ b/src/client/portal_fileicons/icons/dark/json.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/client/portal_fileicons/icons/dark/list-flat.svg b/src/client/portal_fileicons/icons/dark/list-flat.svg new file mode 100644 index 00000000..042ad665 --- /dev/null +++ b/src/client/portal_fileicons/icons/dark/list-flat.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/client/portal_fileicons/icons/dark/mp4.svg b/src/client/portal_fileicons/icons/dark/mp4.svg new file mode 100644 index 00000000..aceee8c4 --- /dev/null +++ b/src/client/portal_fileicons/icons/dark/mp4.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/client/portal_fileicons/icons/dark/note.svg b/src/client/portal_fileicons/icons/dark/note.svg new file mode 100644 index 00000000..8e013c11 --- /dev/null +++ b/src/client/portal_fileicons/icons/dark/note.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/client/portal_fileicons/icons/dark/png.svg b/src/client/portal_fileicons/icons/dark/png.svg new file mode 100644 index 00000000..e4f64df5 --- /dev/null +++ b/src/client/portal_fileicons/icons/dark/png.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/client/portal_fileicons/icons/dark/referrences.svg b/src/client/portal_fileicons/icons/dark/referrences.svg new file mode 100644 index 00000000..280b3d83 --- /dev/null +++ b/src/client/portal_fileicons/icons/dark/referrences.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/client/portal_fileicons/icons/dark/yml.svg b/src/client/portal_fileicons/icons/dark/yml.svg new file mode 100644 index 00000000..551def40 --- /dev/null +++ b/src/client/portal_fileicons/icons/dark/yml.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/client/portal_fileicons/icons/light/book.svg b/src/client/portal_fileicons/icons/light/book.svg new file mode 100644 index 00000000..82ccad3f --- /dev/null +++ b/src/client/portal_fileicons/icons/light/book.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/client/portal_fileicons/icons/light/file-submodule.svg b/src/client/portal_fileicons/icons/light/file-submodule.svg new file mode 100644 index 00000000..2161152f --- /dev/null +++ b/src/client/portal_fileicons/icons/light/file-submodule.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/client/portal_fileicons/icons/light/file-symlink-file.svg b/src/client/portal_fileicons/icons/light/file-symlink-file.svg new file mode 100644 index 00000000..f2de1bf1 --- /dev/null +++ b/src/client/portal_fileicons/icons/light/file-symlink-file.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/client/portal_fileicons/icons/light/json.svg b/src/client/portal_fileicons/icons/light/json.svg new file mode 100644 index 00000000..d7a79a34 --- /dev/null +++ b/src/client/portal_fileicons/icons/light/json.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/client/portal_fileicons/icons/light/list-flat.svg b/src/client/portal_fileicons/icons/light/list-flat.svg new file mode 100644 index 00000000..b1d9eca2 --- /dev/null +++ b/src/client/portal_fileicons/icons/light/list-flat.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/client/portal_fileicons/icons/light/note.svg b/src/client/portal_fileicons/icons/light/note.svg new file mode 100644 index 00000000..81a134ba --- /dev/null +++ b/src/client/portal_fileicons/icons/light/note.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/client/power-pages/commonUtility.ts b/src/client/power-pages/commonUtility.ts index cdb8a8fd..de48a8b3 100644 --- a/src/client/power-pages/commonUtility.ts +++ b/src/client/power-pages/commonUtility.ts @@ -197,3 +197,16 @@ export function getRegExPattern(fileNameArray: string[]): RegExp[] { return patterns; } + +export const findObjectIndexByProperty = (array: any, key: string, value: any): number => { + for (let i=0; i { + return fileName.slice(0, -extn.length); +} \ No newline at end of file diff --git a/src/common/TreeStructure/CovertIItemToJson.ts b/src/common/TreeStructure/CovertIItemToJson.ts new file mode 100644 index 00000000..3ca0fb9b --- /dev/null +++ b/src/common/TreeStructure/CovertIItemToJson.ts @@ -0,0 +1,34 @@ +import { IItem } from './TreeView/Types/Entity/IItem'; + +const componentTypeMap: { [key: string]: string } = { + '08': 'web-temmplates', + '07': 'content-snippets', + '015': 'basic-forms', + '017': 'lists', + '019': 'advanced-forms' +}; + +export function generateJsonFromIItem(item: IItem): any { + const result: any = { + label: item.label + }; + + if (item.children && item.children.length > 0) { + result.children = item.children.map(child => { + const childResult = generateJsonFromIItem(child); + if (item.label === 'References') { + childResult['type'] = componentTypeMap[child.component]; + } + return childResult; + }); + } + + if (item.parentList && item.parentList.length > 0) { + result.parentList = item.parentList.map(parent => ({ + type: parent.title, + label: parent.label, + })); + } + + return result; +} \ No newline at end of file diff --git a/src/common/TreeStructure/DataMapper.ts b/src/common/TreeStructure/DataMapper.ts new file mode 100644 index 00000000..2abf7d42 --- /dev/null +++ b/src/common/TreeStructure/DataMapper.ts @@ -0,0 +1,825 @@ +/* + * 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 { IPreviewEngineContext } from './TreeView/Utils/IDataResolver'; +import { Webpage } from './TreeView/Types/Entity/WebPage'; +import { Website } from './TreeView/Types/Entity/Website'; +import { WebTemplate } from './TreeView/Types/Entity/WebTemplate'; +import { PageTemplate } from './TreeView/Types/Entity/PageTemplate'; +import { WebFile } from "./TreeView/Types/Entity/WebFile"; +import { ContentSnippet } from "./TreeView/Types/Entity/ContentSnippet"; +import { EntityForm } from "./TreeView/Types/Entity/EntityForm"; +import { EntityList } from "./TreeView/Types/Entity/EntityList"; +import { WebForm } from "./TreeView/Types/Entity/WebForm"; +import { SiteMarker } from "./TreeView/Types/Entity/SiteMarker"; +import { SiteSetting } from "./TreeView/Types/Entity/SiteSettings"; +import { Weblink } from "./TreeView/Types/Entity/Weblink"; +import { WeblinkSet } from "./TreeView/Types/Entity/WeblinkSet"; +import { PortalWebView } from '../../client/PortalWebView'; +import { BootstrapSiteSetting, ContextProperty } from './TreeView/Utils/Constant'; +import { createTree } from './TreeViewProvider'; +import { IItem } from './TreeView/Types/Entity/IItem'; +import { getDependencies } from './DataParser'; +import { PortalComponentServiceFactory } from "./dataMapperServices/PortalComponentServiceFactory"; +import { oneDSLoggerWrapper } from "../../common/OneDSLoggerTelemetry/oneDSLoggerWrapper"; +import { MyReferenceProvider } from "./MyReferenceProvider"; +import { generateJsonFromIItem } from './CovertIItemToJson'; +import { contentPage } from './dataMapperServices/WebPageService'; + +const fs = require('fs'); +const yaml = require("js-yaml"); +const load = yaml.load; +const fallbackURI = vscode.Uri.file(''); + +export let globalWebsiteIItem: IItem; +export let globalwebPageIItem: IItem; + +export const treeView = async () => { + const previewHelper = new PreviewHelper(); + try { + await previewHelper.createContext(); + const getPath = await previewHelper.getPath(); + const IPortalMetadataContext = await previewHelper.getPreviewHelper(); + console.log(IPortalMetadataContext); + const web = await previewHelper.web(); + + oneDSLoggerWrapper.getLogger().traceInfo("End of IPortalMetadata creation", { + "timeNow": performance.now() + }); + + const { allwebTemplate, allwebPage, allwebFile, allcontentSnippet, alllist, allentityForm, allwebForm } = convertAllMetadataToItems(IPortalMetadataContext, getPath); + const websiteIItem = await createWebsiteItem(previewHelper); + const { webtemplateIItem, webPageIItem, webFileIItem, contentSnippetIItem, listIItem, entityFormtIItem, webFormIItem, unUsedFileIItem, webIItem } = createIndividualItems(allwebTemplate, allwebPage, allwebFile, allcontentSnippet, alllist, allentityForm, allwebForm); + addWebfileToWebPage(IPortalMetadataContext, allwebPage, allwebFile); + addPageTemplate(IPortalMetadataContext,contentPage, allwebPage, allwebTemplate); + + const entityFileMap: Map> = new Map(); + + addDependencies(webtemplateIItem, webPageIItem, webFileIItem, contentSnippetIItem, listIItem, entityFormtIItem, webFormIItem, entityFileMap); + addUnUsedFiles(unUsedFileIItem, entityFileMap, webtemplateIItem, contentSnippetIItem, listIItem, entityFormtIItem, webFormIItem); + removeusedOne(unUsedFileIItem, IPortalMetadataContext); + + globalwebPageIItem = webIItem; + webPageIItem.children = webPageIItem.children.filter(item => item.label === "Home"); + (websiteIItem.children as IItem[]).push(webtemplateIItem, webPageIItem, webFileIItem, contentSnippetIItem, listIItem, entityFormtIItem, webFormIItem, unUsedFileIItem); + + console.log(websiteIItem); + globalWebsiteIItem = websiteIItem; + + const languages = ['html', 'css', 'javascript', 'json', 'yaml']; + languages.forEach(language => { + vscode.languages.registerReferenceProvider( + { scheme: 'file', language }, + new MyReferenceProvider() + ); + }); + + createTree(websiteIItem); + const jsonObject = generateJsonFromIItem(websiteIItem); + console.log(JSON.stringify(jsonObject, null, 2)); + + oneDSLoggerWrapper.getLogger().traceInfo("End of tree view creation", { + "timeNow": performance.now() + }); + + } catch (error) { + console.error('Error:', error); + } +}; +vscode.workspace.onDidSaveTextDocument(async (document) => { + await treeView(); // Refresh treeView on document save +}); + +function convertAllMetadataToItems(IPortalMetadataContext: any, getPath: any) { + const allwebTemplate = PortalComponentServiceFactory.getPortalComponent("WebTemplate")?.create(IPortalMetadataContext, getPath) || []; + const allwebPage = PortalComponentServiceFactory.getPortalComponent("WebPage")?.create(IPortalMetadataContext, getPath) || []; + const allwebFile = PortalComponentServiceFactory.getPortalComponent("WebFile")?.create(IPortalMetadataContext, getPath) || []; + const allcontentSnippet = PortalComponentServiceFactory.getPortalComponent("Content Snippet")?.create(IPortalMetadataContext, getPath) || []; + const alllist = PortalComponentServiceFactory.getPortalComponent("List")?.create(IPortalMetadataContext, getPath) || []; + const allentityForm = PortalComponentServiceFactory.getPortalComponent("EntityForm")?.create(IPortalMetadataContext, getPath) || []; + const allwebForm = PortalComponentServiceFactory.getPortalComponent("WebForm")?.create(IPortalMetadataContext, getPath) || []; + return { allwebTemplate, allwebPage, allwebFile, allcontentSnippet, alllist, allentityForm, allwebForm }; +} + +async function createWebsiteItem(previewHelper: PreviewHelper) { + const web = await previewHelper.web(); + return { + label: web.adx_name ?? 'Unnamed Website', + title: web.adx_name ?? 'Unnamed Website', + id: web.adx_websiteid, + isFile: false, + content: "", + path: vscode.Uri.parse(`/${web.adx_name}`), + component: "1", + children: [], + error: "", + parentList: [] + }; +} + +function createIndividualItems(allwebTemplate: IItem[], allwebPage: IItem[], allwebFile: IItem[], allcontentSnippet: IItem[], alllist: IItem[], allentityForm: IItem[], allwebForm: IItem[]) { + const webtemplateIItem = { + label: 'Web Templates', + title: 'Web Templates', + id: '', + isFile: false, + content: "", + path: vscode.Uri.parse(`/WebTemplate`), + component: "8", + children: allwebTemplate, + error: "", + parentList: [] + }; + + const webPageIItem = { + label: 'Web Pages', + title: 'Web Pages', + id: '', + isFile: false, + content: "", + path: vscode.Uri.parse(`/WebPage`), + component: "2", + children: allwebPage, + error: "", + parentList: [] + }; + + const webFileIItem = { + label: 'Web Files', + title: 'Web Files', + id: '', + isFile: false, + content: "", + path: vscode.Uri.parse(`/webFile`), + component: "3", + children: allwebFile, + error: "", + parentList: [] + }; + const contentSnippetIItem = { + label: 'Content Snippets', + title: 'Content Snippets', + id: '', + isFile: false, + content: "", + path: vscode.Uri.parse(`/contentSnippet`), + component: "7", + children: allcontentSnippet, + error: "", + parentList: [] + }; + const listIItem = { + label: 'Lists', + title: 'Lists', + id: '', + isFile: false, + content: "", + path: vscode.Uri.parse(`/lists`), + component: "17", + children: alllist, + error: "", + parentList: [] + }; + + const entityFormtIItem = { + label: 'Basic Forms', + title: 'Basic Forms', + id: '', + isFile: false, + content: "", + path: vscode.Uri.parse(`/basic-forms`), + component: "15", + children: allentityForm, + error: "", + parentList: [] + }; + + const webFormIItem = { + label: 'Advanced Forms', + title: 'Advanced Forms', + id: '', + isFile: false, + content: "", + path: vscode.Uri.parse(`/advanced-forms`), + component: "19", + children: allwebForm, + error: "", + parentList: [] + }; + const unUsedFileIItem = { + label: 'Unused Components', + title: 'Unused Components', + id: '', + isFile: false, + content: "", + path: vscode.Uri.parse(`/unused-files`), + component: "20", + children: [], + error: "", + parentList: [] + }; + const webIItem = { + label: 'Web Pages', + title: 'Web Pages', + id: '', + isFile: false, + content: "", + path: vscode.Uri.parse(`/WebPage`), + component: "2", + children: allwebPage, + error: "", + parentList: [] + }; + + return { webtemplateIItem, webPageIItem, webFileIItem, contentSnippetIItem, listIItem, entityFormtIItem, webFormIItem, unUsedFileIItem, webIItem }; +} + +function addWebfileToWebPage(metadataContext: IPreviewEngineContext, webPageIItem: IItem[], webFileIItem: IItem[]) { + const items: IItem[] = []; + const webfile: WebFile[] | undefined = metadataContext.webFiles; + + if (!webfile) { + return items; + } + webfile.forEach(file => { + webPageIItem.forEach(item => { + if (file.adx_parentpageid == item.id) { + webFileIItem.forEach(it => { + if (it.id == file.adx_webfileid) { + let webfileItem = item.children.find(child => child.label === "Web File"); + if (!webfileItem) { + let webFile = createItem('Web File', 'Web File', '', false, vscode.Uri.parse(`/webFile`), "3", [it]); + item.children.push(webFile); + } else { + webfileItem.children.push(it); + } + } + }) + } + }); + }); +} + +function createItem(label: string, title: string, id: string, isFile: boolean, path: vscode.Uri, component: string, children: IItem[] = [], content: string = '', error: string = '', parentList: IItem[] = []): IItem { + return { + label, + title, + id, + isFile, + content, + path, + component, + children, + error, + parentList, + }; +} + +function addPageTemplate(IPortalMetadataContext: any, contentPage: Webpage[], webPageIItem: IItem[], webTemplateIITem: IItem[]) { + const pageTemplate: PageTemplate[] | undefined = IPortalMetadataContext.pageTemplates; + const webpages: Webpage[] | undefined = IPortalMetadataContext.webpages; + pageTemplate?.forEach(pg => { + const pgid = pg.adx_pagetemplateid; + webpages?.forEach(webpage => { + const wbpgid = webpage.adx_pagetemplateid; + const webPageId=webpage.adx_webpageid; + if (wbpgid == pgid) { + const webPage = webPageIItem.find(item => webPageId === item.id); + const tempid = pg.adx_webtemplateid; + if (tempid) { + const te = webTemplateIITem.find(it => tempid === it.id); + if (te) { + const item = createItem(`${te.label}`, `Source-Dependencies`, `${te.id}`, true, vscode.Uri.parse(`/inside treeItem`), '08', [], '', ''); + const pageTemplateAlreadyExists = webPage?.children.find(it => it.label === 'Page Template'); + if (!pageTemplateAlreadyExists) { + let pT = createItem('Page Template', 'Page Template', '', false, vscode.Uri.parse(`/pageTemplate`), "6", []); + pT.children.push(item); + webPage?.children.push(pT); + } else { + pageTemplateAlreadyExists.children.push(item); + webPage?.children.push(pageTemplateAlreadyExists); + } + } + } + } + }) + contentPage?.forEach(webpage => { + const wbpgid = webpage.adx_pagetemplateid; + const webPageId=webpage.adx_webpageid; + if (wbpgid == pgid) { + const webPage = webPageIItem.find(item => webPageId === item.id); + const tempid = pg.adx_webtemplateid; + if (tempid) { + const te = webTemplateIITem.find(it => tempid === it.id); + if (te) { + const item = createItem(`${te.label}`, `Source-Dependencies`, `${te.id}`, true, vscode.Uri.parse(`/inside treeItem`), '08', [], '', ''); + const pageTemplateAlreadyExists = webPage?.children.find(it => it.label === 'Page Template'); + if (!pageTemplateAlreadyExists) { + let pT = createItem('Page Template', 'Page Template', '', false, vscode.Uri.parse(`/pageTemplate`), "6", []); + pT.children.push(item); + webPage?.children.push(pT); + } else { + pageTemplateAlreadyExists.children.push(item); + webPage?.children.push(pageTemplateAlreadyExists); + } + } + } + } + }) + }) + +} + + +function addDependencies(webtemplateIItem: IItem, webPageIItem: IItem, webFileIItem: IItem, contentSnippetIItem: IItem, listIItem: IItem, entityFormtIItem: IItem, webFormIItem: IItem, entityFileMap: Map>): any { + addDependenciesToIItem(webtemplateIItem, contentSnippetIItem, webtemplateIItem, entityFormtIItem, listIItem, webFormIItem, entityFileMap); + addDependenciesToWebPage(webPageIItem, contentSnippetIItem, webtemplateIItem, entityFormtIItem, listIItem, webFormIItem, entityFileMap); + addDependenciesToIItem(contentSnippetIItem, contentSnippetIItem, webtemplateIItem, entityFormtIItem, listIItem, webFormIItem, entityFileMap); + addDependenciesToIItem(listIItem, contentSnippetIItem, webtemplateIItem, entityFormtIItem, listIItem, webFormIItem, entityFileMap); + addDependenciesToIItem(entityFormtIItem, contentSnippetIItem, webtemplateIItem, entityFormtIItem, listIItem, webFormIItem, entityFileMap); + addDependenciesToIItem(webFileIItem, contentSnippetIItem, webtemplateIItem, entityFormtIItem, listIItem, webFormIItem, entityFileMap); +} + +function addDependenciesToIItem(entityIItem: IItem, contentSnippetIItem: IItem, webtemplateIItem: IItem, entityFormtIItem: IItem, listIItem: IItem, webFormIItem: IItem, entityFileMap: Map>): any { + entityIItem.children.forEach((item: IItem) => { + if (!item.isFile) { + let sourceDep = item.children.find((child: IItem) => child.isFile === false); + if (!sourceDep) { + sourceDep = createItem(`References`, `References`, '', false, vscode.Uri.parse(''), "21", [], ""); + } + const htOrJsFile = item.children.find((child: IItem) => child.isFile === true); + const filePath = htOrJsFile?.path?.fsPath; + if (filePath && sourceDep) { + processDependencies(sourceDep, item, filePath, contentSnippetIItem, webtemplateIItem, entityFormtIItem, listIItem, webFormIItem, entityFileMap); + } else { + console.log('No valid file path found for:', item); + } + if (sourceDep.children.length != 0) { + item.children.push(sourceDep); + } + } + }); +} + +function addDependenciesToWebPage(webPageIItem: IItem, contentSnippetIItem: IItem, webtemplateIItem: IItem, entityFormtIItem: IItem, listIItem: IItem, webFormIItem: IItem, entityFileMap: Map>): any { + webPageIItem.children.forEach((item: IItem) => { + const pgcy = item.children.find(child => child.label === "Page Copy"); + const pgsy = item.children.find(child => child.label === "Page Summary"); + const cp = item.children.find(child => child.label === "Content Page") + const cppgcy = cp?.children.find(child => child.label === "Page Copy"); + const cppgsy = cp?.children.find(child => child.label === "Page Summary"); + const cpjsfile = cp?.children.find((child: IItem) => child.label.endsWith('.js')); + const jsfile = item.children.find((child: IItem) => child.label.endsWith('.js')); + + handleChildItem(pgcy, contentSnippetIItem, webtemplateIItem, entityFormtIItem, listIItem, webFormIItem, entityFileMap, jsfile); + handleChildItem(pgsy, contentSnippetIItem, webtemplateIItem, entityFormtIItem, listIItem, webFormIItem, entityFileMap, jsfile); + handleChildItem(cppgcy, contentSnippetIItem, webtemplateIItem, entityFormtIItem, listIItem, webFormIItem, entityFileMap, cpjsfile); + handleChildItem(cppgsy, contentSnippetIItem, webtemplateIItem, entityFormtIItem, listIItem, webFormIItem, entityFileMap, cpjsfile); + }); +} + +function handleChildItem(child: IItem | undefined, contentSnippetIItem: IItem, webtemplateIItem: IItem, entityFormtIItem: IItem, listIItem: IItem, webFormIItem: IItem, entityFileMap: Map>, jsfile?: IItem) { + if (child) { + const ht = child.children.find((child: IItem) => child.isFile === true); + const filePath = ht?.path?.fsPath; + const jsFilePath = jsfile?.path?.fsPath; + let sourceDep = child.children.find((child: IItem) => child.isFile === false); + + if (!sourceDep) { + sourceDep = createItem("References", "References", '', false, vscode.Uri.parse(''), "21", [], ""); + } + if (filePath && sourceDep) { + processDependencies(sourceDep, child, filePath, contentSnippetIItem, webtemplateIItem, entityFormtIItem, listIItem, webFormIItem, entityFileMap, jsFilePath); + } else { + console.log('No valid file path or sourceDep found for:', child); + } + if (sourceDep.children.length != 0) { + child.children.push(sourceDep); + } + } +} + +function processDependencies(sourceDep: IItem, parent: IItem, filePath: string, contentSnippetIItem: IItem, webtemplateIItem: IItem, entityFormtIItem: IItem, listIItem: IItem, webFormIItem: IItem, entityFileMap: Map>, jsFilePath?: string) { + const data = fs.readFileSync(filePath, 'utf8'); + const dependencies = getDependencies(data); + if (jsFilePath) { + const jsdata = fs.readFileSync(jsFilePath, 'utf8'); + const jsdependencies = getDependencies(jsdata) + dependencies.push(...jsdependencies); + } + dependencies.forEach((entity: any) => { + const tagName = entity.tagName.replace(/^['"](.*)['"]$/, '$1'); + const property = entity.property.replace(/^['"](.*)['"]$/, '$1'); + const fileNameOrID = entity.fileNameOrID.replace(/^['"](.*)['"]$/, '$1'); + + if (tagName === "snippets" || tagName === "snippet" || (tagName === 'editable' && (property === "snippets" || property === "snippet"))) { + processEntity(sourceDep, parent, contentSnippetIItem, entity, 'label', entityFileMap, '07'); + } else if (tagName === "Web Template") { + processEntity(sourceDep, parent, webtemplateIItem, entity, 'label', entityFileMap, '08'); + } else if (tagName === "entityform" || tagName === "entity_form") { + if (property == 'id' && isNaN(fileNameOrID)) { + processEntity(sourceDep, parent, entityFormtIItem, entity, 'id', entityFileMap, '015'); + } else if (property == 'name' || property == 'key') { + processEntity(sourceDep, parent, entityFormtIItem, entity, 'label', entityFileMap, '015') + } else { + console.log("Not Valid EnitityForm"); + } + } else if (tagName === "entitylist" || tagName === "entity_list") { + if (property == 'id' && isNaN(fileNameOrID)) { + processEntity(sourceDep, parent, listIItem, entity, 'id', entityFileMap, '017'); + } else if (property == 'name' || property == 'key') { + processEntity(sourceDep, parent, listIItem, entity, 'label', entityFileMap, '017') + } else { + console.log("Not Valid EntityList"); + } + } else if (tagName === "webform") { + if (property == 'id' && isNaN(fileNameOrID)) { + processEntity(sourceDep, parent, webFormIItem, entity, 'id', entityFileMap, '019'); + } else if (property == 'name' || property == 'key') { + processEntity(sourceDep, parent, webFormIItem, entity, 'label', entityFileMap, '019') + } else { + console.log("Not Valid WebForm"); + } + } else if ((tagName != "entityform" && tagName != "entity_form") && (tagName != "entitylist" && tagName != "entity_list") && tagName !== "editable") { + entity.fileNameOrID = tagName; + entity.tagName = 'Web Template' + processEntity(sourceDep, parent, webtemplateIItem, entity, 'label', entityFileMap, '08'); + } else { + console.log("Another Dynamic entity"); + } + }); +} + +function processEntity(sourceDep: IItem, parent: IItem, targetIItem: IItem, entity: any, compareBy: string, entityFileMap: Map>, comp: string) { + let exist = false; + const fileNameOrID = entity.fileNameOrID.replace(/^['"](.*)['"]$/, '$1'); + targetIItem.children.forEach((targetItem: IItem) => { + const compareValue = compareBy === 'label' ? targetItem.label : targetItem.id; + if (compareValue === fileNameOrID) { + exist = true; + let fileNameAlready = sourceDep.children.find(child => (child.label === targetItem.label && child.component.slice(1) === targetItem.component.slice(1))); + let ht = targetItem.children.find((child: IItem) => child.isFile === true); + let htLabel = ht?.label ?? ''; + let parentAlreadyPresent = targetItem.parentList.find(child => (child.label === parent.label && child.component === parent.component)); + if (!parentAlreadyPresent) { + targetItem.parentList.push(parent); + } + const item = createItem(`${targetItem.label}`, `Source-Dependencies`, `${targetItem.id}`, true, vscode.Uri.parse(`/inside treeItem`), comp, [], '', ''); + if (!entityFileMap.has(targetIItem.label)) { + entityFileMap.set(targetIItem.label, new Set()); + } + entityFileMap.get(targetIItem.label)?.add(htLabel); + if (!fileNameAlready) { + sourceDep.children.push(item); + } + } + }); + if (exist == false) { + const item = createItem(`${fileNameOrID} Not Used`, `${fileNameOrID}`, `${entity.tagName}`, true, vscode.Uri.parse(``), comp, [], '', ''); + let fileNameAlready = sourceDep.children.find(child => child.label === `${fileNameOrID} Not Used`); + if (!fileNameAlready) { + sourceDep.children.push(item); + } + } +} + + +function addUnUsedFiles(unUsedFileIItem: IItem, entityFileMap: Map>, webtemplateIItem: IItem, contentSnippetIItem: IItem, listIItem: IItem, entityFormtIItem: IItem, webFormIItem: IItem) { + addUnusedFilesHelper('Web Templates', unUsedFileIItem, entityFileMap, webtemplateIItem, '/WebTemplates', "8"); + addUnusedFilesHelper('Content Snippets', unUsedFileIItem, entityFileMap, contentSnippetIItem, '/contentSnippets', "7"); + addUnusedFilesHelper('Lists', unUsedFileIItem, entityFileMap, listIItem, '/lists', "17"); + addUnusedFilesHelper('Basic Forms', unUsedFileIItem, entityFileMap, entityFormtIItem, '/basic-forms', "15"); + addUnusedFilesHelper('Advanced Forms', unUsedFileIItem, entityFileMap, webFormIItem, '/advanced-forms', "19"); +} + +function addUnusedFilesHelper(entityType: string, unusedFileIItem: IItem, entityFileMap: Map>, entityIItem: IItem, folderPath: string, order: string) { + const usedEntityFiles = entityFileMap.get(entityType); + entityIItem.children.forEach((item: IItem) => { + const file = item.children.find((child: IItem) => child.isFile === true); + if (file && !usedEntityFiles?.has(file.label)) { + let entityPresent = unusedFileIItem.children.find(child => child.label === entityType); + if (entityPresent) { + entityPresent.children.push(file); + } else { + entityPresent = createItem(entityType, entityType, '', false, vscode.Uri.parse(folderPath), order, []); + entityPresent.children.push(file); + unusedFileIItem.children.push(entityPresent); + } + } + }); +} + +function removeusedOne(unUsedFileIItem: IItem, metadataContext: IPreviewEngineContext) { + const pageTemplate = metadataContext.pageTemplates; + const webTemplateItem = unUsedFileIItem.children.find((item: IItem) => item.label === 'Web Template'); + pageTemplate?.forEach(pageTemp => { + if (pageTemp.adx_webtemplateid && webTemplateItem) { + webTemplateItem.children = webTemplateItem.children.filter((item: IItem) => item.id !== pageTemp.adx_webtemplateid); + } + }) + const website = metadataContext.website; + if (website?.adx_footerwebtemplateid && webTemplateItem) { + webTemplateItem.children = webTemplateItem.children.filter((item: IItem) => item.id !== website.adx_footerwebtemplateid); + } + if (website?.adx_headerwebtemplateid && webTemplateItem) { + webTemplateItem.children = webTemplateItem.children.filter((item: IItem) => item.id !== website.adx_headerwebtemplateid); + } +} + +export class PreviewHelper { + private pathroot: vscode.Uri | null; + private previewHelper: IPreviewEngineContext; + private websiteData: Website; + private isBootstrapV5: boolean; + + + constructor() { + this.isBootstrapV5 = false; + this.previewHelper = {}; + // this.pathroot = PortalWebView.getPortalRootFolder(); + this.pathroot = vscode.Uri.file('C:/Users/t-mansisingh/OneDrive - Microsoft/Desktop/clone2/mansi-site-1---site-ajx90'); + this.websiteData = {} as Website; + } + + public createContext = async () => { + this.websiteData = await this.getWebsite(); + this.previewHelper = await this.createEngineContext(); + } + public getPath = async () => { + return this.pathroot; + } + + public web = async () => { + return this.websiteData; + } + + public getPreviewHelper = () => { + return this.previewHelper; + } + + private getWebsite = async (): Promise => { + const website = await vscode.workspace.fs.readFile(this.pathroot?.with({ path: this.pathroot.path + '/website.yml' }) || fallbackURI); + const websiteYaml = load(new TextDecoder().decode(website)); + return websiteYaml as Website; + } + + private createEngineContext = async (): Promise => { + if (this.pathroot) { + return { + webpages: await this.getWebpages(), + pageTemplates: await this.getPageTemplates(), + webTemplates: await this.getWebTemplates(), + webFiles: await this.getWebFiles(), + contentSnippets: await this.getContentSnippets(), + entityLists: await this.getEntityLists(), + entityForms: await this.getEntityForms(), + webForms: await this.getWebForms(), + siteMarkers: await this.getSiteMarker(), + siteSettings: await this.getSiteSetting(), + weblinks: await this.getWeblinks(), + weblinkSets: await this.getWeblinkSets(), + website: this.websiteData, + isBootstrapV5: this.isBootstrapV5, + } + } else return {} + } + + private getWebpages = async (): Promise => { + const webpagesDir = await vscode.workspace.fs.readDirectory(this.pathroot?.with({ path: this.pathroot.path + '/web-pages' }) || fallbackURI); + const webpageArray: Webpage[] = []; + + for (const webpage of webpagesDir) { + webpageArray.push(await this.webPageHelper(this.pathroot?.with({ path: this.pathroot.path + '/web-pages/' + webpage[0] + '/' + webpage[0] }) || fallbackURI)); + + const contentPageDir = await vscode.workspace.fs.readDirectory(this.pathroot?.with({ path: this.pathroot.path + '/web-pages/' + webpage[0] + '/content-pages' }) || fallbackURI); + for (const page of contentPageDir) { + if (page[0].endsWith(ContextProperty.WEBPAGE_YAML)) { + const pageName = page[0].slice(0, -12); + webpageArray.push(await this.webPageHelper(this.pathroot?.with({ path: this.pathroot.path + '/web-pages/' + webpage[0] + '/content-pages/' + pageName }) || fallbackURI)); + } + } + } + return webpageArray; + } + + private webPageHelper = async (pageUri: vscode.Uri): Promise => { + const webpageYaml = await vscode.workspace.fs.readFile(pageUri?.with({ path: pageUri.path + '.webpage.yml' })); + const webpageJS = await vscode.workspace.fs.readFile(pageUri?.with({ path: pageUri.path + '.webpage.custom_javascript.js' })); + const webpageCSS = await vscode.workspace.fs.readFile(pageUri?.with({ path: pageUri.path + '.webpage.custom_css.css' })); + const webpageCopy = await vscode.workspace.fs.readFile(pageUri?.with({ path: pageUri.path + '.webpage.copy.html' })); + const webpageSummary = await vscode.workspace.fs.readFile(pageUri?.with({ path: pageUri.path + '.webpage.summary.html' })); + const webpageRecord = load(new TextDecoder().decode(webpageYaml)) as Webpage; + webpageRecord.adx_customcss = new TextDecoder().decode(webpageCSS); + webpageRecord.adx_customjavascript = new TextDecoder().decode(webpageJS); + webpageRecord.adx_copy = new TextDecoder().decode(webpageCopy); + webpageRecord.adx_summary = new TextDecoder().decode(webpageSummary); + webpageRecord.adx_websiteid = this.websiteData.adx_websiteid; + return webpageRecord; + } + + private getPageTemplates = async (): Promise => { + const pageTemplatesDirectory = await vscode.workspace.fs.readDirectory(this.pathroot?.with({ path: this.pathroot.path + '/page-templates' }) || fallbackURI); + const pageTemplatesArray: PageTemplate[] = []; + + for (const pageTemplate of pageTemplatesDirectory) { + pageTemplatesArray.push(await this.pageTemplateHelper(this.pathroot?.with({ path: this.pathroot.path + '/page-templates/' + pageTemplate[0] }) || fallbackURI)); + } + return pageTemplatesArray; + } + + private pageTemplateHelper = async (fileUri: vscode.Uri): Promise => { + const pageTemplateYaml = await vscode.workspace.fs.readFile(fileUri); + const pageTemplateRecord = load(new TextDecoder().decode(pageTemplateYaml)) as PageTemplate; + return pageTemplateRecord; + }; + + + private webTemplateHelper = async (fileUri: vscode.Uri): Promise => { + const webTemplateYaml = await vscode.workspace.fs.readFile(fileUri?.with({ path: fileUri.path + '.webtemplate.yml' })); + const webTemplateSource = await vscode.workspace.fs.readFile(fileUri?.with({ path: fileUri.path + '.webtemplate.source.html' })); + const webTemplateSourceHTML = new TextDecoder().decode(webTemplateSource); + const webTemplateRecord = load(new TextDecoder().decode(webTemplateYaml)) as WebTemplate; + webTemplateRecord.adx_source = webTemplateSourceHTML; + webTemplateRecord.adx_websiteid = this.websiteData.adx_websiteid; + return webTemplateRecord; + }; + + private getWebTemplates = async (): Promise => { + const webTemplatesDirectory = await vscode.workspace.fs.readDirectory(this.pathroot?.with({ path: this.pathroot.path + '/web-templates' }) || fallbackURI); + + const webTemplatesArray: WebTemplate[] = []; + for (const webTemplate of webTemplatesDirectory) { + webTemplatesArray.push(await this.webTemplateHelper(this.pathroot?.with({ path: this.pathroot.path + '/web-templates/' + webTemplate[0] + `/${webTemplate[0]}` }) || fallbackURI)); + } + return webTemplatesArray; + } + + private webFileHelper = async (fileUri: vscode.Uri): Promise => { + const webFileYaml = await vscode.workspace.fs.readFile(fileUri); + const webFileRecord = load(new TextDecoder().decode(webFileYaml)) as WebFile; + return webFileRecord; + }; + + private getWebFiles = async (): Promise => { + const webFilesDirectory = await vscode.workspace.fs.readDirectory(this.pathroot?.with({ path: this.pathroot.path + '/web-files' }) || fallbackURI); + const webFilesArray: WebFile[] = []; + for (const webFile of webFilesDirectory) { + if (webFile[0].endsWith("webfile.yml")) { + webFilesArray.push(await this.webFileHelper(this.pathroot?.with({ path: this.pathroot.path + '/web-files/' + `/${webFile[0]}` }) || fallbackURI)); + } + } + return webFilesArray; + } + + private contentSnippetHelper = async (fileUri: vscode.Uri): Promise => { + const snippetYaml = await vscode.workspace.fs.readFile(fileUri?.with({ path: fileUri.path + '.contentsnippet.yml' })); + const snippetValue = await vscode.workspace.fs.readFile(fileUri?.with({ path: fileUri.path + '.contentsnippet.value.html' })); + const snippetRecord = load(new TextDecoder().decode(snippetYaml)) as ContentSnippet + snippetRecord.adx_value = new TextDecoder().decode(snippetValue); + snippetRecord.adx_websiteid = this.websiteData.adx_websiteid; + snippetRecord.stateCode = 0; + return snippetRecord; + }; + + private getContentSnippets = async (): Promise => { + const contentSnippetsDirectory = await vscode.workspace.fs.readDirectory(this.pathroot?.with({ path: this.pathroot.path + '/content-snippets' }) || fallbackURI); + const contentSnippetsArray: ContentSnippet[] = []; + for (const contentSnippet of contentSnippetsDirectory) { + const snippetSubDirectory = await vscode.workspace.fs.readDirectory(this.pathroot?.with({ path: this.pathroot.path + '/content-snippets/' + contentSnippet[0] }) || fallbackURI); + for (const snippet of snippetSubDirectory) { + if (snippet[0].endsWith(ContextProperty.CONTENT_SNIPPET_YAML)) { + contentSnippetsArray.push(await this.contentSnippetHelper(this.pathroot?.with({ path: this.pathroot.path + '/content-snippets/' + contentSnippet[0] + `/${snippet[0].slice(0, -19)}` }) || fallbackURI)); + } + } + } + return contentSnippetsArray; + } + + private entityFormHelper = async (fileUri: vscode.Uri): Promise => { + const entityFormYaml = await vscode.workspace.fs.readFile(fileUri); + const entityFormRecord = load(new TextDecoder().decode(entityFormYaml)) as EntityForm; + entityFormRecord.adx_websiteid = this.websiteData.adx_websiteid; + return entityFormRecord; + }; + + private getEntityForms = async (): Promise => { + const entityFormsDirectory = await vscode.workspace.fs.readDirectory(this.pathroot?.with({ path: this.pathroot.path + '/basic-forms' }) || fallbackURI); + + const entityFormsArray: EntityForm[] = []; + for (const entityForm of entityFormsDirectory) { + entityFormsArray.push(await this.entityFormHelper(this.pathroot?.with({ path: this.pathroot.path + '/basic-forms/' + entityForm[0] + `/${entityForm[0]}.basicform.yml` }) || fallbackURI)); + } + return entityFormsArray; + } + + private entityListHelper = async (fileUri: vscode.Uri): Promise => { + const entityListYaml = await vscode.workspace.fs.readFile(fileUri); + const entityListRecord = load(new TextDecoder().decode(entityListYaml)) as EntityList; + entityListRecord.adx_websiteid = this.websiteData.adx_websiteid; + return entityListRecord; + }; + + private getEntityLists = async (): Promise => { + const entityListsDirectory = await vscode.workspace.fs.readDirectory(this.pathroot?.with({ path: this.pathroot.path + '/lists' }) || fallbackURI); + + const entityListsArray: EntityList[] = []; + for (const entityList of entityListsDirectory) { + if (entityList[0].endsWith(ContextProperty.ENTITY_LIST)) { + entityListsArray.push(await this.entityListHelper(this.pathroot?.with({ path: this.pathroot.path + '/lists/' + entityList[0] }) || fallbackURI)); + } + } + return entityListsArray; + } + + private webFormHelper = async (fileUri: vscode.Uri): Promise => { + const webFormYaml = await vscode.workspace.fs.readFile(fileUri); + const webFormRecord = load(new TextDecoder().decode(webFormYaml)) as WebForm; + webFormRecord.adx_websiteid = this.websiteData.adx_websiteid; + return webFormRecord; + }; + + + private getWebForms = async (): Promise => { + const webFormsDirectory = await vscode.workspace.fs.readDirectory(this.pathroot?.with({ path: this.pathroot.path + '/advanced-forms' }) || fallbackURI); + + const webFormsArray: WebForm[] = []; + for (const webForm of webFormsDirectory) { + webFormsArray.push(await this.webFormHelper(this.pathroot?.with({ path: this.pathroot.path + '/advanced-forms/' + webForm[0] + `/${webForm[0]}.advancedform.yml` }) || fallbackURI)); + } + return webFormsArray; + } + + private getSiteSetting = async (): Promise => { + const siteSetting = await vscode.workspace.fs.readFile(this.pathroot?.with({ path: this.pathroot.path + '/sitesetting.yml' }) || fallbackURI); + const siteSettingYaml = load(new TextDecoder().decode(siteSetting)) as SiteSetting[]; + const siteSettingRecords = siteSettingYaml.map((siteSettingRecord) => { + if (siteSettingRecord.adx_name === BootstrapSiteSetting) { + this.isBootstrapV5 = siteSettingRecord.adx_value ? String(siteSettingRecord.adx_value).toLowerCase() === 'true' : false; + } + return { + adx_websiteid: this.websiteData.adx_websiteid, + adx_name: siteSettingRecord.adx_name, + adx_value: siteSettingRecord.adx_value, + adx_sitesettingid: siteSettingRecord.adx_sitesettingid, + } + }); + return siteSettingRecords; + } + + private getSiteMarker = async (): Promise => { + const siteMarker = await vscode.workspace.fs.readFile(this.pathroot?.with({ path: this.pathroot.path + '/sitemarker.yml' }) || fallbackURI); + const siteMarkerYaml = load(new TextDecoder().decode(siteMarker)) as SiteMarker[]; + const siteMarkerRecords = siteMarkerYaml.map((siteMarkerRecord) => { + return { + adx_name: siteMarkerRecord.adx_name as string, + adx_pageid: siteMarkerRecord.adx_pageid as string, + adx_sitemarkerid: siteMarkerRecord.adx_sitemarkerid, + adx_websiteid: this.websiteData.adx_websiteid, + + } + + }); + return siteMarkerRecords; + } + + private getWeblinks = async (): Promise => { + const weblinksDirectory = await vscode.workspace.fs.readDirectory(this.pathroot?.with({ path: this.pathroot.path + '/weblink-sets' }) || fallbackURI); + + const weblinksArray: Weblink[] = []; + for (const link of weblinksDirectory) { + const linkSubDirectory = await vscode.workspace.fs.readDirectory(this.pathroot?.with({ path: this.pathroot.path + '/weblink-sets/' + link[0] }) || fallbackURI); + for (const sublink of linkSubDirectory) { + if (sublink[0].endsWith(ContextProperty.WEB_LINK)) { + const weblinkYaml = await vscode.workspace.fs.readFile(this.pathroot?.with({ path: this.pathroot.path + '/weblink-sets/' + link[0] + `/${sublink[0]}` }) || fallbackURI); + const weblinkRecord = load(new TextDecoder().decode(weblinkYaml)) as Weblink[] + weblinksArray.push(...weblinkRecord); + } + } + } + return weblinksArray; + } + + private webLinkSetHelper = async (fileUri: vscode.Uri): Promise => { + const weblinkSetYaml = await vscode.workspace.fs.readFile(fileUri); + const weblinkSetRecord = load(new TextDecoder().decode(weblinkSetYaml)) as WeblinkSet; + weblinkSetRecord.adx_websiteid = this.websiteData.adx_websiteid; + return weblinkSetRecord; + }; + + private getWeblinkSets = async (): Promise => { + const weblinkSetsDirectory = await vscode.workspace.fs.readDirectory(this.pathroot?.with({ path: this.pathroot.path + '/weblink-sets' }) || fallbackURI); + + const weblinkSetsArray: WeblinkSet[] = []; + for (const weblinkSet of weblinkSetsDirectory) { + const linkSubDirectory = await vscode.workspace.fs.readDirectory(this.pathroot?.with({ path: this.pathroot.path + '/weblink-sets/' + weblinkSet[0] }) || fallbackURI); + for (const sublink of linkSubDirectory) { + if (sublink[0].endsWith(ContextProperty.WEB_LINK_SET)) { + weblinkSetsArray.push(await this.webLinkSetHelper(this.pathroot?.with({ path: this.pathroot.path + '/weblink-sets/' + weblinkSet[0] + `/${sublink[0]}` }) || fallbackURI)); // adx_title not in pac data but is manadatory, studio sends as undefined. + } + } + } + return weblinkSetsArray; + } +} \ No newline at end of file diff --git a/src/common/TreeStructure/DataParser.ts b/src/common/TreeStructure/DataParser.ts new file mode 100644 index 00000000..3edb4c7e --- /dev/null +++ b/src/common/TreeStructure/DataParser.ts @@ -0,0 +1,38 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ +import { Tokenizer, TokenKind } from "liquidjs"; +import { OutputToken, TagToken } from "liquidjs/dist/tokens"; +import { DataParserRule, ruleDefinitions, DyanmicEntity } from "./DataParserRule"; + +const rules: DataParserRule[] = []; +ruleDefinitions.forEach(rule => rules.push(rule)) + +export const getDependencies = (content: string): DyanmicEntity[] => { + const tokenizer = new Tokenizer(content); + const liquidTokens = tokenizer.readTopLevelTokens(); + const allDependencey: DyanmicEntity[] = []; + liquidTokens.forEach(token => { + if (token.kind !== TokenKind.HTML) { + const info = checkRule(token as TagToken | OutputToken); + allDependencey.push(...info); + } + }); + return allDependencey; +}; + +const checkRule = (liquidToken: TagToken | OutputToken): DyanmicEntity[] => { + return rules + .map(r => { + if (r.isValid(liquidToken)) { + const info = r.apply(liquidToken) + return info; + } else { + return [] + } + }) + .reduce(function (prev, next) { + return prev.concat(next); + }, []); +} \ No newline at end of file diff --git a/src/common/TreeStructure/DataParserRule.ts b/src/common/TreeStructure/DataParserRule.ts new file mode 100644 index 00000000..26c3c9eb --- /dev/null +++ b/src/common/TreeStructure/DataParserRule.ts @@ -0,0 +1,145 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ +import { TagToken, Tokenizer, TokenKind, Value } from "liquidjs"; +import { OutputToken, PropertyAccessToken } from "liquidjs/dist/tokens"; +import { PortalObjects, PortalTags } from "../../server/constants/PortalEnums"; + +export interface DyanmicEntity { + tagName: string, + property: string; + fileNameOrID?: string; +} + +export interface DataParserRule { + name: string, + isValid: (liquidToken: TagToken | OutputToken) => boolean + apply: (liquidToken: TagToken | OutputToken) => DyanmicEntity[] +} + +const snippetObjectRule: DataParserRule = { + name: 'snippetObject', + isValid: (liquidToken) => liquidToken.kind === TokenKind.Output && liquidToken.content.includes(PortalObjects.SNIPPETS), + apply: (liquidToken) => { + const entities: DyanmicEntity[] = []; + const tokenizer = new Tokenizer(liquidToken.content) + const property = tokenizer.readIdentifier().getText(); + const value = tokenizer.readValue() as PropertyAccessToken; + const fileNameOrID = value.propertyName; + const tagName = PortalObjects.SNIPPETS; + entities.push({ tagName, property, fileNameOrID }); + return entities; + } +} + + +const entityFormTagRule: DataParserRule = { + name: 'entityFormTag', + isValid: (liquidToken) => liquidToken.kind === TokenKind.Tag && (liquidToken as TagToken).name.toLowerCase() === PortalTags.ENTITYFORM, + apply: (liquidToken) => { + const entities: DyanmicEntity[] = []; + const tokenizer = new Tokenizer((liquidToken as TagToken).args) + const hashes = tokenizer.readHashes(); + hashes.forEach(hash => { + const property = hash.name.getText() + const fileNameOrID = hash.value?.getText() + if (['id', 'name', 'key'].includes(property)) { + const tagName = PortalTags.ENTITYFORM; + entities.push({ tagName, property, fileNameOrID }); + } + }) + return entities + } +} + +const entityListTagRule: DataParserRule = { + name: 'entityListTag', + isValid: (liquidToken) => liquidToken.kind === TokenKind.Tag && (liquidToken as TagToken).name.toLowerCase() === PortalTags.ENTITYLIST, + apply: (liquidToken) => { + const entities: DyanmicEntity[] = []; + const tokenizer = new Tokenizer((liquidToken as TagToken).args) + const hashes = tokenizer.readHashes(); + hashes.forEach(hash => { + const property = hash.name.getText() + const fileNameOrID = hash.value?.getText() + if (['id', 'name', 'key'].includes(property)) { + const tagName = PortalTags.ENTITYLIST; + entities.push({ tagName, property, fileNameOrID }); + } + }) + return entities; + } +} + +const webFormTagRule: DataParserRule = { + name: 'webFormTag', + isValid: (liquidToken) => liquidToken.kind === TokenKind.Tag && (liquidToken as TagToken).name.toLowerCase() === PortalTags.WEBFORM, + apply: (liquidToken) => { + const entities: DyanmicEntity[] = []; + const tokenizer = new Tokenizer((liquidToken as TagToken).args) + const hashes = tokenizer.readHashes(); + hashes.forEach(hash => { + const property = hash.name.getText() + const fileNameOrID = hash.value?.getText() + if (['id', 'name', 'key'].includes(property)) { + const tagName = PortalTags.WEBFORM; + entities.push({ tagName, property, fileNameOrID }); + } + }) + return entities; + } +} + +const includeTagRule: DataParserRule = { + name: 'includeTag', + isValid: (liquidToken) => liquidToken.kind === TokenKind.Tag && (liquidToken as TagToken).name.toLowerCase() === PortalTags.INCLUDE, + apply: (tagToken: TagToken | OutputToken): DyanmicEntity[] => { + const entities: DyanmicEntity[] = []; + const tokenizer = new Tokenizer((tagToken as TagToken).args); + const value = tokenizer.readValue(); + const hashes = tokenizer.readHashes(); + //Handling case - {% include 'Search' %} + if (hashes.length == 0) { + const property = "name"; + const fileNameOrID = value?.getText() || ''; + const tagName = "Web Template"; + entities.push({ tagName, property, fileNameOrID }); + } else { + hashes.forEach(hash => { + const tagName = value?.getText() || ''; + const property = hash.name.getText().toLowerCase(); + const fileNameOrID = hash.value?.getText(); + entities.push({ tagName, property, fileNameOrID }); + }); + } + return entities; + } +}; + +const editableTagRule: DataParserRule = { + name: 'editableTag', + isValid: (liquidToken) => liquidToken.kind === TokenKind.Tag && (liquidToken as TagToken).name.toLowerCase() === PortalTags.EDITABLE, + apply: (liquidToken) => { + const entities: DyanmicEntity[] = []; + const tokenizer = new Tokenizer((liquidToken as TagToken).args) + const property = tokenizer.readIdentifier().getText(); + const value = tokenizer.readValue() + const fileNameOrID = value?.getText() || ''; + const tagName = PortalTags.EDITABLE; + entities.push({ tagName, property, fileNameOrID }); + return entities; + } +} + + + +export const ruleDefinitions = [ + includeTagRule, + editableTagRule, + entityFormTagRule, + webFormTagRule, + entityListTagRule, + snippetObjectRule, +] + diff --git a/src/common/TreeStructure/MyReferenceProvider.ts b/src/common/TreeStructure/MyReferenceProvider.ts new file mode 100644 index 00000000..e0056754 --- /dev/null +++ b/src/common/TreeStructure/MyReferenceProvider.ts @@ -0,0 +1,234 @@ +/* + * 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 fs from 'fs'; +import { getDependencies } from "./DataParser"; +import { DyanmicEntity } from './DataParserRule'; +import { IItem } from './TreeView/Types/Entity/IItem'; +import { globalWebsiteIItem, globalwebPageIItem } from './DataMapper'; + +export class MyReferenceProvider implements vscode.ReferenceProvider { + async provideReferences( + document: vscode.TextDocument, + position: vscode.Position, + context: vscode.ReferenceContext, + token: vscode.CancellationToken + ): Promise { + const selection = vscode.window.activeTextEditor?.selection; + if (!selection) { + return []; + } + const startLine = selection.start.line; + const endLine = selection.end.line; + + const selectedLines: string[] = []; + for (let line = startLine; line <= endLine; line++) { + const lineText = document.lineAt(line).text; + selectedLines.push(lineText); + } + const selectedText = document.getText(selection); + const selectedTextLine = selectedLines.join('\n'); + const dependencies = getDependencies(selectedTextLine); + const locations: vscode.Location[] = []; + if (dependencies.length === 0) { + findText(selectedText, globalWebsiteIItem, locations); + } else { + processDependencies(dependencies, locations); + } + return locations; + } +} + +function findText(selectedText: string, globalWebsiteIItem: IItem, locations: vscode.Location[]) { + for (const entityIItem of globalWebsiteIItem.children) { + if (entityIItem.label == 'Web Page') { + globalwebPageIItem.children.forEach((item: IItem) => { + const pgcy = item.children.find(child => child.label === "Page Copy"); + const pgsy = item.children.find(child => child.label === "Page Summary"); + const cp = item.children.find(child => child.label === "Content Page") + const cppgcy = cp?.children.find(child => child.label === "Page Copy"); + const cppgsy = cp?.children.find(child => child.label === "Page Summary"); + + findTextInFile(pgcy, selectedText, locations); + findTextInFile(pgsy, selectedText, locations); + findTextInFile(cppgcy, selectedText, locations); + findTextInFile(cppgsy, selectedText, locations); + }) + } else { + for (const iitem of entityIItem.children) { + findTextInFile(iitem, selectedText, locations); + } + } + } +} + +function findTextInFile(entityIItem: any, selectedText: any, locations: vscode.Location[]) { + let file = entityIItem.children.find((child: IItem) => child.isFile === true); + const filePath = file?.path?.fsPath; + if (!filePath) { + return; + } + + try { + const fileContent = fs.readFileSync(filePath, 'utf-8'); + const lines = fileContent.split(/\r?\n/); + + for (let i = 0; i < lines.length; i++) { + let startIndex = 0; + while ((startIndex = lines[i].indexOf(selectedText, startIndex)) !== -1) { + const entityPosition = new vscode.Position(i, startIndex); + const entityLocation = new vscode.Location(vscode.Uri.file(filePath), entityPosition); + locations.push(entityLocation); + startIndex += selectedText.length; + } + } + } catch (error) { + console.error('Error reading file:', error); + } +} + +function processDependencies(dependencies: DyanmicEntity[], locations: vscode.Location[]) { + dependencies.forEach((entity: any) => { + const tagName = entity.tagName.replace(/^['"](.*)['"]$/, '$1'); + const property = entity.property.replace(/^['"](.*)['"]$/, '$1'); + const fileNameOrID = entity.fileNameOrID.replace(/^['"](.*)['"]$/, '$1'); + if (tagName === "snippets" || tagName === "snippet" || (tagName === 'editable' && (property === "snippets" || property === "snippet"))) { + const contentSnippetIItem = globalWebsiteIItem.children.find((child: IItem) => child.label === 'Content Snippets'); + if (contentSnippetIItem) { + processEntity(contentSnippetIItem, entity, locations, 'label', '07'); + } + } else if (tagName === "Web Template") { + const webtemplateIItem = globalWebsiteIItem.children.find((child: IItem) => child.label === 'Web Templates'); + if (webtemplateIItem) { + processEntity(webtemplateIItem, entity, locations, 'label', '08'); + } + } else if (tagName === "entityform" || tagName === "entity_form") { + const entityFormtIItem = globalWebsiteIItem.children.find((child: IItem) => child.label === 'Basic Forms'); + if (entityFormtIItem) { + if (property == 'id' && isNaN(fileNameOrID)) { + processEntity(entityFormtIItem, entity, locations, 'id', '015'); + } else if (property == 'name' || property == 'key') { + processEntity(entityFormtIItem, entity, locations, 'label', '015') + } else { + console.log("Not Valid EnitityForm"); + } + } + } else if (tagName === "entitylist" || tagName === "entity_list") { + const listIItem = globalWebsiteIItem.children.find((child: IItem) => child.label === 'Lists'); + if (listIItem) { + if (property == 'id' && isNaN(fileNameOrID)) { + processEntity(listIItem, entity, locations, 'id', '017'); + } else if (property == 'name' || property == 'key') { + processEntity(listIItem, entity, locations, 'label', '017') + } else { + console.log("Not Valid EntityList"); + } + } + } else if (tagName === "webform") { + const webFormIItem = globalWebsiteIItem.children.find((child: IItem) => child.label === 'Advanced Forms'); + if (webFormIItem) { + if (property == 'id' && isNaN(fileNameOrID)) { + processEntity(webFormIItem, entity, locations, 'id', '019'); + } else if (property == 'name' || property == 'key') { + processEntity(webFormIItem, entity, locations, 'label', '019') + } else { + console.log("Not Valid WebForm"); + } + } + } else if ((tagName != "entityform" && tagName != "entity_form") && (tagName != "entitylist" && tagName != "entity_list") && tagName !== "editable") { + entity.fileNameOrID = tagName; + entity.tagName = 'Web Template'; + const webtemplateIItem = globalWebsiteIItem.children.find((child: IItem) => child.label === 'Web Templates'); + if (webtemplateIItem) { + processEntity(webtemplateIItem, entity, locations, 'label', '08'); + } + } else { + console.log("Another Dynamic entity"); + } + }) +} + + +function processEntity(targetIItem: IItem, entity: any, locations: vscode.Location[], compareBy: string, comp: string) { + const fileNameOrID = entity.fileNameOrID.replace(/^['"](.*)['"]$/, '$1'); + const tagName = entity.tagName.replace(/^['"](.*)['"]$/, '$1'); + targetIItem.children.forEach((targetItem: IItem) => { + const compareValue = compareBy === 'label' ? targetItem.label : targetItem.id; + if (compareValue === fileNameOrID) { + const IITem = targetItem.parentList; + if (IITem) { + for (const iitem of IITem) { + const file = iitem.children.find((child: IItem) => child.isFile === true); + if (file) { + addFileLocations(file, locations, entity); + } + } + } + } + }); +} + +function addFileLocations(file: IItem, locations: vscode.Location[], entity: DyanmicEntity) { + const filePath = file?.path?.fsPath; + if (!filePath) { + return; + } + try { + const fileContent = fs.readFileSync(filePath, 'utf-8'); + const lines = fileContent.split(/\r?\n/); + let file = entity.fileNameOrID?.replace(/^['"](.*)['"]$/, '$1'); + let tagName = entity.tagName.replace(/^['"](.*)['"]$/, '$1'); + const property = entity.property.replace(/^['"](.*)['"]$/, '$1'); + + if (!file) { + return; + } + for (let i = 0; i < lines.length; i++) { + if (lines[i].includes(file)) { + const dep = getDependencies(lines[i]); + dep.forEach((de: any) => { + let tag = de.tagName.replace(/^['"](.*)['"]$/, '$1'); + const pro = de.property.replace(/^['"](.*)['"]$/, '$1'); + let fn = de.fileNameOrID?.replace(/^['"](.*)['"]$/, '$1'); + if (tag !== 'snippets' && tag !== 'snippet' && tag !== 'editable' && tag !== 'Web Template' && tag !== "webform" && tag !== "entityform" && tag !== "entity_form" && tag !== "entitylist" && tag !== "entity_list") { + fn = tag; + tag = 'Web Template'; + } + if (tag == tagName && fn == file) { + const entityPosition = new vscode.Position(i, 0); + const entityLocation = new vscode.Location(vscode.Uri.file(filePath), entityPosition); + locations.push(entityLocation); + } else if ((tag == 'snippets' && (tagName === 'snippets' || tagName === 'snippet')) || (tagName == 'snippets' && (tag === 'snippets' || tag === 'snippet')) && fn == file) { + const entityPosition = new vscode.Position(i, 0); + const entityLocation = new vscode.Location(vscode.Uri.file(filePath), entityPosition); + locations.push(entityLocation); + } else if (tagName === 'editable' && (property === "snippets" || property === "snippet")) { + if ((tag === 'snippets' || tag === 'snippet') && fn == file) { + const entityPosition = new vscode.Position(i, 0); + const entityLocation = new vscode.Location(vscode.Uri.file(filePath), entityPosition); + locations.push(entityLocation); + } + } else if (tag === 'editable' && (pro === "snippets" || pro === "snippet")) { + if ((tagName === 'snippets' || tagName === 'snippet') && fn == file) { + const entityPosition = new vscode.Position(i, 0); + const entityLocation = new vscode.Location(vscode.Uri.file(filePath), entityPosition); + locations.push(entityLocation); + } + } else if (((tag === 'entity_form' && tagName === 'entityform') || (tagName === 'entity_form' && tag === 'entityform')) && fn == file) { + const entityPosition = new vscode.Position(i, 0); + const entityLocation = new vscode.Location(vscode.Uri.file(filePath), entityPosition); + locations.push(entityLocation); + } else if (((tag === 'entity_list' && tagName === 'entitylist') || (tagName === 'entity_list' && tag === 'entitylist')) && fn == file) { + const entityPosition = new vscode.Position(i, 0); + const entityLocation = new vscode.Location(vscode.Uri.file(filePath), entityPosition); + locations.push(entityLocation); + } + }) + } + } + } catch (error) { + console.error('Error reading file:', error); + } +} diff --git a/src/common/TreeStructure/TreeView/PortalDrops/PortalDrops.ts b/src/common/TreeStructure/TreeView/PortalDrops/PortalDrops.ts new file mode 100644 index 00000000..cb62037d --- /dev/null +++ b/src/common/TreeStructure/TreeView/PortalDrops/PortalDrops.ts @@ -0,0 +1,11 @@ +import { Drop } from 'liquidjs'; +import { IDataResolver } from '../Utils/IDataResolver'; + +export class PortalDrop extends Drop { + protected dataResolver: IDataResolver; + + constructor(dataResolver: IDataResolver) { + super(); + this.dataResolver = dataResolver; + } +} diff --git a/src/common/TreeStructure/TreeView/Types/Entity/AbstractEntity.ts b/src/common/TreeStructure/TreeView/Types/Entity/AbstractEntity.ts new file mode 100644 index 00000000..a8c15e6c --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/Entity/AbstractEntity.ts @@ -0,0 +1,5 @@ +export interface AbstractEntity { + ownerId?: string; + stateCode?: number; + statusCode?: number; +} diff --git a/src/common/TreeStructure/TreeView/Types/Entity/ContentSnippet.ts b/src/common/TreeStructure/TreeView/Types/Entity/ContentSnippet.ts new file mode 100644 index 00000000..e36749db --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/Entity/ContentSnippet.ts @@ -0,0 +1,18 @@ +import { AbstractEntity } from './AbstractEntity'; + +export interface ContentSnippet extends AbstractEntity { + adx_display_name?: string; + adx_value: string; + adx_createdbyipaddress?: string; + adx_createdbyusername?: string; + adx_name: string; + adx_modifiedbyipaddress?: string; + adx_modifiedbyusername?: string; + adx_websiteid: string; + adx_websiteidname?: string; + adx_contentsnippetlanguageidname?: string; + adx_type?: number; + adx_contentsnippetlanguageid?: string; + adx_typename?: string; + adx_contentsnippetid: string; +} diff --git a/src/common/TreeStructure/TreeView/Types/Entity/EntityAttributeMetadata.ts b/src/common/TreeStructure/TreeView/Types/Entity/EntityAttributeMetadata.ts new file mode 100644 index 00000000..b4dda8c8 --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/Entity/EntityAttributeMetadata.ts @@ -0,0 +1,7 @@ +import { AbstractEntity } from './AbstractEntity'; + +export interface IEntityAttributeMetadata extends AbstractEntity { + attributeType: string; + localizedDisplayName: string; + logicalName: string; +} diff --git a/src/common/TreeStructure/TreeView/Types/Entity/EntityForm.ts b/src/common/TreeStructure/TreeView/Types/Entity/EntityForm.ts new file mode 100644 index 00000000..e787514d --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/Entity/EntityForm.ts @@ -0,0 +1,117 @@ +import { AbstractEntity } from './AbstractEntity'; + +export interface EntityForm extends AbstractEntity { + adx_appendquerystring?: boolean; + adx_appendquerystringname?: string; + adx_associatecurrentportaluser?: boolean; + adx_associatecurrentportalusername?: string; + adx_attachfile?: boolean; + adx_attachfileaccept?: string; + adx_attachfileacceptextensions?: string; + adx_attachfileallowmultiple?: boolean; + adx_attachfilelabel?: string; + adx_attachfilemaxsize?: number; + adx_attachfilename?: string; + adx_attachfilerequired?: boolean; + adx_attachfilerequirederrormessage?: string; + adx_attachfilerequiredname?: string; + adx_attachfilerestrictaccept?: boolean; + adx_attachfilesaveoption?: number; + adx_attachfilesizeerrormessage?: string; + adx_attachfilestoragelocation?: number; + adx_attachfiletypeerrormessage?: string; + adx_autogeneratesteps?: boolean; + adx_autogeneratestepsname?: string; + adx_captcharequired?: boolean; + adx_captcharequiredname?: string; + adx_entityformid: string; + adx_entityname: string; + adx_entitypermissionsenabled?: boolean; + adx_entitysourcetype?: number; + adx_forceallfieldsrequired?: boolean; + adx_formname: string; + adx_geolocation_addresslinefieldname?: string; + adx_geolocation_cityfieldname?: string; + adx_geolocation_countryfieldname?: string; + adx_geolocation_countyfieldname?: string; + adx_geolocation_displaymap?: boolean; + adx_geolocation_enabled?: boolean; + adx_geolocation_formattedaddressfieldname?: string; + adx_geolocation_latitudefieldname?: string; + adx_geolocation_longitudefieldname?: string; + adx_geolocation_maptype?: number; + adx_geolocation_neighborhoodfieldname?: string; + adx_geolocation_postalcodefieldname?: string; + adx_geolocation_statefieldname?: string; + adx_hideformonsuccess?: boolean; + adx_instructions?: string; + adx_mode?: number; + adx_modename?: string; + adx_name: string; + adx_nextbuttoncssclass?: string; + adx_nextbuttontext?: string; + adx_onsuccess?: number; + adx_populatereferenceentitylookupfield?: boolean; + adx_populatereferenceentitylookupfieldname?: string; + adx_portaluserlookupattributeisactivityparty?: boolean; + adx_portaluserlookupattributeisactivitypartyname?: string; + adx_previousbuttoncssclass?: string; + adx_previousbuttontext?: string; + adx_primarykeyname?: string; + adx_provisionedlanguages?: number; + adx_recommendedfieldsrequired?: boolean; + adx_recommendedfieldsrequiredname?: string; + adx_recordidquerystringparametername?: string; + adx_recordnotfoundmessage?: string; + adx_recordsourceallowcreateonnull?: boolean; + adx_recordsourceentitylogicalname?: string; + adx_recordsourcerelationshipname?: string; + adx_redirecturl?: string; + adx_redirecturlappendentityidquerystring?: boolean; + adx_redirecturlappendentityidquerystringname?: string; + adx_redirecturlcustomquerystring?: string; + adx_redirecturlquerystringattribute?: string; + adx_redirecturlquerystringattributeparamname?: string; + adx_redirecturlquerystringname?: string; + adx_redirectwebpage?: string; + adx_redirectwebpagename?: string; + adx_referenceentitylogicalname?: string; + adx_referenceentityprimarykeylogicalname?: string; + adx_referenceentityreadonlyformname?: string; + adx_referenceentityrelationshipname?: string; + adx_referenceentityshowreadonlyform?: boolean; + adx_referenceentityshowreadonlyformname?: string; + adx_referenceentitysourcetype?: number; + adx_referenceentitysourcetypename?: string; + adx_referencequeryattributelogicalname?: string; + adx_referencequerystringisprimarykey?: boolean; + adx_referencequerystringisprimarykeyname?: string; + adx_referencequerystringname?: string; + adx_referencerecordsourcerelationshipname?: string; + adx_referencetargetlookupattributelogicalname?: string; + adx_registerstartupscript?: string; + adx_renderwebresourcesinline?: boolean; + adx_setentityreference?: boolean; + adx_setentityreferencename?: string; + adx_settings?: string; + adx_showcaptchaforauthenticatedusers?: boolean; + adx_showcaptchaforauthenticatedusersname?: string; + adx_showownerfields?: boolean; + adx_showownerfieldsname?: string; + adx_showunsupportedfields?: boolean; + adx_showunsupportedfieldsname?: string; + adx_submitbuttonbusytext?: string; + adx_submitbuttoncssclass?: string; + adx_submitbuttontext?: string; + adx_successmessage: string; + adx_tabname?: string; + adx_targetentityportaluserlookupattribute?: string; + adx_tooltipenabled?: boolean; + adx_validationgroup?: string; + adx_validationsummarycssclass?: string; + adx_validationsummaryheadertext?: string; + adx_validationsummarylinksenabled?: boolean; + adx_validationsummarylinktext?: string; + adx_websiteid: string; + adx_websiteidname?: string; +} \ No newline at end of file diff --git a/src/common/TreeStructure/TreeView/Types/Entity/EntityFormMetadata.ts b/src/common/TreeStructure/TreeView/Types/Entity/EntityFormMetadata.ts new file mode 100644 index 00000000..21d93467 --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/Entity/EntityFormMetadata.ts @@ -0,0 +1,57 @@ +import { AbstractEntity } from './AbstractEntity'; + +export interface EntityFormMetadata extends AbstractEntity { + adx_adddescription?: boolean; + adx_adddescriptionname?: string; + adx_attributelogicalname?: string; + adx_constantsummaximumtotal?: number; + adx_constantsumminimumtotal?: number; + adx_constantsumvalidationerrormessage?: string; + adx_controlstyle?: number; + adx_cssclass?: string; + adx_description?: string; + adx_descriptionposition?: number; + adx_entityform: string; + adx_entityformforcreate?: string; + adx_entityformforcreatename?: string; + adx_entityformmetadataid: string; + adx_entityformname?: string; + adx_fieldisrequired?: boolean; + adx_geolocationvalidatorerrormessage?: string; + adx_groupname?: string; + adx_ignoredefaultvalue?: boolean; + adx_label?: string; + adx_maxmultiplechoiceselectedcount?: number; + adx_minmultiplechoiceselectedcount?: number; + adx_multiplechoicevalidationerrormessage?: string; + adx_name?: string; + adx_notes_settings?: string; + adx_onsavefromattribute?: string; + adx_onsavetype?: number; + adx_onsavetypename?: string; + adx_onsavevalue?: string; + adx_prepopulatefromattribute?: string; + adx_prepopulatetype?: number; + adx_prepopulatetypename?: string; + adx_prepopulatevalue?: string; + adx_provisionedlanguages?: number; + adx_randomizeoptionsetvalues?: boolean; + adx_randomizeoptionsetvaluesname?: string; + adx_rangevalidationerrormessage?: string; + adx_rankordernotiesvalidationerrormessage?: string; + adx_requiredfieldvalidationerrormessage?: string; + adx_sectionname?: string; + adx_setvalueonsave?: boolean; + adx_setvalueonsavename?: string; + adx_subgrid_name?: string; + adx_subgrid_settings?: string; + adx_tabname?: string; + adx_timeline_settings?: string; + adx_type: number; + adx_typename?: string; + adx_useattributedescriptionproperty?: boolean; + adx_useattributedescriptionpropertyname?: string; + adx_validationerrormessage?: string; + adx_validationregularexpression?: string; + adx_validationregularexpressionerrormessage?: string; +} \ No newline at end of file diff --git a/src/common/TreeStructure/TreeView/Types/Entity/EntityList.ts b/src/common/TreeStructure/TreeView/Types/Entity/EntityList.ts new file mode 100644 index 00000000..c8893ad0 --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/Entity/EntityList.ts @@ -0,0 +1,84 @@ +import { AbstractEntity } from './AbstractEntity'; + +export interface EntityList extends AbstractEntity { + adx_provisionedlanguages?: number; + adx_calendar_stylename?: string; + adx_calendar_initialviewname?: string; + adx_calendar_initialdate?: Date; + adx_map_longitude?: string; + adx_filter_applybuttonlabel?: string; + adx_map_infoboxdescriptionfieldname?: string; + adx_map_distanceunitsname?: string; + adx_map_infoboxtitlefieldname?: string; + adx_odata_enabled?: boolean; + adx_calendar_alldayfieldname?: string; + adx_calendar_enddatefieldname?: string; + adx_entitypermissionsenabledname?: string; + adx_map_type?: number; + adx_pagesize: number; + adx_filteraccount?: string; + adx_entitylistid: string; + adx_websiteidname?: string; + adx_idquerystringparametername?: string; + adx_filterwebsite?: string; + adx_map_distanceunits?: number; + adx_map_credentials?: string; + adx_map_latitudefieldname?: string; + adx_calendar_organizerfieldname?: string; + adx_map_pushpinwidth?: number; + adx_calendar_enabledname?: string; + adx_odata_entitysetname?: string; + adx_odata_entitytypename?: string; + adx_map_pushpinurl?: string; + adx_filter_enabled?: boolean; + adx_filter_definition?: string; + adx_views: string; + adx_webpagefordetailsviewname?: string; + adx_filter_enabledname?: string; + adx_websiteid: string; + adx_map_pushpinheight?: number; + adx_filter_orientation?: number; + adx_registerstartupscript?: string; + adx_createbuttonlabel?: string; + adx_map_resturl?: string; + adx_entitypermissionsenabled?: boolean; + adx_map_typename?: string; + adx_calendar_timezone?: number; + adx_map_infoboxoffsetx?: number; + adx_map_zoom?: number; + adx_key?: string; + adx_view?: string; + adx_calendar_timezonemodename?: string; + adx_filterportaluser?: string; + adx_calendar_descriptionfieldname?: string; + adx_map_distancevalues?: string; + adx_detailsbuttonlabel?: string; + adx_searchenabledname?: string; + adx_calendar_locationfieldname?: string; + adx_searchplaceholdertext?: string; + adx_webpageforcreatename?: string; + adx_primarykeyname?: string; + adx_settings?: string; + adx_calendar_initialview?: number; + adx_searchenabled?: boolean; + adx_calendar_enabled?: boolean; + adx_name: string; + adx_calendar_startdatefieldname?: string; + adx_calendar_style?: number; + adx_entityname: string; + adx_map_enabledname?: string; + adx_map_enabled?: boolean; + adx_map_latitude?: string; + adx_webpageforcreate?: string; + adx_webpagefordetailsview?: string; + adx_filter_orientationname?: string; + adx_emptylisttext?: string; + adx_calendar_timezonemode?: number; + adx_odata_enabledname?: string; + adx_searchtooltiptext?: string; + adx_calendar_summaryfieldname?: string; + adx_odata_view?: string; + adx_map_infoboxoffsety?: number; + adx_map_longitudefieldname?: string; + adx_iscodecomponent?: boolean; +} diff --git a/src/common/TreeStructure/TreeView/Types/Entity/IItem.ts b/src/common/TreeStructure/TreeView/Types/Entity/IItem.ts new file mode 100644 index 00000000..f781b77e --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/Entity/IItem.ts @@ -0,0 +1,14 @@ +import * as vscode from 'vscode'; + +export interface IItem { + label: string; + title: string; + id: string; + isFile: boolean; + content: string; + path?: vscode.Uri; + component: string; + children: IItem[]; + error: string; + parentList: IItem[]; +} \ No newline at end of file diff --git a/src/common/TreeStructure/TreeView/Types/Entity/PageTemplate.ts b/src/common/TreeStructure/TreeView/Types/Entity/PageTemplate.ts new file mode 100644 index 00000000..6495f5cb --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/Entity/PageTemplate.ts @@ -0,0 +1,19 @@ +export interface PageTemplate { + adx_description?: string; + adx_webtemplateid?: string; + adx_isdefaultname?: string; + adx_name: string; + adx_typename?: string; + adx_usewebsiteheaderandfootername?: string; + adx_entityname?: string; + adx_usewebsiteheaderandfooter?: boolean; + adx_websiteid?: string; + adx_isdefault?: boolean; + adx_websiteidname?: string; + adx_webtemplateidname?: string; + adx_type?: number; + adx_pagetemplateid: string; + adx_botconsumerid?: string; + adx_rewriteurl?: string; + adx_botconsumeridname?: string; +} \ No newline at end of file diff --git a/src/common/TreeStructure/TreeView/Types/Entity/SiteMarker.ts b/src/common/TreeStructure/TreeView/Types/Entity/SiteMarker.ts new file mode 100644 index 00000000..e09690e8 --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/Entity/SiteMarker.ts @@ -0,0 +1,10 @@ +import { AbstractEntity } from './AbstractEntity'; + +export interface SiteMarker extends AbstractEntity { + adx_websiteid: string; + adx_name: string; + adx_pageidname?: string; + adx_websiteidname?: string; + adx_pageid: string; + adx_sitemarkerid: string; +} diff --git a/src/common/TreeStructure/TreeView/Types/Entity/SiteSettings.ts b/src/common/TreeStructure/TreeView/Types/Entity/SiteSettings.ts new file mode 100644 index 00000000..9a4f5353 --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/Entity/SiteSettings.ts @@ -0,0 +1,10 @@ +import { AbstractEntity } from './AbstractEntity'; + +export interface SiteSetting extends AbstractEntity { + adx_websiteid: string; + adx_description?: string; + adx_value?: string; + adx_name: string; + adx_websiteidname?: string; + adx_sitesettingid: string; +} diff --git a/src/common/TreeStructure/TreeView/Types/Entity/WebFile.ts b/src/common/TreeStructure/TreeView/Types/Entity/WebFile.ts new file mode 100644 index 00000000..108f13c0 --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/Entity/WebFile.ts @@ -0,0 +1,19 @@ +import { AbstractEntity } from './AbstractEntity'; + +export interface WebFile extends AbstractEntity { + adx_contentdisposition: number; + adx_enabletracking: boolean; + adx_excludefromsearch: boolean; + adx_hiddenfromsitemap: boolean; + adx_name: string; + adx_parentpageid: string; + adx_partialurl: string; + adx_publishingstateid: string; + adx_webfileid: string; + annotationid: string; + filename: string; + isdocument: boolean; + mimetype: string; + objectid: string; + objecttypecode: string; +} diff --git a/src/common/TreeStructure/TreeView/Types/Entity/WebForm.ts b/src/common/TreeStructure/TreeView/Types/Entity/WebForm.ts new file mode 100644 index 00000000..798b1b5b --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/Entity/WebForm.ts @@ -0,0 +1,36 @@ +import { AbstractEntity } from './AbstractEntity'; + +export interface WebForm extends AbstractEntity { + adx_progressindicatorpositionname?: string; + adx_savechangeswarningmessage?: string; + adx_progressindicatorposition?: number; + adx_name: string; + adx_editexpiredstatuscode?: number; + adx_startstep?: string; + adx_editexpiredstatecode?: number; + adx_editnotpermittedmessage?: string; + adx_websiteid: string; + adx_editexistingrecordpermittedname?: string; + adx_progressindicatortypename?: string; + adx_progressindicatorprependstepnum?: boolean; + adx_progressindicatorignorelaststep?: boolean; + adx_progressindicatorenabled?: boolean; + adx_multiplerecordsperuserpermitted?: boolean; + adx_authenticationrequired?: boolean; + adx_provisionedlanguages?: number; + adx_authenticationrequiredname?: string; + adx_startnewsessiononloadname?: string; + adx_startstepname?: string; + adx_startnewsessiononload?: boolean; + adx_progressindicatorprependstepnumname?: string; + adx_editexpiredmessage?: string; + adx_savechangeswarningonclose?: boolean; + adx_webformid: string; + adx_progressindicatorignorelaststepname?: string; + adx_editexistingrecordpermitted?: boolean; + adx_savechangeswarningonclosename?: string; + adx_progressindicatortype?: number; + adx_progressindicatorenabledname?: string; + adx_multiplerecordsperuserpermittedname?: string; + adx_websiteidname?: string; +} diff --git a/src/common/TreeStructure/TreeView/Types/Entity/WebFormMetadata.ts b/src/common/TreeStructure/TreeView/Types/Entity/WebFormMetadata.ts new file mode 100644 index 00000000..41938fb7 --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/Entity/WebFormMetadata.ts @@ -0,0 +1,75 @@ + +import { AbstractEntity } from './AbstractEntity'; + +export interface WebFormMetadata extends AbstractEntity { + adx_adddescription?: boolean; + adx_adddescriptionname?: string; + adx_attributelogicalname: string; + adx_constantsummaximumtotal?: number; + adx_constantsumminimumtotal?: number; + adx_constantsumvalidationerrormessage?: string; + adx_controlstyle?: number; + adx_cssclass?: string; + adx_description?: string; + adx_descriptionposition?: number; + adx_entityformforcreate?: string; + adx_entityformforcreateinwebformmetadata?: string; + adx_entityformforcreatename?: string; + adx_fieldisrequired?: boolean; + adx_geolocationvalidatorerrormessage?: string; + adx_groupname?: string; + adx_ignoredefaultvalue?: boolean; + adx_label?: string; + adx_maxmultiplechoiceselectedcount?: number; + adx_minmultiplechoiceselectedcount?: number; + adx_multiplechoicevalidationerrormessage?: string; + adx_notes_settings?: string; + adx_onsavefromattribute?: string; + adx_onsavetype?: number; + adx_onsavetypename?: string; + adx_onsavevalue?: string; + adx_prepopulatefromattribute?: string; + adx_prepopulatetype?: number; + adx_prepopulatetypename?: string; + adx_prepopulatevalue?: string; + adx_provisionedlanguages?: number; + adx_purchasecreateinvoiceonpayment?: boolean; + adx_purchasefulfillorderonpayment?: boolean; + adx_purchaselineitemdescriptionattribute?: string; + adx_purchaselineiteminstructionsattribute?: string; + adx_purchaselineitemorderattribute?: string; + adx_purchaselineitemproductattribute?: string; + adx_purchaselineitemquantityattribute?: string; + adx_purchaselineitemrelationship?: string; + adx_purchaselineitemrequiredattribute?: string; + adx_purchaselineitemuomattribute?: string; + adx_purchaseoptionalproductsrelationship?: string; + adx_purchasequotename?: string; + adx_purchaserequiredproductsrelationship?: string; + adx_purchaserequiresshipping?: boolean; + adx_purchasetargetentityinvoicerelationship?: string; + adx_purchasetargetentityorderrelationship?: string; + adx_purchasetargetentityrelationship?: string; + adx_randomizeoptionsetvalues?: boolean; + adx_randomizeoptionsetvaluesname?: string; + adx_rangevalidationerrormessage?: string; + adx_rankordernotiesvalidationerrormessage?: string; + adx_requiredfieldvalidationerrormessage?: string; + adx_sectionname?: string; + adx_setvalueonsave?: boolean; + adx_setvalueonsavename?: string; + adx_subgrid_name?: string; + adx_subgrid_settings?: string; + adx_tabname?: string; + adx_timeline_settings?: string; + adx_type: number; + adx_typename?: string; + adx_useattributedescriptionproperty?: boolean; + adx_useattributedescriptionpropertyname?: string; + adx_validationerrormessage?: string; + adx_validationregularexpression?: string; + adx_validationregularexpressionerrormessage?: string; + adx_webformstep: string; + adx_entityformmetadataid?: string; + adx_webformstepid?: string; +} diff --git a/src/common/TreeStructure/TreeView/Types/Entity/WebFormStep.ts b/src/common/TreeStructure/TreeView/Types/Entity/WebFormStep.ts new file mode 100644 index 00000000..fae6f0b2 --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/Entity/WebFormStep.ts @@ -0,0 +1,146 @@ +import { AbstractEntity } from './AbstractEntity'; + +export interface WebFormStep extends AbstractEntity { + adx_accept?: string; + adx_allowmultiplefiles?: boolean; + adx_appendquerystring?: boolean; + adx_appendquerystringname?: string; + adx_associatecurrentportaluser?: boolean; + adx_associatecurrentportalusername?: string; + adx_attachfile?: boolean; + adx_attachfilelabel?: string; + adx_attachfilemaxsize?: number; + adx_attachfilename?: string; + adx_attachfilerequired?: boolean; + adx_attachfilerequirederrormessage?: string; + adx_attachfilerequiredname?: string; + adx_attachfilerestrictaccept?: boolean; + adx_attachfilesizeerrormessage?: string; + adx_attachfilestoragelocation?: number; + adx_attachfiletypeerrormessage?: string; + adx_autogeneratesteps?: boolean; + adx_autogeneratestepsname?: string; + adx_autonumberattributelogicalname?: string; + adx_autonumberdefinitionname?: string; + adx_captcharequired?: boolean; + adx_captcharequiredname?: string; + adx_condition?: string; + adx_conditiondefaultnextstep?: string; + adx_conditiondefaultnextstepname?: string; + adx_createautonumber?: boolean; + adx_editexistingrecordpermitted?: boolean; + adx_editexpiredmessage?: string; + adx_editexpiredstatecode?: number; + adx_editexpiredstatusreason?: number; + adx_editnotpermittedmessage?: string; + adx_entitypermissionsenabled?: boolean; + adx_entitysourcestep?: string; + adx_entitysourcestepname?: string; + adx_entitysourcetype?: number; + adx_forceallfieldsrequired?: boolean; + adx_formname?: string; + adx_geolocation_addresslinefieldname?: string; + adx_geolocation_cityfieldname?: string; + adx_geolocation_countryfieldname?: string; + adx_geolocation_countyfieldname?: string; + adx_geolocation_displaymap?: boolean; + adx_geolocation_enabled?: boolean; + adx_geolocation_formattedaddressfieldname?: string; + adx_geolocation_latitudefieldname?: string; + adx_geolocation_longitudefieldname?: string; + adx_geolocation_maptype?: number; + adx_geolocation_neighborhoodfieldname?: string; + adx_geolocation_postalcodefieldname?: string; + adx_geolocation_statefieldname?: string; + adx_hideformonsuccess?: boolean; + adx_instructions?: string; + adx_loadeventkeyname?: string; + adx_loguser?: boolean; + adx_mode?: number; + adx_modename?: string; + adx_movepreviouspermitted?: boolean; + adx_multiplerecordsperuserpermitted?: boolean; + adx_name: string; + adx_nextbuttoncssclass?: string; + adx_nextbuttontext?: string; + adx_nextstep: string; + adx_nextstepname?: string; + adx_populatereferenceentitylookupfield?: boolean; + adx_populatereferenceentitylookupfieldname?: string; + adx_portaluserlookupattributeisactivityparty?: boolean; + adx_portaluserlookupattributeisactivitypartyname?: string; + adx_postbackurl?: string; + adx_previousbuttoncssclass?: string; + adx_previousbuttontext?: string; + adx_previousstep: string; + adx_previousstepname?: string; + adx_primarykeyattributelogicalname?: string; + adx_primarykeyquerystringparametername?: string; + adx_provisionedlanguages?: number; + adx_recommendedfieldsrequired?: boolean; + adx_recommendedfieldsrequiredname?: string; + adx_recordnotfoundmessage?: string; + adx_recordsourcerelationshipname?: string; + adx_redirecturl?: string; + adx_redirecturlappendentityidquerystring?: boolean; + adx_redirecturlappendentityidquerystringname?: string; + adx_redirecturlcustomquerystring?: string; + adx_redirecturlquerystringattribute?: string; + adx_redirecturlquerystringattributeparamname?: string; + adx_redirecturlquerystringname?: string; + adx_redirectwebpage?: string; + adx_referenceentitylogicalname?: string; + adx_referenceentityprimarykeylogicalname?: string; + adx_referenceentityreadonlyformname?: string; + adx_referenceentityrelationshipname?: string; + adx_referenceentityshowreadonlyform?: boolean; + adx_referenceentityshowreadonlyformname?: string; + adx_referenceentitysourcetype?: number; + adx_referenceentitysourcetypename?: string; + adx_referenceentitystep?: string; + adx_referenceentitystepname?: string; + adx_referencequeryattributelogicalname?: string; + adx_referencequerystringisprimarykey?: boolean; + adx_referencequerystringisprimarykeyname?: string; + adx_referencequerystringname?: string; + adx_referencerecordsourcerelationshipname?: string; + adx_referencesourceentitylogicalname?: string; + adx_referencetargetlookupattributelogicalname?: string; + adx_registerstartupscript?: string; + adx_renderwebresourcesinline?: boolean; + adx_savedeventkeyname?: string; + adx_savingeventkeyname?: string; + adx_setentityreference?: boolean; + adx_setentityreferencename?: string; + adx_settings?: string; + adx_showcaptchaforauthenticatedusers?: boolean; + adx_showcaptchaforauthenticatedusersname?: string; + adx_showownerfields?: boolean; + adx_showownerfieldsname?: string; + adx_showunsupportedfields?: boolean; + adx_showunsupportedfieldsname?: string; + adx_submitbuttonbusytext?: string; + adx_submitbuttoncssclass?: string; + adx_submitbuttontext?: string; + adx_submiteventkeyname?: string; + adx_successmessage: string; + adx_tabname?: string; + adx_targetentitylogicalname: string; + adx_targetentityportaluserlookupattribute?: string; + adx_targetentityprimarykeylogicalname?: string; + adx_title: string; + adx_tooltipenabled?: boolean; + adx_type: number; + adx_usercontrolpath?: string; + adx_usercontroltitle?: string; + adx_userhostaddressattributelogicalname?: string; + adx_useridentitynameattributelogicalname?: string; + adx_validationgroup?: string; + adx_validationsummarycssclass?: string; + adx_validationsummaryheadertext?: string; + adx_validationsummarylinksenabled?: boolean; + adx_validationsummarylinktext?: string; + adx_webform: string; + adx_webformname?: string; + adx_webformstepid: string; +} \ No newline at end of file diff --git a/src/common/TreeStructure/TreeView/Types/Entity/WebPage.ts b/src/common/TreeStructure/TreeView/Types/Entity/WebPage.ts new file mode 100644 index 00000000..431ef6da --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/Entity/WebPage.ts @@ -0,0 +1,76 @@ +import { AbstractEntity } from './AbstractEntity'; + +export interface Webpage extends AbstractEntity { + adx_customcss?: string; + adx_authoridname?: string; + adx_entityform?: string; + adx_alloworigin?: string; + adx_modifiedbyipaddress?: string; + adx_entityformname?: string; + adx_meta_description?: string; + adx_botconsumeridname?: string; + adx_hiddenfromsitemapname?: string; + adx_modifiedbyusername?: string; + adx_webpagelanguageid?: string; + adx_title?: string; + adx_createdbyipaddress?: string; + adx_websiteidname?: string; + adx_editorialcomments?: string; + adx_categoryname?: string; + adx_authoridyominame?: string; + adx_parentpageidname?: string; + adx_navigationname?: string; + adx_authorid?: string; + adx_isrootname?: string; + adx_hiddenfromsitemap?: boolean; + adx_excludefromsearchname?: string; + adx_image?: string; + adx_imageurl?: string; + adx_publishingstateidname?: string; + adx_webform?: string; + adx_partialurl: string; + adx_displaydate?: Date; + adx_websiteid: string; + adx_customjavascript?: string; + adx_isofflinecachedname?: string; + adx_imagename?: string; + adx_enabletracking?: boolean; + adx_subjectid?: string; + adx_rootwebpageidname?: string; + adx_webformname?: string; + adx_displayorder?: number; + adx_isroot: boolean; + adx_category?: number; + adx_webpagelanguageidname?: string; + adx_summary?: string; + adx_subjectidname?: string; + adx_parentpageid?: string; + adx_expirationdate?: Date; + adx_entitylist?: string; + adx_releasedate?: Date; + adx_enabletrackingname?: string; + adx_createdbyusername?: string; + adx_sharedpageconfigurationname?: string; + adx_pagetemplateidname?: string; + adx_name: string; + adx_excludefromsearch?: boolean; + adx_navigation?: string; + adx_pagetemplateid: string; + adx_feedbackpolicy?: number; + adx_botconsumerid?: string; + adx_enableratingname?: string; + adx_masterwebpageid?: string; + adx_entitylistname?: string; + adx_feedbackpolicyname?: string; + adx_copy?: string; + adx_masterwebpageidname?: string; + adx_enablerating?: boolean; + adx_rootwebpageid?: string; + adx_webpageid: string; + adx_sharedpageconfiguration?: boolean; + adx_publishingstateid?: string; + adx_isofflinecached?: boolean; + _adx_webform_value?: string; + _adx_entitylist_value?: string; + _adx_entityform_value?: string; +} diff --git a/src/common/TreeStructure/TreeView/Types/Entity/WebTemplate.ts b/src/common/TreeStructure/TreeView/Types/Entity/WebTemplate.ts new file mode 100644 index 00000000..40508371 --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/Entity/WebTemplate.ts @@ -0,0 +1,13 @@ +/*! + * Copyright (C) Microsoft Corporation. All rights reserved. + */ +import { AbstractEntity } from './AbstractEntity'; + +export interface WebTemplate extends AbstractEntity { + adx_mimetype?: string; + adx_websiteid: string; + adx_source: string; + adx_websiteidname?: string; + adx_webtemplateid: string; + adx_name: string; +} diff --git a/src/common/TreeStructure/TreeView/Types/Entity/Weblink.ts b/src/common/TreeStructure/TreeView/Types/Entity/Weblink.ts new file mode 100644 index 00000000..2ef1feeb --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/Entity/Weblink.ts @@ -0,0 +1,35 @@ +import { AbstractEntity } from './AbstractEntity'; + +export interface Weblink extends AbstractEntity { + adx_disablepagevalidation?: boolean; + adx_imageurl?: string; + adx_imagewidth?: number; + adx_displaypagechildlinks?: boolean; + adx_modifiedbyusername?: string; + adx_weblinksetidname?: string; + adx_displayorder: number; + adx_openinnewwindowname?: string; + adx_name: string; + adx_displayimageonly?: boolean; + adx_weblinksetid: string; + adx_imagealttext?: string; + adx_createdbyusername?: string; + adx_parentweblinkid?: string; + adx_modifiedbyipaddress?: string; + adx_robotsfollowlink: boolean; + adx_description?: string; + adx_publishingstateid?: string; + adx_disablepagevalidationname?: string; + adx_createdbyipaddress?: string; + adx_pageidname?: string; + adx_weblinkid: string; + adx_displaypagechildlinksname?: string; + adx_parentweblinkidname?: string; + adx_openinnewwindow: boolean; + adx_externalurl?: string; + adx_displayimageonlyname?: string; + adx_robotsfollowlinkname?: string; + adx_imageheight?: number; + adx_pageid?: string; + adx_publishingstateidname?: string; +} diff --git a/src/common/TreeStructure/TreeView/Types/Entity/WeblinkSet.ts b/src/common/TreeStructure/TreeView/Types/Entity/WeblinkSet.ts new file mode 100644 index 00000000..df9d2271 --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/Entity/WeblinkSet.ts @@ -0,0 +1,15 @@ +import { AbstractEntity } from './AbstractEntity'; + +export interface WeblinkSet extends AbstractEntity { + adx_publishingstateidname?: string; + adx_name: string; + adx_weblinksetid: string; + adx_copy?: string; + adx_websitelanguageidname?: string; + adx_websiteidname?: string; + adx_websitelanguageid: string; + adx_websiteid: string; + adx_display_name?: string; + adx_publishingstateid?: string; + adx_title: string; +} diff --git a/src/common/TreeStructure/TreeView/Types/Entity/Website.ts b/src/common/TreeStructure/TreeView/Types/Entity/Website.ts new file mode 100644 index 00000000..5102d4f7 --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/Entity/Website.ts @@ -0,0 +1,19 @@ +import { AbstractEntity } from './AbstractEntity'; + +export interface Website extends AbstractEntity { + adx_defaultlanguagename?: string; + adx_name?: string; + adx_defaultbotconsumerid?: string; + adx_defaultlanguage: string; + adx_partialurl?: string; + adx_headerwebtemplateid?: string; + adx_parentwebsiteidname?: string; + adx_headerwebtemplateidname?: string; + adx_websiteid: string; + adx_defaultbotconsumeridname?: string; + adx_website_language: number; + adx_primarydomainname?: string; + adx_footerwebtemplateid?: string; + adx_footerwebtemplateidname?: string; + adx_parentwebsiteid?: string; +} diff --git a/src/common/TreeStructure/TreeView/Types/View/WebPage.ts b/src/common/TreeStructure/TreeView/Types/View/WebPage.ts new file mode 100644 index 00000000..14017124 --- /dev/null +++ b/src/common/TreeStructure/TreeView/Types/View/WebPage.ts @@ -0,0 +1,22 @@ +/*! + * Copyright (C) Microsoft Corporation. All rights reserved. + */ + +import { WebTemplate } from '../Entity/WebTemplate'; + +export interface WebpageView { + baseURL: string; + runtimeCdnRootPath: string; + cssWebFiles: string[]; + runtimeJsAssets: string[]; + runtimeCssAssets: string[]; + fontStyles: string[]; +} + +export interface WebPageContent { + webTemplateContent: string; + header: WebTemplate; + footer: WebTemplate; + customJs?: string; + customCss?: string; +} diff --git a/src/common/TreeStructure/TreeView/Utils/Constant.ts b/src/common/TreeStructure/TreeView/Utils/Constant.ts new file mode 100644 index 00000000..409c5e1a --- /dev/null +++ b/src/common/TreeStructure/TreeView/Utils/Constant.ts @@ -0,0 +1,37 @@ +export enum ContextProperty { + WEBPAGE_YAML = '.webpage.yml', + WEBPAGE_COPY = '.webpage.copy.html', + WEBPAGE_CSS = '.webpage.custom_css.css', + WEBPAGE_JS = '.webpage.custom_javascript.js', + WEBPAGE_SUMMARY = '.webpage.summary.html', + CONTENT_SNIPPET_YAML = '.contentsnippet.yml', + CONTENT_SNIPPET_VALUE = '.contentsnippet.value.html', + WEB_TEMPLATE_YAML = '.webtemplate.yml', + WEB_TEMPLATE_SOURCE = '.webtemplate.source.html', + SITE_MARKER = 'sitemarker.yml', + SITE_SETTING = 'sitesetting.yml', + ENTITY_LIST = '.list.yml', + ENTITY_FORM = '.basicform.yml', + WEB_FORM = '.advancedform.yml', + WEB_LINK = '.weblink.yml', + WEB_LINK_SET = '.weblinkset.yml', + WEBSITE = 'website.yml', + PAGE_TEMPLATE = '.pagetemplate.yml', + UNKNOWN_PROPERTY = '' +} +export enum ContextPropertyKey { + WEBPAGE = 'adx_webpageid', + CONTENT_SNIPPET = 'adx_contentsnippetid', + WEB_TEMPLATE = 'adx_webtemplateid', + SITE_MARKER = 'adx_sitemarkerid', + SITE_SETTING = 'adx_sitesettingid', + ENTITY_LIST = 'adx_entitylistid', + ENTITY_FORM = 'adx_entityformid', + WEB_FORM = 'adx_webformid', + WEB_LINK = 'adx_weblinkid', + WEB_LINK_SET = 'adx_weblinksetid', + WEBSITE = 'adx_websiteid', + PAGE_TEMPLATE = 'adx_pagetemplateid', +} + +export const BootstrapSiteSetting = 'Site/BootstrapV5Enabled'; \ No newline at end of file diff --git a/src/common/TreeStructure/TreeView/Utils/IDataResolver.ts b/src/common/TreeStructure/TreeView/Utils/IDataResolver.ts new file mode 100644 index 00000000..d45256bd --- /dev/null +++ b/src/common/TreeStructure/TreeView/Utils/IDataResolver.ts @@ -0,0 +1,126 @@ + +import { ContentSnippet } from '../Types/Entity/ContentSnippet'; +import { EntityForm } from '../Types/Entity/EntityForm'; +import { EntityList } from '../Types/Entity/EntityList'; +import { PageTemplate } from '../Types/Entity/PageTemplate'; +import { SiteMarker } from '../Types/Entity/SiteMarker'; +import { SiteSetting } from '../Types/Entity/SiteSettings'; +import { WebForm } from '../Types/Entity/WebForm'; +import { Weblink } from '../Types/Entity/Weblink'; +import { WeblinkSet } from '../Types/Entity/WeblinkSet'; +import { PortalDrop } from '../PortalDrops/PortalDrops'; +import { WebpageView } from '../Types/View/WebPage'; +import { Webpage } from '../Types/Entity/WebPage'; +import { Website } from '../Types/Entity/Website'; +import { WebFile } from '../Types/Entity/WebFile'; +import { WebTemplate } from '../Types/Entity/WebTemplate'; +import { IEntityAttributeMetadata } from '../Types/Entity/EntityAttributeMetadata'; + +export type PortalEntity = + | Webpage + | ContentSnippet + | WebTemplate + | SiteSetting + | SiteMarker + | Website + | WeblinkSet + | WebFile + | Weblink; + +export interface IPreviewEngineContext { + webpages?: Webpage[]; + contentSnippets?: ContentSnippet[]; + webTemplates?: WebTemplate[]; + webFiles?: WebFile[]; + siteMarkers?: SiteMarker[]; + siteSettings?: SiteSetting[]; + entityLists?: EntityList[]; + entityForms?: EntityForm[]; + webForms?: WebForm[]; + weblinks?: Weblink[]; + weblinkSets?: WeblinkSet[]; + website?: Website; + pageTemplates?: PageTemplate[]; + dataResolverExtras?: { [key: string]: {} }; + resx?: { [key: string]: string }; + featureConfig?: Map; + entityAttributeMetadata?: IEntityAttributeMetadata[]; + lcid?: string; + isBootstrapV5?: boolean; +} + + +export enum RenderingMode { + VIEW = 'viewer', + EDIT = 'editor', +} + +export enum DebugMode { + OFF = 'OFF', + INFO = 'INFO', + ERROR = 'ERROR', + TRACE = 'TRACE', +} + +interface IEngineConfig { + strictFilters: boolean; + strictVariables: boolean; + isDynamicComponentCheck?: boolean; + addDynamicEntity?: (entityName: string, id: string, attributeName?: string) => void; + addFetchXMLExpression?: (varName: string, fetchXML: string) => void; + addPcfControl?: (controlName: string) => void; + addDynamicDrops?: (dropName: string) => void; +} + +export interface IPreviewEngineConfig { + renderingMode?: RenderingMode; + debugMode?: DebugMode; + engineConfig?: IEngineConfig; + websiteLanguageId?: string; + breadcrumb?: IBreadcrumbParams; +} + +export interface IBreadcrumbParams { + separator: string; + home_as: string; + tag: string; + textClass: string; +} + +export interface IDataResolver { + context?: IPreviewEngineContext; + config?: IPreviewEngineConfig; +} + +export interface IPortalDrops { + [key: string]: PortalDrop; +} + +export interface IPortalPreviewEngine { + renderWebpageById: ( + webpageId: string, + htmlMeta?: WebpageView, + beforeTagRender?: (tagName: string, entityName: string, entityId: string) => void, + dynamicContext?: {} + ) => string; + evaluateExpression: ( + expression: string, + webpageId?: string, + beforeTagRender?: (tagName: string, entityName: string, entityId: string) => void, + dynamicContext?: {} + ) => string; + renderWebpageByURL: ( + pageUrl: string, + htmlMeta?: WebpageView, + beforeTagRender?: (tagName: string, entityName: string, entityId: string) => void, + dynamicContext?: {} + ) => string; + updateContext: (context?: IPreviewEngineContext) => void; + updateConfig: (config?: IPreviewEngineConfig) => void; + getConfig: () => IPreviewEngineConfig; + renderPartialWebpageById: ( + webpageId?: string, + htmlMeta?: WebpageView, + beforeTagRender?: (tagName: string, entityName: string, entityId: string) => void + ) => { header: string; mainContent: string; footer: string }; +} diff --git a/src/common/TreeStructure/TreeViewProvider.ts b/src/common/TreeStructure/TreeViewProvider.ts new file mode 100644 index 00000000..9a5ca0ae --- /dev/null +++ b/src/common/TreeStructure/TreeViewProvider.ts @@ -0,0 +1,299 @@ +import * as vscode from 'vscode'; + +import * as path from "path"; +import { IItem } from './TreeView/Types/Entity/IItem'; + +class MyTreeItem extends vscode.TreeItem { + constructor(public readonly item: IItem, public readonly collapsibleState: vscode.TreeItemCollapsibleState) { + super(item.label, collapsibleState); + this.tooltip = `${this.item.title}`; + this.description = this.item.content; + this.iconPath = this.getIconPath(item); + this.contextValue = this.item.isFile ? 'file' : 'folder'; + this.command = this.getCommand(item); + } + + private getCommand(item: IItem): vscode.Command | undefined { + if (item.isFile && item.title === 'Source-Dependencies') { + return { + title: 'Item Clicked', + command: 'extension.itemClicked', + arguments: [item] + }; + } else if (item.isFile) { + return { + title: 'Open File', + command: 'extension.openFile', + arguments: [item] + }; + } + return undefined; + } + + private getIconPath(item: IItem): { light: string, dark: string } | vscode.ThemeIcon { + const basePath = path.join(__dirname, '..', 'src', 'client', 'portal_fileicons', 'icons'); + + if (item.isFile) { + switch (item.component) { + case "01": // HTML + return { + light: path.join(basePath, 'dark', 'html.svg'), + dark: path.join(basePath, 'dark', 'html.svg') + }; + case "02": // CSS + return { + light: path.join(basePath, 'dark', 'css.svg'), + dark: path.join(basePath, 'dark', 'css.svg') + }; + case "03": // JSON + return { + light: path.join(basePath, 'dark', 'js.svg'), + dark: path.join(basePath, 'dark', 'js.svg') + }; + case "04": // YML + return { + light: path.join(basePath, 'dark', 'yml.svg'), + dark: path.join(basePath, 'dark', 'yml.svg') + }; + case "05": // PNG + return { + light: path.join(basePath, 'dark', 'png.svg'), + dark: path.join(basePath, 'dark', 'png.svg') + }; + case "06": // Json + return { + light: path.join(basePath, 'light', 'json.svg'), + dark: path.join(basePath, 'dark', 'json.svg') + }; + case "09": // MP4 + return { + light: path.join(basePath, 'dark', 'mp4.svg'), + dark: path.join(basePath, 'dark', 'mp4.svg') + }; + case "10": // Text File + return new vscode.ThemeIcon('file'); + case "07": + case "08": + case "015": + case "017": + case "019": + return { + light: path.join(basePath, 'light', 'file-symlink-file.svg'), + dark: path.join(basePath, 'dark', 'file-symlink-file.svg') + }; + default: + return new vscode.ThemeIcon('file'); + } + } else { + switch (item.component) { + case "1": // Website + return { + light: path.join(basePath, 'light', 'website.svg'), + dark: path.join(basePath, 'dark', 'website.svg') + }; + case "2": // Webpage + return { + light: path.join(basePath, 'light', 'web_pages.svg'), + dark: path.join(basePath, 'dark', 'web_pages.svg') + }; + case "3": // Webfile + return { + light: path.join(basePath, 'light', 'web_files.svg'), + dark: path.join(basePath, 'dark', 'web_files.svg') + }; + case "4": // Weblink set + return { + light: path.join(basePath, 'light', 'weblink_sets.svg'), + dark: path.join(basePath, 'dark', 'weblink_sets.svg') + }; + case "5": // Weblink + return { + light: path.join(basePath, 'light', 'weblinks.svg'), + dark: path.join(basePath, 'dark', 'weblinks.svg') + }; + case "6": // Page template + return { + light: path.join(basePath, 'light', 'page_templates.svg'), + dark: path.join(basePath, 'dark', 'page_templates.svg') + }; + case "7": // Content snippet + return { + light: path.join(basePath, 'light', 'content_snippets.svg'), + dark: path.join(basePath, 'dark', 'content_snippets.svg') + }; + case "8": // Webtemplate + return { + light: path.join(basePath, 'light', 'web_templates.svg'), + dark: path.join(basePath, 'dark', 'web_templates.svg') + }; + case "9": // Site setting + return { + light: path.join(basePath, 'light', 'site_settings.svg'), + dark: path.join(basePath, 'dark', 'site_settings.svg') + }; + case "13": // Site marker + return { + light: path.join(basePath, 'light', 'site_markers.svg'), + dark: path.join(basePath, 'dark', 'site_markers.svg') + }; + case "15": // Basic Forms + return { + light: path.join(basePath, 'light', 'basic_forms.svg'), + dark: path.join(basePath, 'dark', 'basic_forms.svg') + }; + case "17": // Lists + return { + light: path.join(basePath, 'light', 'lists.svg'), + dark: path.join(basePath, 'dark', 'lists.svg') + }; + case "19": // Advanced Forms + return { + light: path.join(basePath, 'light', 'advanced_forms.svg'), + dark: path.join(basePath, 'dark', 'advanced_forms.svg') + }; + case "22": // Content Page + return { + light: path.join(basePath, 'light', 'book.svg'), + dark: path.join(basePath, 'dark', 'book.svg') + }; + case "23": // Page Copy + return { + light: path.join(basePath, 'light', 'list-flat.svg'), + dark: path.join(basePath, 'dark', 'list-flat.svg') + }; + case "24": // Page Summary + return { + light: path.join(basePath, 'light', 'note.svg'), + dark: path.join(basePath, 'dark', 'note.svg') + }; + case "25": // Subpage + return { + light: path.join(basePath, 'light', 'file-submodule.svg'), + dark: path.join(basePath, 'dark', 'file-submodule.svg') + }; + default: // Default folder icon + return { + light: path.join(basePath, 'light', 'folder.svg'), + dark: path.join(basePath, 'dark', 'folder.svg') + }; + } + } + } +} + +class MyTreeDataProvider implements vscode.TreeDataProvider { + private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); + readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; + + constructor(private readonly data: IItem[]) { } + + getTreeItem(element: MyTreeItem): vscode.TreeItem { + return element; + } + + getChildren(element?: MyTreeItem): Thenable { + if (element) { + return Promise.resolve(this.getItemChildren(element.item)); + } else { + return Promise.resolve(this.data.map(item => new MyTreeItem(item, vscode.TreeItemCollapsibleState.Collapsed))); + } + } + + private getItemChildren(item: IItem): MyTreeItem[] { + return item.children.map(child => new MyTreeItem(child, child.children.length > 0 ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None)); + } + + getParent(element: MyTreeItem): MyTreeItem | undefined { + const parentItem = this.findParentItem(this.data[0], element.item); + if (parentItem) { + return new MyTreeItem(parentItem, vscode.TreeItemCollapsibleState.Collapsed); + } + return undefined; + } + + private findParentItem(currentItem: IItem, targetItem: IItem): IItem | undefined { + if (!currentItem || !currentItem.children) { + return undefined; + } + const foundChild = currentItem.children.find(child => child.id === targetItem.id && child.title === targetItem.title); + if (foundChild) { + return currentItem; + } + for (const child of currentItem.children) { + const parentItem = this.findParentItem(child, targetItem); + if (parentItem) { + return parentItem; + } + } + + return undefined; + } + + findItemById(item: IItem, websiteIItem: IItem): IItem | undefined { + const comp = item.component.slice(1); + if (comp == '7') { + const contentSnipppetIItem = websiteIItem.children.find((child: IItem) => child.label === 'Content Snippets'); + return helper(item, contentSnipppetIItem); + } else if (comp == '8') { + const webTemplateIItem = websiteIItem.children.find((child: IItem) => child.label === 'Web Templates'); + return helper(item, webTemplateIItem); + } else if (comp == '15') { + const entityFormIItem = websiteIItem.children.find((child: IItem) => child.label === 'Basic Forms'); + return helper(item, entityFormIItem); + } else if (comp == '17') { + const listsIItem = websiteIItem.children.find((child: IItem) => child.label === 'Lists'); + return helper(item, listsIItem); + } else if (comp == '19') { + const webformIItem = websiteIItem.children.find((child: IItem) => child.label === 'Advanced Forms'); + return helper(item, webformIItem); + } else { + return undefined; + } + } +} +function helper(item: IItem, entityIItem: any) { + for (const child of entityIItem.children) { + if (child.id == item.id) { + return child; + } + } + return undefined; +} +export function createTree(websiteIItem: IItem) { + const treeDataProvider = new MyTreeDataProvider([websiteIItem]); + const treeView = vscode.window.createTreeView('exampleView', { treeDataProvider }); + + vscode.commands.registerCommand('extension.openWebpage', (webpageName: string) => { + vscode.window.showInformationMessage(`Opening Webpage: ${webpageName}`); + }); + + vscode.commands.registerCommand('extension.openFile', async (item: IItem) => { + try { + if (item.path) { + const pathString = item.path.fsPath.toLowerCase(); + + if (pathString.endsWith('.html') || pathString.endsWith('.css') || pathString.endsWith('.js') || pathString.endsWith('.json') || pathString.endsWith('.yml')) { + const document = await vscode.workspace.openTextDocument(item.path); + await vscode.window.showTextDocument(document); + } else if (pathString.endsWith('.png') || pathString.endsWith('.jpg') || pathString.endsWith('.jpeg') || pathString.endsWith('.gif') || pathString.endsWith('.mp4')) { + await vscode.commands.executeCommand('vscode.open', item.path); + } else { + const document = await vscode.workspace.openTextDocument(item.path); + await vscode.window.showTextDocument(document); + } + } else { + const document = await vscode.workspace.openTextDocument({ content: item.content, language: 'plaintext' }); + await vscode.window.showTextDocument(document); + } + } catch (error) { + vscode.window.showErrorMessage(`${item.title} ${item.id} does not exist`); + } + }); + vscode.commands.registerCommand('extension.itemClicked', (item: IItem) => { + const foundItem = treeDataProvider.findItemById(item, websiteIItem); + if (foundItem && !foundItem.isFile) { + const treeItem = new MyTreeItem(foundItem, foundItem.children.length > 0 ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None); + treeView.reveal(treeItem, { focus: true, expand: true }); + } + }); +} \ No newline at end of file diff --git a/src/common/TreeStructure/dataMapperServices/DefaultPortalComponentService.ts b/src/common/TreeStructure/dataMapperServices/DefaultPortalComponentService.ts new file mode 100644 index 00000000..19bbb654 --- /dev/null +++ b/src/common/TreeStructure/dataMapperServices/DefaultPortalComponentService.ts @@ -0,0 +1,87 @@ +/*! + * Copyright (C) Microsoft Corporation. All rights reserved. + */ +import { IItem } from "../TreeView/Types/Entity/IItem"; +import { IPortalComponentService } from "./IPortalComponentService"; +import * as vscode from 'vscode'; + +interface PortalComponentConfig { + type: string; + idField: string; + nameField: string; + url: string; + fileType: string, + comp: string, + getItems: (metadataContext: any) => any[]; +} + +export class DefaultPortalComponentService implements IPortalComponentService { + private config: PortalComponentConfig; + + constructor(config: PortalComponentConfig) { + this.config = config; + } + + create(metadataContext: any, getPath?: any): IItem[] { + const items: IItem[] = []; + const components = this.config.getItems(metadataContext); + + if (!components) { + return items; + } + + for (const component of components) { + const name = component[this.config.nameField]; + const id = component[this.config.idField]; + const type = this.config.type; + const url = this.config.url; + const filetype = this.config.fileType; + const comp = this.config.comp; + let x = name.replace(/[/\s]+/g, '-'); + let y = x.toLowerCase(); + let c = '01'; + if (type == 'lists') { + y = ''; + } + if (type == 'basic-forms') { + x = y; + } + if (filetype == 'js') { + c = '03'; + } + if (filetype == 'yml') { + c = '04' + } + const children: IItem[] = [ + { + label: `${x}${url}`, + title: `${name}.${filetype}`, + id: `${id}`, + isFile: true, + content: '', + path: vscode.Uri.file(`${getPath.path}/${type}/${y}/${x}${url}`), + component: c, + children: [], + error: "", + parentList:[] + } + ]; + + const item: IItem = { + label: name, + title: type, + id: id, + isFile: false, + content: '', + path: vscode.Uri.parse(`${getPath.path}/${type}/${y}`), + component: comp, + children: children, + error: "", + parentList:[] + }; + + items.push(item); + } + return items; + } +} \ No newline at end of file diff --git a/src/common/TreeStructure/dataMapperServices/IPortalComponentService.ts b/src/common/TreeStructure/dataMapperServices/IPortalComponentService.ts new file mode 100644 index 00000000..7d005dc5 --- /dev/null +++ b/src/common/TreeStructure/dataMapperServices/IPortalComponentService.ts @@ -0,0 +1,16 @@ +/*! + * Copyright (C) Microsoft Corporation. All rights reserved. + */ +import { IItem } from "../TreeView/Types/Entity/IItem"; + +export interface IPortalComponentService { + /** + * Save source value of the component and refresh component if save successful. + * @param id: Id of component + * @param value: source code value + * @param element Outer HTML element for portal component + * @param subComponent one of many dependent on its parent component service for saving + */ + create(metadataContext: any, getPath?: any): IItem[]; + +} \ No newline at end of file diff --git a/src/common/TreeStructure/dataMapperServices/PortalComponentServiceFactory.ts b/src/common/TreeStructure/dataMapperServices/PortalComponentServiceFactory.ts new file mode 100644 index 00000000..b608ce0e --- /dev/null +++ b/src/common/TreeStructure/dataMapperServices/PortalComponentServiceFactory.ts @@ -0,0 +1,39 @@ +/*! + * Copyright (C) Microsoft Corporation. All rights reserved. + */ +import { IPortalComponentService } from "./IPortalComponentService"; +import { WebPageService } from "./WebPageService"; +import { DefaultPortalComponentService } from "./DefaultPortalComponentService"; +import { WebFileService } from './WebFileService' +import { contentSnippetConfig, webTemplateConfig, listConfig, entityFormConfig, webFormConfig } from "./portalComponentConfigs"; + +export class PortalComponentServiceFactory { + static getPortalComponent(componentType: string): IPortalComponentService | null { + switch (componentType) { + case "WebPage": { + return new WebPageService(); + } + case "WebFile": { + return new WebFileService(); + } + case "WebTemplate": { + return new DefaultPortalComponentService(webTemplateConfig); + } + case "Content Snippet": { + return new DefaultPortalComponentService(contentSnippetConfig); + } + case "List": { + return new DefaultPortalComponentService(listConfig); + } + case "EntityForm": { + return new DefaultPortalComponentService(entityFormConfig); + } + case "WebForm": { + return new DefaultPortalComponentService(webFormConfig); + } + default: { + return null; + } + } + } +} \ No newline at end of file diff --git a/src/common/TreeStructure/dataMapperServices/WebFileService.ts b/src/common/TreeStructure/dataMapperServices/WebFileService.ts new file mode 100644 index 00000000..c211d9e8 --- /dev/null +++ b/src/common/TreeStructure/dataMapperServices/WebFileService.ts @@ -0,0 +1,68 @@ +/*! + * Copyright (C) Microsoft Corporation. All rights reserved. + */ +import { IPortalComponentService } from "./IPortalComponentService"; +import { IItem } from "../TreeView/Types/Entity/IItem"; +import { WebFile } from "../TreeView/Types/Entity/WebFile"; +import * as vscode from 'vscode'; + +export class WebFileService implements IPortalComponentService { + create(metadataContext: any, getPath?: any): IItem[] { + const items: IItem[] = []; + const webFile: WebFile[] | undefined = metadataContext.webFiles; + + if (!webFile) { + return items; + } + for (const file of webFile) { + let c = ''; + if (file.adx_name.endsWith(".css")) { + c = '02'; + } else if (file.adx_name.endsWith(".json")) { + c = '06'; + } else if (file.adx_name.endsWith(".mp4")) { + c = '09'; + file.adx_name=file.adx_name.replace(/\s+/g, '-'); + } else if (file.adx_name.endsWith(".html")) { + c = '01'; + } else if (file.adx_name.endsWith(".png")) { + c = '05'; + } else if (file.adx_name.endsWith(".js")) { + c = '03'; + }else{ + c='10'; + file.adx_name=file.adx_name.replace(/\s+/g, '-'); + } + const item: IItem = { + label: file.adx_name, + title: file.adx_name, + id: file.adx_webfileid, + isFile: true, + content: '', + path: vscode.Uri.file(`${getPath.path}/web-files/${file.adx_name}`), + component: c, + children: [], + error: "", + parentList:[] + }; + if(file.adx_name.endsWith(".html") || file.adx_name.endsWith(".js")){ + const fileItem: IItem = { + label: file.filename, + title: 'webFile', + id: file.adx_webfileid, + isFile: false, + content: '', + path: vscode.Uri.file(`/web-files`), + component: c, + children: [item], + error: "", + parentList:[] + }; + items.push(fileItem); + }else{ + items.push(item); + } + } + return items; + } +} \ No newline at end of file diff --git a/src/common/TreeStructure/dataMapperServices/WebPageService.ts b/src/common/TreeStructure/dataMapperServices/WebPageService.ts new file mode 100644 index 00000000..ea33d5ea --- /dev/null +++ b/src/common/TreeStructure/dataMapperServices/WebPageService.ts @@ -0,0 +1,95 @@ +/*! + * Copyright (C) Microsoft Corporation. All rights reserved. + */ +import { IPortalComponentService } from "./IPortalComponentService"; +import { IItem } from "../TreeView/Types/Entity/IItem"; +import { Webpage } from '../TreeView/Types/Entity/WebPage'; +import * as vscode from 'vscode'; + +export const contentPage: Webpage[] = []; +export class WebPageService implements IPortalComponentService { + create(metadataContext: any, getPath?: any): IItem[] { + const items: IItem[] = []; + const webpages: Webpage[] | undefined = metadataContext.webpages; + + if (!webpages) { + return items; + } + + for (const webpage of webpages) { + if (!webpage.adx_webpagelanguageid) { + const str = webpage.adx_name; + let x = str.replace(/\s+/g, '-'); + let y = x.toLowerCase(); + const [pageCopy, cssItem, jsItem, pageSummary] = createCopyItems(webpage, getPath, y, x); + const webpageItem = createItem(webpage.adx_name, webpage.adx_name, webpage.adx_webpageid, false, vscode.Uri.parse(`/${webpage.adx_name}`), "/2", [pageCopy, cssItem, jsItem, pageSummary]); + items.push(webpageItem); + } else { + contentPage.push(webpage); + } + } + + items.forEach(item => { + webpages.forEach(webpage => { + if (item.id === webpage.adx_parentpageid) { + const subItem = items.find(it => webpage.adx_webpageid === it.id); + if (subItem) { + let subpageItem = item.children.find(child => child.label === "Subpage"); + if (!subpageItem) { + subpageItem = createItem('Subpage', 'Subpage', '', false, vscode.Uri.parse(`/Subpage`), "25", [subItem]); + item.children.push(subpageItem); + } else { + subpageItem.children.push(subItem); + } + } + } + }); + }); + + for (const contentpg of contentPage) { + const str = contentpg.adx_name; + let x = str.replace(/\s+/g, '-'); + let y = x.toLowerCase(); + const [pageCopy, cssItem, jsItem, pageSummary] = createCopyItems(contentpg, getPath, y, x, '.en-US', '/content-pages'); + const contentPageItem = createItem(`Content Page`, `Content Page`, `${contentpg.adx_webpageid}_content`, false, vscode.Uri.file(`${contentpg.adx_name}/Content`), "22", [pageCopy, cssItem, jsItem, pageSummary]); + + items.forEach(item => { + if (item.title === contentpg.adx_name) { + const x = item.children.find(child => child.label === "Content Page"); + if (!x) { + item.children.push(contentPageItem); + } + } + }); + } + return items; + } + +} + +function createItem(label: string, title: string, id: string, isFile: boolean, path: vscode.Uri, component: string, children: IItem[] = [], content: string = '', error: string = '', parentList: IItem[] = []): IItem { + return { + label, + title, + id, + isFile, + content, + path, + component, + children, + error, + parentList, + }; +} + +function createCopyItems(webpage: Webpage, getPath: any, y: string, x: string, langSuffix: string = '', content: string = ''): IItem[] { + const basePath = `${getPath.path}/web-pages/${y}${content}/${x}${langSuffix}`; + const copyItem = createItem(`${x}${langSuffix}.webpage.copy.html`, `${x}${langSuffix}.webpage.copy.html`, `${x}${langSuffix}.webpage.copy.html`, true, vscode.Uri.file(`${basePath}.webpage.copy.html`), "01"); + const cssItem = createItem(`${x}${langSuffix}.webpage.custom_css.css`, `${x}${langSuffix}.webpage.custom_css.css`, `${webpage.adx_webpageid}_css`, true, vscode.Uri.file(`${basePath}.webpage.custom_css.css`), "02"); + const jsItem = createItem(`${x}${langSuffix}.webpage.custom_javascript.js`, `${x}${langSuffix}.webpage.custom_javascript.js`, `${webpage.adx_webpageid}_js`, true, vscode.Uri.file(`${basePath}.webpage.custom_javascript.js`), "03"); + const summaryItem = createItem(`${x}${langSuffix}.webpage.summary.html`, `${x}${langSuffix}.webpage.summary.html`, `${x}${langSuffix}.webpage.summary.html`, true, vscode.Uri.file(`${basePath}.webpage.summary.html`), "01"); + const pageCopy = createItem(`Page Copy`, `Page Copy`, `Page_copy`, false, vscode.Uri.file(`/pagecopy`), "23", [copyItem]); + const pageSummary = createItem(`Page Summary`, `Page Summary`, `Page_Summary`, false, vscode.Uri.file(`/pageSummary`), "24", [summaryItem]); + + return [pageCopy, cssItem, jsItem, pageSummary]; +} \ No newline at end of file diff --git a/src/common/TreeStructure/dataMapperServices/portalComponentConfigs.ts b/src/common/TreeStructure/dataMapperServices/portalComponentConfigs.ts new file mode 100644 index 00000000..694ac7f4 --- /dev/null +++ b/src/common/TreeStructure/dataMapperServices/portalComponentConfigs.ts @@ -0,0 +1,52 @@ +/*! + * Copyright (C) Microsoft Corporation. All rights reserved. + */ +export const contentSnippetConfig = { + type: "content-snippets", + idField: "adx_contentsnippetid", + nameField: "adx_name", + url: ".en-US.contentsnippet.value.html", + fileType: "html", + comp: "/7", + getItems: (metadataContext: any) => metadataContext.contentSnippets +}; + +export const webTemplateConfig = { + type: "web-templates", + idField: "adx_webtemplateid", + nameField: "adx_name", + url: ".webtemplate.source.html", + fileType: "html", + comp: "/8", + getItems: (metadataContext: any) => metadataContext.webTemplates, +}; + +export const listConfig = { + type: "lists", + idField: "adx_entitylistid", + nameField: "adx_name", + url: ".list.custom_javascript.js", + fileType: "js", + comp: "/17", + getItems: (metadataContext: any) => metadataContext.entityLists, +}; + +export const entityFormConfig = { + type: "basic-forms", + idField: "adx_entityformid", + nameField: "adx_name", + url: ".basicform.custom_javascript.js", + fileType: "js", + comp: "/15", + getItems: (metadataContext: any) => metadataContext.entityForms, +}; + +export const webFormConfig = { + type: "advanced-forms", + idField: "adx_webformid", + nameField: "adx_name", + url: ".advancedform.yml", + fileType: "yml", + comp: "/9", + getItems: (metadataContext: any) => metadataContext.webForms, +}; \ No newline at end of file