ABSYZ ABSYZ

  • Home

    Home

  • 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
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!

81
Like this post
3 Posts
Pradeep Chary

Search Posts

Archives

Categories

Recent posts

Meet OmniStudio – Revolutionize Your Creative Work

Meet OmniStudio – Revolutionize Your Creative Work

BioAsia 2023 in Hyderabad: An Annual International Event

BioAsia 2023 in Hyderabad: An Annual International Event

The Role Of Marketing in Small & Medium Enterprises

The Role Of Marketing in Small & Medium Enterprises

Salesforce For Retail: How Salesforce CRM Can Help Retailers

Salesforce For Retail: How Salesforce CRM Can Help Retailers

What is ChatGPT & How Does It Work?

What is ChatGPT & How Does It Work?

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

Related Posts

Meet OmniStudio – Revolutionize Your Creative Work
Salesforce

Meet OmniStudio – Revolutionize Your Creative Work

Salesforce For Retail: How Salesforce CRM Can Help Retailers
Salesforce

Salesforce For Retail: How Salesforce CRM Can Help Retailers

Introduction To Copado Devops Tool
Salesforce

Introduction To Copado Devops Tool

What is Salesforce Code Builder?
Salesforce

What is Salesforce Code Builder?

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

INDIA | USA | UAE

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

Copyright ©2022 Absyz Inc. All Rights Reserved.

youngsoft
Copy
We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “ACCEPT ALL”, you consent to the use of ALL the cookies. However, you may visit "Cookie Settings" to provide a controlled consent. Privacy Policy
Cookie SettingsREJECT ALLACCEPT ALL
Manage consent

Privacy Overview

This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary
Always Enabled

Necessary cookies are absolutely essential for the website to function properly. These cookies ensure basic functionalities and security features of the website, anonymously.

CookieDurationDescription
cookielawinfo-checkbox-analytics11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Analytics".
cookielawinfo-checkbox-functional11 monthsThe cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional".
cookielawinfo-checkbox-necessary11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary".
cookielawinfo-checkbox-others11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Other.
cookielawinfo-checkbox-performance11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Performance".
viewed_cookie_policy11 monthsThe cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data.

Functional

Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features.

Performance

Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.

Analytics

Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.

Advertisement

Advertisement cookies are used to provide visitors with relevant ads and marketing campaigns. These cookies track visitors across websites and collect information to provide customized ads.

Others

Other uncategorized cookies are those that are being analyzed and have not been classified into a category as yet.

SAVE & ACCEPT