ABSYZ ABSYZ

  • Home

    Welcome to ABSYZ

  • About us

    Who We Are

  • Our Expertise

    What We Do

  • Our Approach

    How We Do It

  • Products

    What We Made

  • Industries

    Who We Do It For

  • Clients

    Whom We Did It For.

  • Article & Blogs

    What Experts Think

  • Careers

    Join The Team

  • Get In Touch

    Let’s Get Started

ABSYZ

Tool to Delete Components from Production

Home / Article & Blogs / Apex / Tool to Delete Components from Production

Tool to Delete Components from Production

By Pradeep Chary inApex, Salesforce

It is known that we cannot delete components like Apex Classes, Triggers, etc. manually from production. The common approach to delete these components are through Force.com IDE or Force.com Migration tool. In this article we design a tool to delete these components. We build a Visualforce page which creates two XML files named package.xml and destructiveChanges.xml, later compress them and deploy.

To start with include JSZip as a static resource. Now lets create a Visualforce page as below,

Screen Shot 2016-07-01 at 6.05.14 PM

How this works ?

The field “Component Type” contain elements like ApexClass, ApexPage, ApexTrigger, etc and in the field “Component” we have to enter the Object Name of the component to be deleted, click on the button “Add” and the component is added to the wrapper list. When confirmed click on the button “Delete”, a javascript function is triggered to add the wrapper list elements to a destructive changes XML string, package XML string is also initialised, both these strings are converted into files and zipped using JSZip. After this an apex Deploy method is invoked from the Visualforce page javascript, we are using MetadataService Class to make the deployment call.

Please use the below code for the Visualforce page and note that in the controller in the method createService we have to update service endpoint with the URL of the organisation. Also add the organisation url in the Remote site settings.

Controller:

[sourcecode language=”java” collapse=”true” title=”DeleteComponentsClass.apxc”]
public class DeleteComponentsClass {

// Initializing elements
public String selectedType{get;set;}
public String componentName{get;set;}
public List<DeleteComponentsWrpClass> componentItems{get;set;}

// Getting picklist elements
public static List<SelectOption> getDataTypes(){

List<String> itemTypes = new List<String>{‘ActionLinkGroupTemplate’, ‘AnalyticSnapshot’, ‘ApexClass’, ‘ApexComponent’, ‘ApexPage’,
‘ApexTrigger’, ‘AppMenu’, ‘ApprovalProcess’, ‘AssignmentRules’, ‘AuraDefinitionBundle’, ‘AuthProvider’, ‘AutoResponseRules’, ‘BusinessProcess’,
‘CallCenter’, ‘ChannelLayout’, ‘Community’, ‘CompactLayout’, ‘CorsWhitelistOrigin’, ‘CustomApplication’, ‘CustomField’, ‘CustomMetadata’,
‘CustomObject’, ‘CustomObjectTranslation’, ‘CustomPageWebLink’, ‘CustomPermission’, ‘CustomSite’, ‘CustomTab’, ‘Dashboard’, ‘Document’,
‘EmailTemplate’, ‘EscalationRules’, ‘ExternalDataSource’, ‘FieldSet’, ‘FlexiPage’, ‘FlowDefinition’, ‘Group’, ‘HomePageComponent’, ‘HomePageLayout’,
‘Layout’, ‘Letterhead’, ‘ListView’, ‘MatchingRule’, ‘NamedCredential’, ‘PermissionSet’, ‘PlatformCachePartition’, ‘Portal’, ‘PostTemplate’,
‘Profile’, ‘Queue’, ‘QuickAction’, ‘RecordType’, ‘RemoteSiteSetting’, ‘Report’, ‘ReportType’, ‘Role’, ‘Scontrol’, ‘Settings’, ‘SharingCriteriaRule’,
‘SharingOwnerRule’, ‘SharingReason’, ‘SharingRules’, ‘SharingSet’, ‘SiteDotCom’, ‘StaticResource’, ‘UserPermissions’, ‘ValidationRule’,
‘WebLink’, ‘Workflow’, ‘WorkflowAlert’, ‘WorkflowFieldUpdate’, ‘WorkflowOutboundMessage’, ‘WorkflowRule’, ‘WorkflowTask’};

List<SelectOption> customTypes = new List<SelectOption>();
customTypes.add(new SelectOption(”,’–None–‘));
for(String item : itemTypes){
customTypes.add(new SelectOption(item,item));
}

return customTypes;
}

// Adding Items to the List
public void add(){

List<String> splitItems = componentName.split(‘;’);

for(String Item : splitItems){
DeleteComponentsWrpClass compItem = new DeleteComponentsWrpClass();
compItem.compName = Item;
compItem.compType = selectedType;
if(componentItems == null){
componentItems = new List<DeleteComponentsWrpClass>();
}
componentItems.add(compItem);
}
componentName = null;

}

// Deploying zip file
@RemoteAction
public static String getDeployContent(String zipData){

MetadataService.MetadataPort service = createService();

MetadataService.DeployOptions deployOptions = new MetadataService.DeployOptions();
deployOptions.allowMissingFiles = false;
deployOptions.autoUpdatePackage = false;
deployOptions.checkOnly = false;
deployOptions.ignoreWarnings = false;
deployOptions.performRetrieve = false;
deployOptions.purgeOnDelete = false;
deployOptions.rollbackOnError = true;
deployOptions.testLevel = ‘NoTestRun’;
deployOptions.singlePackage = true;

MetadataService.AsyncResult AsyncResult = service.deploy(zipData, DeployOptions);

return (‘Success’);
}

// Cancel button action
public PageReference cancel(){

PageReference pageRef = new PageReference(‘/home/home.jsp’);
pageRef.setRedirect(true);
return pageRef;

}

// Delete button action
public PageReference deleteItems(){

Long startTime = System.now().getTime();
Integer delay = 5000;

do {
// Do nothing
}while (System.now().getTime() – startTime < delay);

PageReference pageRef = new PageReference(‘/changemgmt/monitorDeployment.apexp’);
pageRef.setRedirect(true);
return pageRef;

}

// Creating service
private static MetadataService.MetadataPort createService() {

MetadataService.MetadataPort service = new MetadataService.MetadataPort();
service.SessionHeader = new MetadataService.SessionHeader_element();
service.endpoint_x = ‘https://coyg-dev-ed.my.salesforce.com/services/Soap/m/35.0’;
service.timeout_x = 120000;
service.SessionHeader.sessionId = UserInfo.getSessionId();
return service;

}

// Wrapper Class
public class DeleteComponentsWrpClass {

public String compType{get;set;}
public String compName{get;set;}

public DeleteComponentsWrpClass(){

}
}

}
[/sourcecode]

