Disabling Lightning Experience w/ Python and Selenium in Salesforce
Tags: salesforce, tutorials, python, selenium
Published: 2019-09-12 22:23:43

---
title: Disabling Lightning Experience w/ Python and Selenium in Salesforce
description:  "More Salesforce Profile automated button-clicking"
published: true
tags: salesforce,tutorials,python,selenium
canonical_url: https://katiekodes.com/salesforce-profiles-lightning-python-selenium/
---

You saw me be too lazy to click buttons in [No-Permissions Salesforce Profile w/ Python and Selenium](https://dev.to/katiekodes/no-permissions-salesforce-profile-w-python-and-selenium-2fc7): watch me do it again turning Lightning Experience off in all my Custom Profiles.  
  
_(And watch me get shunned from the Salesforce community for making it easier to neuter the [Winter ‘20 release feature that flips Lightning Experience on](https://admin.salesforce.com/blog/2018/be-lightning-ready-by-the-winter-20-release)?)_

* * *

## Background

Where I work, we’re going to roll out Lightning Experience to our users by Permission Set, not by Profile.

We don’t have any entire profiles trained up, so we want to make sure they see _nothing_ about Lightning Experience when it goes on.

_(Our help desk already got calls from confused end-users when our testing sandbox asked people if they wanted to try Lightning Experience after this weekend’s upgrade.)_

## The Task

I want to loop over all my profiles, which I can find in Salesforce at `https://MyURL.com/00e`, check if there’s an editable **System Permission** called “**Lightning Experience User**,” and make sure it’s un-checked.

In Setup, this is under Profiles, in the “System Permissions” area of a given Profile’s configuration page.

It turns out that editable versions of this page for every profile are available at:

```
https://MyURL.com/00eSOMETHING/e?s=SystemPermissions
```

…where `00eSOMETHING` is the ID of the profile _(which can be revealed in the profile-by-profile links in `https://MyURL.com/00e` take you, handily enough)_.

* * *

## The Code

As before, I’m going to “screen-scrape” Salesforce web pages and “hijack” my own browser to save myself a few clicks.

![XKCD comic about the perils of trying to automate processes](https://imgs.xkcd.com/comics/automation.png)

- Yes, of course this [took me longer to code](https://xkcd.com/1319/) than to click manually.
- That said, now I can reuse my code against any org. 
  - Until Salesforce changes the HTML behind their setup pages and breaks all my code, of course.  
_(That’s the danger of screen-scraping.)_

Note that this works against Setup in the “classic” user interface – the HTML’s probably all different in Lightning Experience.  
_(I wouldn’t know – I have it turned off!)_

### Step 1: Log In

First, I ran the following Python code

```python
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver import Chrome
browser = Chrome(executable_path='C:\\exampleprograms\\ChromeDriver\\chromedriver.exe')
browser.get('https://test.salesforce.com')
```

This caused a special instance of Chrome to pop up on my computer.

I logged into Salesforce with my username and password as usual.

### Step 2: Get My Object Links

Then I commented out the code above and, without letting [my Python IDE](https://katiekodes.com/setup-python-windows-anaconda/#write-your-first-python-program) clear out the value of the `browser` variable _(which is normal behavior of my IDE from one “run” button click to the next)_, I ran the following code:

```python
editableLinksToVisit = []
browser.get('https://cs99.salesforce.com/00e')
profilerows = browser.find_elements_by_xpath("//table[contains(@class, 'x-grid3-row-table')]")
for profilerow in profilerows:
    profileLink = profilerow.find_element_by_css_selector("div[id$='ProfileName']").find_element_by_css_selector("a[href*='/00e']")
    profileBaseURL = profileLink.get_attribute('href')
    profileEditURL = profileBaseURL + '/e?s=SystemPermissions'
    editableLinksToVisit.append({'text':profileLink.text, 'editLink':profileEditURL})
```

Here’s what the code above does:

1. The `browser.get()` command made me visit a new web site in Chrome – as if I myself had typed that URL into my browser bar and hit “enter.” 
  - **It’s pretty trippy** – you literally watch your computer do things without you, like someone else has taken over control.
2. The URL I told `browser.get()` about is the URL of the “profiles” screen. 
  - Note that this script won’t run so well if you’re using a List View of profiles that doesn’t, say, show all of them – or at least all the custom ones.
3. When I run `browser.get()`, the contents stored in my program’s `browser` variable change.
4. Then I use various [methods](https://en.wikipedia.org/wiki/Method_(computer_programming)) built into the [data type](https://en.wikipedia.org/wiki/Data_type) I’ve stored in `browser` _(I forget exactly what it is)_, such as `.find_element_by_css_selector()`, to [scrape](https://en.wikipedia.org/wiki/Web_scraping) the contents of this particular web page. 
  - Technically, I’m scraping the DOM, or the thing you see when you use the Firefox or Chrome developer console “inspect element” tool, not scraping the HTML source. Their contents are ever-so-slightly different. It used to be that you could tell what HTML was making up a page just by looking at its source. These days, JavaScript can “inject” HTML into the “DOM” at the last minute, and your browser _actually_ renders a web page based on the DOM, so you have to know how to check what’s really in the DOM if you want to parse the code behind a web page.
5. My `for` loop looks for links on the page within a `<div>...</div>` tagset whose `id` property ends with “`ProfileName`” that point to a URL containing the text “`/00e`.”
6. Then I grab the name of the object _(per the link’s text)_ and its URL.
7. Finally, I edit the link by sticking an `/e?s=SystemPermissions` at the end of it – now instead of visiting a Profile overview page, I’ll go straight to an editable “System Permissions” configuration page when I visit my link.

My big list of object names and their links is what I store as `editableLinksToVisit`.

### Step 3: Click lots of buttons

I again commented out the code above and, without letting my Python IDE clear out the value any variables _(which is normal behavior of my IDE from one “run” button click to the next)_, took a stretch and ran the following code _(it took about **1-2 minutes to run 30 profiles** )_:

```python
for linkDict in editableLinksToVisit:
    print()
    print(linkDict.get('text'))
    browser.get(linkDict.get('editLink'))
    
    try:
        submitbutton = browser.find_element_by_css_selector("input[type='submit'][value='Save'][id$='button_pc_save']")
    except NoSuchElementException:
        submitbutton = None
    
    try:
        checkedCheckbox = browser.find_element_by_css_selector("input:enabled:checked[type='checkbox'][title='LightningExperienceUser']")
    except NoSuchElementException:
        checkedCheckbox = None
    
    if submitbutton is not None and checkedCheckbox is not None:
        print('submit ' + str(submitbutton is not None))
        print('checkbox ' + str(checkedCheckbox is not None))
        checkedCheckbox.send_keys(" ")
        submitbutton.send_keys("\n")
        print('done')
    else:
        print('There is nothing to save.')
```

This code is a loop over all the description-URL link pairs _([dictionaries](https://www.w3schools.com/python/python_dictionaries.asp))_ I’d saved into `editableLinksToVisit`.

Here’s what it does for **each** link in the list:

1. It writes down a note I can read telling me what object it’s supposed to be about to hijack my browser to visit the URL of
2. It hijacks my browser and visits that URL.
3. It reads the code of the page’s DOM to find the Save button. 
  - If there isn’t one, it makes a note of that.
4. It reads the code of the page’s DOM to find the first checkbox on the page titled `LightningExperienceUser` that is currently checked and un-checkable. 
  - If there isn’t one, it makes a note of that.
5. If it found anything that “could use un-checking,” it does so. 
  - I use an imitation of hitting the spacebar after “tabbing” to a checkbox with the keyboard, rather than Selenium’s `.click()` command, because `.click()` wasn’t working reliably and someone on StackOverflow said this might and they were right. 🤷
6. It clicks the Save button _(again, I simulate this by virtually hitting “enter” on the keyboard rather than by clicking because for some reason that worked more reliably)_.

You can watch your browser “drive” without you. It’s kind of fun.

There’re also a lot of `print()` notes so you can watch what’s going on from your “standard output” terminal of the software in which you’re running your Python code and anticipate how far through the list it is.

* * *

## Conclusion

So … there you go. A new party trick for lazy adminelopers.

Have fun, and don’t forget to give people you _do_ want to be able to see Lightning Experience that permission in a Permission Set after you disable it in all custom Profiles.

You do that by:

1. Creating a permission set
2. Going into _its_ “System Permissions” area and clicking “Edit”
3. Checking the “Lightning Experience User” checkbox and clicking “Save”
4. Assigning a user to the permission set in question