11import { readFileSync , writeFileSync } from 'fs' ;
22import { Folders } from '../check/Folders' ;
33import { commands , Progress , ProgressLocation , Uri , window , workspace , WorkspaceFolder } from 'vscode' ;
4- import { Commands , ContextKeys , WebViewType , WebviewCommand , WorkflowType } from '../../constants' ;
4+ import { Commands , WebViewType , WebviewCommand , WorkflowType } from '../../constants' ;
55import { AppCatalogApp , GenerateWorkflowCommandInput , SiteAppCatalog , SolutionAddResult , Subscription } from '../../models' ;
66import { Extension } from '../dataType/Extension' ;
77import { CliExecuter } from '../executeWrappers/CliCommandExecuter' ;
@@ -16,9 +16,10 @@ import { parseYoRc } from '../../utils/parseYoRc';
1616import { parseCliCommand } from '../../utils/parseCliCommand' ;
1717import { CertificateActions } from './CertificateActions' ;
1818import path = require( 'path' ) ;
19- import { ActionTreeItem } from '../../providers/ActionTreeDataProvider' ;
2019import { getExtensionSettings } from '../../utils/getExtensionSettings' ;
2120import * as fs from 'fs' ;
21+ import { ActionTreeItem } from '../../providers/ActionTreeDataProvider' ;
22+
2223
2324export class CliActions {
2425
@@ -43,42 +44,6 @@ export class CliActions {
4344 subscriptions . push (
4445 commands . registerCommand ( Commands . pipeline , CliActions . showGenerateWorkflowForm )
4546 ) ;
46- subscriptions . push (
47- commands . registerCommand ( Commands . deployAppCatalogApp , ( node : ActionTreeItem ) =>
48- CliActions . toggleAppDeployed ( node , ContextKeys . deployApp , 'deploy' )
49- )
50- ) ;
51- subscriptions . push (
52- commands . registerCommand ( Commands . retractAppCatalogApp , ( node : ActionTreeItem ) =>
53- CliActions . toggleAppDeployed ( node , ContextKeys . retractApp , 'retract' )
54- )
55- ) ;
56- subscriptions . push (
57- commands . registerCommand ( Commands . removeAppCatalogApp , CliActions . removeAppCatalogApp )
58- ) ;
59- subscriptions . push (
60- commands . registerCommand ( Commands . enableAppCatalogApp , ( node : ActionTreeItem ) =>
61- CliActions . toggleAppEnabled ( node , ContextKeys . enableApp , 'enable' )
62- )
63- ) ;
64- subscriptions . push (
65- commands . registerCommand ( Commands . disableAppCatalogApp , ( node : ActionTreeItem ) =>
66- CliActions . toggleAppEnabled ( node , ContextKeys . disableApp , 'disable' )
67- )
68- ) ;
69- subscriptions . push (
70- commands . registerCommand ( Commands . installAppCatalogApp , ( node : ActionTreeItem ) =>
71- CliActions . toggleAppInstalled ( node , ContextKeys . installApp , 'install' )
72- )
73- ) ;
74- subscriptions . push (
75- commands . registerCommand ( Commands . uninstallAppCatalogApp , ( node : ActionTreeItem ) =>
76- CliActions . toggleAppInstalled ( node , ContextKeys . uninstallApp , 'uninstall' )
77- )
78- ) ;
79- subscriptions . push (
80- commands . registerCommand ( Commands . upgradeAppCatalogApp , CliActions . upgradeAppCatalogApp )
81- ) ;
8247 subscriptions . push (
8348 commands . registerCommand ( Commands . setFormCustomizer , CliActions . setFormCustomizer )
8449 ) ;
@@ -152,312 +117,6 @@ export class CliActions {
152117 }
153118 }
154119
155- /**
156- * Deploys or retracts the app in the tenant or site app catalog.
157- *
158- * @param node The tree item representing the app to be deployed or retracted.
159- * @param ctxValue The context value used to identify the action node.
160- * @param action The action to be performed: 'deploy' or 'retract'.
161- */
162- public static async toggleAppDeployed ( node : ActionTreeItem , ctxValue : string , action : 'deploy' | 'retract' ) {
163- try {
164- const actionNode = node . children ?. find ( child => child . contextValue === ctxValue ) ;
165-
166- if ( ! actionNode ?. command ?. arguments ) {
167- Notifications . error ( `Failed to retrieve app details for ${ action } .` ) ;
168- return ;
169- }
170-
171- const [ appID , appTitle , appCatalogUrl , deployed ] = actionNode . command . arguments ;
172-
173- if ( action === 'deploy' && deployed ) {
174- Notifications . info ( `App '${ appTitle } ' is already deployed.` ) ;
175- return ;
176- }
177-
178- if ( action === 'retract' && ! deployed ) {
179- Notifications . info ( `App '${ appTitle } ' is already retracted.` ) ;
180- return ;
181- }
182-
183- const commandOptions : any = {
184- id : appID ,
185- ...( action === 'retract' && { force : true } ) ,
186- ...( appCatalogUrl ?. trim ( ) && {
187- appCatalogScope : 'sitecollection' ,
188- appCatalogUrl : appCatalogUrl
189- } )
190- } ;
191-
192- const cliCommand = action === 'deploy' ? 'spo app deploy' : 'spo app retract' ;
193- await CliExecuter . execute ( cliCommand , 'json' , commandOptions ) ;
194- Notifications . info ( `App '${ appTitle } ' has been successfully ${ action === 'deploy' ? 'deployed' : 'retracted' } .` ) ;
195-
196- // refresh the environmentTreeView
197- await commands . executeCommand ( 'spfx-toolkit.refreshAppCatalogTreeView' ) ;
198- } catch ( e : any ) {
199- const message = e ?. error ?. message ;
200- Notifications . error ( message ) ;
201- }
202- }
203-
204- /**
205- * Removes an app from the tenant or site app catalog.
206- *
207- * @param node The tree item representing the app to be removed.
208- */
209- public static async removeAppCatalogApp ( node : ActionTreeItem ) {
210- try {
211- const actionNode = node . children ?. find ( child => child . contextValue === ContextKeys . removeApp ) ;
212-
213- if ( ! actionNode ?. command ?. arguments ) {
214- Notifications . error ( 'Failed to retrieve app details for removal.' ) ;
215- return ;
216- }
217-
218- const [ appID , appTitle , appCatalogUrl ] = actionNode . command . arguments ;
219-
220- const shouldRemove = await window . showQuickPick ( [ 'Yes' , 'No' ] , {
221- title : `Are you sure you want to remove the app '${ appTitle } ' from the app catalog?` ,
222- ignoreFocusOut : true ,
223- canPickMany : false
224- } ) ;
225-
226- const shouldRemoveAnswer = shouldRemove === 'Yes' ;
227-
228- if ( ! shouldRemoveAnswer ) {
229- return ;
230- }
231-
232- const commandOptions : any = {
233- id : appID ,
234- force : true ,
235- ...( appCatalogUrl ?. trim ( ) && {
236- appCatalogScope : 'sitecollection' ,
237- appCatalogUrl : appCatalogUrl
238- } )
239- } ;
240-
241- await CliExecuter . execute ( 'spo app remove' , 'json' , commandOptions ) ;
242- Notifications . info ( `App '${ appTitle } ' has been successfully removed.` ) ;
243-
244- // refresh the environmentTreeView
245- await commands . executeCommand ( 'spfx-toolkit.refreshAppCatalogTreeView' ) ;
246- } catch ( e : any ) {
247- const message = e ?. error ?. message ;
248- Notifications . error ( message ) ;
249- }
250- }
251-
252- /**
253- * Upgrades an app to a newer version available in the app catalog.
254- *
255- * @param node The tree item representing the app to be upgraded.
256- */
257- public static async upgradeAppCatalogApp ( node : ActionTreeItem ) {
258- try {
259- const actionNode = node . children ?. find ( child => child . contextValue === ContextKeys . upgradeApp ) ;
260-
261- if ( ! actionNode ?. command ?. arguments ) {
262- Notifications . error ( 'Failed to retrieve app details for upgrade.' ) ;
263- return ;
264- }
265-
266- const [ appID , appTitle , appCatalogUrl , isTenantApp ] = actionNode . command . arguments ;
267-
268- let siteUrl : string = appCatalogUrl ;
269-
270- if ( isTenantApp ) {
271- const relativeUrl = await window . showInputBox ( {
272- prompt : 'Enter the relative URL of the site to upgrade the app in' ,
273- placeHolder : 'e.g., sites/sales or leave blank for root site' ,
274- validateInput : ( input ) => {
275- const trimmedInput = input . trim ( ) ;
276-
277- if ( trimmedInput . startsWith ( 'https://' ) ) {
278- return 'Please provide a relative URL, not an absolute URL.' ;
279- }
280- if ( trimmedInput . startsWith ( '/' ) ) {
281- return 'Please provide a relative URL without a leading slash.' ;
282- }
283-
284- return undefined ;
285- }
286- } ) ;
287-
288- if ( relativeUrl === undefined ) {
289- Notifications . warning ( 'No site URL provided. App upgrade aborted.' ) ;
290- return ;
291- }
292-
293- siteUrl = `${ new URL ( appCatalogUrl ) . origin } /${ relativeUrl . trim ( ) } ` ;
294- }
295-
296- const commandOptions : any = {
297- id : appID ,
298- ...( isTenantApp
299- ? { siteUrl }
300- : { appCatalogScope : 'sitecollection' , siteUrl } )
301- } ;
302-
303- await CliExecuter . execute ( 'spo app upgrade' , 'json' , commandOptions ) ;
304- Notifications . info ( `App '${ appTitle } ' has been successfully upgraded on site '${ siteUrl } '.` ) ;
305- } catch ( e : any ) {
306- const message = e ?. message || 'An unexpected error occurred during the app upgrade.' ;
307- Notifications . error ( message ) ;
308- }
309- }
310-
311- /**
312- * Enables or disables the app in the tenant or site app catalog.
313- *
314- * @param node The tree item representing the app to be deployed or retracted.
315- * @param ctxValue The context value used to identify the action node.
316- * @param action The action to be performed: 'enable' or 'disable'.
317- */
318- public static async toggleAppEnabled ( node : ActionTreeItem , ctxValue : string , action : 'enable' | 'disable' ) {
319- try {
320- const actionNode = node . children ?. find ( child => child . contextValue === ctxValue ) ;
321-
322- if ( ! actionNode ?. command ?. arguments ) {
323- Notifications . error ( `Failed to retrieve app details for ${ action } .` ) ;
324- return ;
325- }
326-
327- const [ appTitle , appCatalogUrl , isEnabled ] = actionNode . command . arguments ;
328-
329- if ( action === 'enable' && isEnabled ) {
330- Notifications . info ( `App '${ appTitle } ' is already enabled.` ) ;
331- return ;
332- }
333-
334- if ( action === 'disable' && ! isEnabled ) {
335- Notifications . info ( `App '${ appTitle } ' is already disabled.` ) ;
336- return ;
337- }
338-
339- const appProductIdFilter = `Title eq '${ appTitle } '` ;
340- const commandOptionsList : any = {
341- listTitle : 'Apps for SharePoint' ,
342- webUrl : appCatalogUrl ,
343- fields : 'Id, Title, IsAppPackageEnabled' ,
344- filter : appProductIdFilter
345- } ;
346-
347- const listItemsResponse = await CliExecuter . execute ( 'spo listitem list' , 'json' , commandOptionsList ) ;
348- const listItems = JSON . parse ( listItemsResponse . stdout || '[]' ) ;
349-
350- if ( listItems . length === 0 ) {
351- Notifications . error ( `App '${ appTitle } ' not found in the app catalog.` ) ;
352- return ;
353- }
354-
355- const appListItemId = listItems [ 0 ] . Id ;
356-
357- const commandOptionsSet : any = {
358- listTitle : 'Apps for SharePoint' ,
359- id : appListItemId ,
360- webUrl : appCatalogUrl ,
361- IsAppPackageEnabled : ! isEnabled ? true : false
362- } ;
363-
364- await CliExecuter . execute ( 'spo listitem set' , 'json' , commandOptionsSet ) ;
365- Notifications . info ( `App '${ appTitle } ' has been successfully ${ action === 'enable' ? 'enabled' : 'disabled' } .` ) ;
366-
367- // refresh the environmentTreeView
368- await commands . executeCommand ( 'spfx-toolkit.refreshAppCatalogTreeView' ) ;
369- } catch ( e : any ) {
370- const message = e ?. error ?. message ;
371- Notifications . error ( message ) ;
372- }
373- }
374-
375- /**
376- * Installs or uninstalls the app on a specified site.
377- *
378- * @param node The tree item representing the app to be installed or uninstalled.
379- * @param ctxValue The context value used to identify the action node.
380- * @param action The action to be performed: 'install' or 'uninstall'.
381- */
382- public static async toggleAppInstalled ( node : ActionTreeItem , ctxValue : string , action : 'install' | 'uninstall' ) {
383- try {
384- const actionNode = node . children ?. find ( child => child . contextValue === ctxValue ) ;
385-
386- if ( ! actionNode ?. command ?. arguments ) {
387- Notifications . error ( `Failed to retrieve app details for ${ action } .` ) ;
388- return ;
389- }
390-
391- const [ appID , appTitle , appCatalogUrl ] = actionNode . command . arguments ;
392-
393- let siteUrl : string | undefined ;
394- if ( ! appCatalogUrl ) {
395- const relativeUrl = await window . showInputBox ( {
396- prompt : 'Enter the relative URL of the site' ,
397- ignoreFocusOut : true ,
398- placeHolder : 'e.g., sites/sales or leave blank for root site' ,
399- validateInput : ( input ) => {
400- const trimmedInput = input . trim ( ) ;
401-
402- if ( trimmedInput . startsWith ( 'https://' ) ) {
403- return 'Please provide a relative URL, not an absolute URL.' ;
404- }
405- if ( trimmedInput . startsWith ( '/' ) ) {
406- return 'Please provide a relative URL without a leading slash.' ;
407- }
408-
409- return undefined ;
410- }
411- } ) ;
412-
413- if ( relativeUrl === undefined ) {
414- Notifications . warning ( 'No site URL provided. Operation aborted.' ) ;
415- return ;
416- }
417-
418- siteUrl = `${ EnvironmentInformation . tenantUrl } /${ relativeUrl . trim ( ) } ` ;
419-
420- } else {
421- siteUrl = appCatalogUrl ;
422- }
423-
424- let forceUninstall = false ;
425- if ( action === 'uninstall' ) {
426- const confirmForce = await window . showQuickPick ( [ 'Yes' , 'No' ] , {
427- placeHolder : `Are you sure you want to uninstall the app '${ appTitle } ' from site '${ siteUrl } '?` ,
428- ignoreFocusOut : true ,
429- canPickMany : false
430- } ) ;
431-
432- if ( confirmForce === 'Yes' ) {
433- forceUninstall = true ;
434- } else {
435- Notifications . warning ( 'App uninstallation aborted.' ) ;
436- return ;
437- }
438- }
439-
440- const commandOptions : any = {
441- id : appID ,
442- siteUrl : siteUrl ,
443- ...( appCatalogUrl && {
444- appCatalogScope : 'sitecollection'
445- } ) ,
446- ...( forceUninstall && { force : true } )
447- } ;
448-
449- const cliCommand = action === 'install' ? 'spo app install' : 'spo app uninstall' ;
450- await CliExecuter . execute ( cliCommand , 'json' , commandOptions ) ;
451- Notifications . info ( `App '${ appTitle } ' has been successfully ${ action === 'install' ? 'installed' : 'uninstalled' } on site '${ siteUrl } '.` ) ;
452-
453- // refresh the environmentTreeView
454- await commands . executeCommand ( 'spfx-toolkit.refreshAppCatalogTreeView' ) ;
455- } catch ( e : any ) {
456- const message = e ?. error ?. message ;
457- Notifications . error ( message ) ;
458- }
459- }
460-
461120 /**
462121 * Retrieves the tenant-wide extensions from the specified tenant app catalog URL.
463122 * @param tenantAppCatalogUrl The URL of the tenant app catalog.
0 commit comments