Visualforce page:

[sourcecode language=”html” collapse=”true” title=”DeleteComponents.vfp”]
<apex:page id=”page” controller=”DeleteComponentsClass”>

<apex:includeScript value=”{!$Resource.jsZip}”/>

<script type=”text/javascript”>
var compMap = {};

function contentToMap(key,value) {
compMap[key] = compMap[key] || [];
compMap[key].push(value);
}

function getRemoteContent(){

var keys = Object.keys(compMap);

if(keys.length > 0){
var packageXml = ‘<?xml version=”1.0″ encoding=”UTF-8″?>’ +
‘<Package xmlns=”http://soap.sforce.com/2006/04/metadata”>’ +
‘<version>35.0</version>’ +
‘</Package>’ ;

var destructiveChanges = ‘<?xml version=”1.0″ encoding=”UTF-8″?>’ +
‘<Package xmlns=”http://soap.sforce.com/2006/04/metadata”>’ ;

for(var i=0;i<keys.length;i++){

destructiveChanges = destructiveChanges + ‘<types>’;

if(keys[i] == ‘Dashboard’ || keys[i] == ‘Report’ || keys[i] == ‘EmailTemplate’ ||keys[i] == ‘Document’){

for(var j=0;j<compMap[keys[i]].length;j++) {
var items = compMap[keys[i]][j].split(‘/’);
if((destructiveChanges.indexOf(‘<members>’ + items[0] + ‘</members>’) > -1) && (items[0] != ‘unfiled$public’)) {
destructiveChanges = destructiveChanges + ‘<members>’+items[0]+'</members>’;
}

destructiveChanges = destructiveChanges + ‘<members>’+compMap[keys[i]][j]+'</members>’;
}

} else {

for(var j=0;j<compMap[keys[i]].length;j++) {
destructiveChanges = destructiveChanges + ‘<members>’+compMap[keys[i]][j]+'</members>’;
}

}

destructiveChanges = destructiveChanges + ‘<name>’+keys[i]+'</name>’;
destructiveChanges = destructiveChanges + ‘</types>’;

}

destructiveChanges = destructiveChanges + ‘</Package>’;

zipFile = new JSZip();
zipFile.file(‘package.xml’,packageXml);
zipFile.file(‘destructiveChanges.xml’,destructiveChanges);
zipFile.generateAsync({type:”base64″}).then(function(data) {
Visualforce.remoting.Manager.invokeAction(
‘{!$RemoteAction.DeleteComponentsClass.getDeployContent}’,
data,

function(result, event){
if (event.status) {

} else {
alert(“Error”);
}
});
});
} else {
alert(‘Add Components’);
}
}

</script>

<apex:form id=”frm” >

<apex:pageBlock id=”pblck1″ title=”Delete Components”>

<apex:pageBlockSection id=”pbSectn1″ columns=”4″ showHeader=”false”>
<apex:outputLabel value=”Component Type” for=”picklistID” />
<apex:selectList id=”picklistID” value=”{!SelectedType}” title=”Component Type” multiselect=”false” size=”1″>
<apex:selectOptions value=”{!DataTypes}” />
</apex:selectList>
</apex:pageBlockSection>

<apex:pageBlockSection id=”pbSectn2″ columns=”4″ showHeader=”false”>
<apex:outputLabel value=”Component” for=”nameID” />
<apex:inputText id=”componentName” value=”{!ComponentName}” />
</apex:pageBlockSection>

