UNPKG

sfdx-hardis

Version:

Swiss-army-knife Toolbox for Salesforce. Allows you to define a complete CD/CD Pipeline. Orchestrate base commands and assist users with interactive wizards

171 lines (164 loc) • 8.81 kB
/* jscpd:ignore-start */ import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; import { Messages } from '@salesforce/core'; import c from 'chalk'; import { glob } from 'glob'; import sortArray from 'sort-array'; import { uxLog } from '../../../../common/utils/index.js'; import { soqlQueryTooling } from '../../../../common/utils/apiUtils.js'; import { prompts } from '../../../../common/utils/prompts.js'; import { parseXmlFile, writeXmlFile } from '../../../../common/utils/xmlUtils.js'; import { GLOB_IGNORE_PATTERNS } from '../../../../common/utils/projectUtils.js'; Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('sfdx-hardis', 'org'); export default class FixV53Flexipages extends SfCommand { static title = 'Fix profiles to add tabs that are not retrieved by SF CLI'; static description = ` ## Command Behavior **Interactively updates tab visibility settings in Salesforce profiles, addressing a common issue where tab visibilities are not correctly retrieved by \`sf project retrieve start\`.** This command provides a user-friendly interface to manage tab settings within your profile XML files, ensuring that your local project accurately reflects the intended tab configurations in your Salesforce org. Key functionalities: - **Interactive Tab Selection:** Displays a multi-select menu of all available tabs in your org, allowing you to choose which tabs to update. - **Visibility Control:** Lets you set the visibility for the selected tabs to either \`DefaultOn\` (Visible) or \`Hidden\`. - **Profile Selection:** Presents a multi-select menu of all .profile-meta.xml files in your project, allowing you to apply the tab visibility changes to specific profiles. - **XML Updates:** Modifies the <tabVisibilities> section of the selected profile XML files to reflect the chosen tab settings. If a tab visibility setting already exists for a selected tab, it will be updated; otherwise, a new one will be added. - **Sorted Output:** The <tabVisibilities> in the updated profile XML files are sorted alphabetically for consistency and readability. <details markdown="1"> <summary>Technical explanations</summary> The command's technical implementation involves: - **SOQL Queries (Tooling API):** It queries the \`TabDefinition\` object using \`soqlQueryTooling\` to retrieve a list of all available tabs in the target org. - **File Discovery:** Uses \`glob\` to find all .profile-meta.xml files within the specified project path. - **Interactive Prompts:** Leverages the \`prompts\` library to create interactive menus for selecting tabs, visibility settings, and profiles. - **XML Parsing and Manipulation:** Uses \`parseXmlFile\` to read the content of profile XML files and \`writeXmlFile\` to write the modified content back. It manipulates the \`tabVisibilities\` array within the parsed XML to add or update tab settings. - **Array Sorting:** Employs the \`sort-array\` library to sort the \`tabVisibilities\` alphabetically by tab name. - **Logging:** Provides feedback to the user about which profiles have been updated and a summary of the changes. </details> `; static examples = ['$ sf hardis:project:fix:profiletabs']; static flags = { path: Flags.string({ char: 'p', default: process.cwd(), description: 'Root folder', }), debug: Flags.boolean({ char: 'd', default: false, description: messages.getMessage('debugMode'), }), websocket: Flags.string({ description: messages.getMessage('websocket'), }), skipauth: Flags.boolean({ description: 'Skip authentication check when a default username is required', }), 'target-org': requiredOrgFlagWithDeprecations, }; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default static requiresProject = true; pathToBrowse; debugMode = false; async run() { const { flags } = await this.parse(FixV53Flexipages); this.pathToBrowse = flags.path || process.cwd(); this.debugMode = flags.debug || false; /* jscpd:ignore-end */ // List available tabs in org const tabsRequest = 'SELECT Label,DurableId,Name,SobjectName FROM TabDefinition ORDER BY Label'; const tabsResult = await soqlQueryTooling(tabsRequest, flags['target-org'].getConnection()); const choices = tabsResult.records.map((tab) => { return { title: `${tab.Label} (${tab.Name} on SObject ${tab.SobjectName})}`, value: tab.Name, }; }); // Prompt tabs to add to Profiles const promptTabsToAdd = await prompts([ { type: 'multiselect', name: 'tabs', message: 'Please select the tabs you want to display or hide in Profile(s)', description: 'Choose which tabs should be configured for profiles', choices: choices, }, { type: 'select', name: 'visibility', message: 'Please select the flag you want the tabs to be applied on profiles you will select', description: 'Choose the visibility setting for the selected tabs', placeholder: 'Select visibility', choices: [ { title: 'Visible (DefaultOn)', value: 'DefaultOn', }, { title: 'Hidden', value: 'Hidden', }, ], }, ]); const tabsToUpdate = promptTabsToAdd.tabs; const visibility = promptTabsToAdd.visibility; // Prompt profiles to user const globPattern = this.pathToBrowse + `/**/*.profile-meta.xml`; const profileSourceFiles = await glob(globPattern, { cwd: this.pathToBrowse, ignore: GLOB_IGNORE_PATTERNS }); const promptProfilesToUpdate = await prompts({ type: 'multiselect', name: 'profiles', message: 'Please select the profiles you want to update to apply tabs [' + tabsToUpdate.join(', ') + '] with visibility ' + visibility, description: 'Choose which profiles should receive the tab visibility updates', choices: profileSourceFiles.map((profileFile) => { return { title: (profileFile.replace(/\\/g, '/').split('/').pop() || '').replace('.profile-meta.xml', ''), value: profileFile, }; }), }); // Apply updates on Profiles for (const profileFile of promptProfilesToUpdate.profiles) { const profile = await parseXmlFile(profileFile); let tabVisibilities = profile.Profile['tabVisibilities'] || []; for (const tabName of tabsToUpdate) { // Update existing tabVisibility if (tabVisibilities.filter((tabVisibility) => tabVisibility.tab[0] === tabName).length > 0) { tabVisibilities = tabVisibilities.map((tabVisibility) => { if (tabVisibility.tab[0] === tabName) { tabVisibility.visibility = [visibility]; } return tabVisibility; }); continue; } //Add new visibility tabVisibilities.push({ tab: [tabName], visibility: [visibility], }); } // Sort items by name const sortedTabVisibility = sortArray(tabVisibilities.map((tabVisibility) => { return { key: tabVisibility.tab[0], value: tabVisibility, }; }), { by: ['key'], order: ['asc'], }).map((sorted) => sorted.value); profile.Profile['tabVisibilities'] = sortedTabVisibility; // Update Profile XML File await writeXmlFile(profileFile, profile); uxLog("log", this, c.grey('Updated ' + profileFile)); } // Summary const msg = `Updated ${c.green(c.bold(promptProfilesToUpdate.profiles.length))} profiles.`; uxLog("action", this, c.cyan(msg)); // Return an object to be displayed with --json return { outputString: msg, updatedNumber: promptProfilesToUpdate.profiles.length }; } } //# sourceMappingURL=profiletabs.js.map