<apex:pageBlockButtons location=”bottom”>
<apex:commandButton action=”{!add}” value=” Add “> </apex:commandButton>
<apex:commandButton onclick=”getRemoteContent()” action=”{!deleteItems}” value=” Delete “> </apex:commandButton>
<apex:commandButton action=”{!cancel}” value=” Cancel “></apex:commandButton>
</apex:pageBlockButtons>

</apex:pageBlock>

<apex:pageBlock id=”pblck2″ >
<table border=’1′>
<tr style=”background:#3498db;color:white”>
<th>Component Name</th>
<th>Component Type</th>
</tr>
<apex:repeat id=”itemId” value=”{!componentItems}” var=”item”>
<script>
contentToMap(‘{!item.compType}’,'{!item.compName}’);
</script>
<tr>
<td>{!item.compName}</td>
<td>{!item.compType}</td>
</tr>
</apex:repeat></table>
</apex:pageBlock>

<apex:pageBlock >

<apex:pageBlockSection columns=”1″>
<span style=”font-style:Italic”>
Note: Component is the Component Object Name. For Folder components like Dashboard, Document, EmailTemplate and Report, the Folder Name is added as prefix to the Component. For example my Dashboard folder is demoFolder and Dashboard is demoComponent, then the Component name is demoFolder/demoComponent.
You can add more than one Component Name separated by semicolon(‘;’).
</span>
</apex:pageBlockSection>

</apex:pageBlock>

</apex:form>

</apex:page>
[/sourcecode]

Let’s test this:

I have an Apex class “HelloWorld” and Apex Trigger “HelloTrigger”. (I am using the developer org not the production).

Screen Shot 2016-07-01 at 7.08.13 PM

Screen Shot 2016-07-01 at 7.05.32 PM

Add these components in the tool.

Screen Shot 2016-07-01 at 7.09.52 PM

Click on the delete button we are redirected to the Deployment status page displaying record showing deployment succeeded.

Screen Shot 2016-07-01 at 7.10.45 PM

We can also check in the Apex classes and Apex Triggers setup that the components are deleted.

Screen Shot 2016-07-01 at 7.13.24 PM

Screen Shot 2016-07-01 at 7.14.06 PM

This tool can also be used to delete other components like Apex pages, Dashboards, Documents, Reports, Workflows, etc,. The components Dashboard, Document, EmailTemplate and Report are folder components, so we have to mention they folder name in order to delete. For example, my dashboard is demoDashboard and its in folder demoFolder. Then the component name will be demoFolder/demoDashboard. We can also add multiple component names of same type separated by semicolon (;).

Related links:

View complete code on GitHub

Install Unmanaged package

Happy Coding!

47
Like this post
3 Posts
Pradeep Chary

Search Posts

Archives

Categories

Recent posts

Smarter and efficient way of selecting a candidate with Compare Applicant

Smarter and efficient way of selecting a candidate with Compare Applicant

Tableau CRM: Add action buttons on Table without XMD

Tableau CRM: Add action buttons on Table without XMD

jQuery DataTable in Salesforce Lightning Component

jQuery DataTable in Salesforce Lightning Component

Boost Recruiter’s Efficiency with Smart Candidate Recommendations

Boost Recruiter’s Efficiency with Smart Candidate Recommendations

Audit Trail in Marketing Cloud

Audit Trail in Marketing Cloud

  • Previous PostWave Analytics
  • Next PostTop and Most Awaited Winter 17 Features

Related Posts

Smarter and efficient way of selecting a candidate with Compare Applicant
AppExchange ConnectEazy Salesforce

Smarter and efficient way of selecting a candidate with Compare Applicant

Tableau CRM: Add action buttons on Table without XMD
Salesforce Salesforce Einstein Tableau

Tableau CRM: Add action buttons on Table without XMD

jQuery DataTable in Salesforce Lightning Component
Jquery Lightning Salesforce

jQuery DataTable in Salesforce Lightning Component

Boost Recruiter’s Efficiency with Smart Candidate Recommendations
ConnectEazy Salesforce

Boost Recruiter’s Efficiency with Smart Candidate Recommendations

1 Comment

  1. KSP
    Reply
    20 August 2016

    Proud of you Chary…keep going 🙂

    Reply

Leave a Reply (Cancel reply)

Your email address will not be published. Required fields are marked *

*
*

ABSYZ Logo
  • Home
  • About us
  • Article & Blogs
  • Careers
  • Get In Touch
  • Our Expertise
  • Our Approach
  • Products
  • Industries
  • Clients
  • White Papers

ABSYZ Software Consulting Pvt. Ltd.
USA: 49197 Wixom Tech Dr, Wixom, MI 48393, USA
M: +1.415.364.8055

India: 6th Floor, SS Techpark, PSR Prime, DLF Cyber City, Gachibowli, Hyderabad, Telangana – 500032
M: +91 79979 66174

Copyright ©2020 Absyz Inc. All Rights Reserved.

youngsoft
Copy