{"id":15105518,"url":"https://github.com/jeffsikes/salesforce-eleventy-directory","last_synced_at":"2026-01-18T01:36:21.206Z","repository":{"id":248329185,"uuid":"828317780","full_name":"jeffsikes/salesforce-eleventy-directory","owner":"jeffsikes","description":"An 11ty template for pulling down Salesforce user data via the JSForce API","archived":false,"fork":false,"pushed_at":"2024-07-14T03:24:32.000Z","size":87,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-02T05:32:38.110Z","etag":null,"topics":["11ty","11ty-starter","eleventy","jsforce","salesforce"],"latest_commit_sha":null,"homepage":"","language":"Nunjucks","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jeffsikes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-13T19:04:38.000Z","updated_at":"2024-07-14T03:25:51.000Z","dependencies_parsed_at":null,"dependency_job_id":"4849120a-1cb3-4507-8b66-bd853bf34d96","html_url":"https://github.com/jeffsikes/salesforce-eleventy-directory","commit_stats":{"total_commits":7,"total_committers":1,"mean_commits":7.0,"dds":0.0,"last_synced_commit":"5e4df85737e137e9b9e935257b26cb154237a921"},"previous_names":["jeffsikes/salesforce-eleventy-directory"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeffsikes%2Fsalesforce-eleventy-directory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeffsikes%2Fsalesforce-eleventy-directory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeffsikes%2Fsalesforce-eleventy-directory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeffsikes%2Fsalesforce-eleventy-directory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jeffsikes","download_url":"https://codeload.github.com/jeffsikes/salesforce-eleventy-directory/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247339133,"owners_count":20923009,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["11ty","11ty-starter","eleventy","jsforce","salesforce"],"created_at":"2024-09-25T20:40:33.805Z","updated_at":"2026-01-18T01:36:21.141Z","avatar_url":"https://github.com/jeffsikes.png","language":"Nunjucks","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Eleventy Experiment: Salesforce User Directory\nThis is a simple experiment to see if I can use Eleventy to generate a small user directory from a Salesforce SOQL query.\n\nI'm using the [JSForce](https://jsforce.github.io/) library to connect to Salesforce and run a SOQL query to get a list of users. I'm then using Eleventy to generate a static site with the user data.\n\n\u003e I used JSForce 2.0 for this experiment. It looks like JSForce 3.0 is coming out soon, so I'll have to update this when it's released.\n\nObviously not a real-world use case, but it's a fun experiment to see how Eleventy can be used to generate static sites from Salesforce data.\n\n## Project Setup\nI wanted to keep things simple, so I'm using very limited dependencies. Here's what you need to get started:\n\n1. Install [Node.js](https://nodejs.org/) (I'm using v18.20.2)\n2. Install [Eleventy](https://www.11ty.dev/): `npm install -g @11ty/eleventy`\n3. Install [JSForce](https://github.com/jsforce/): `npm install jsforce`\n\nI decided against using a CSS framework for simplicity. If I did use one, tho, for these little lab experiments I'd probably use [Pico.css](https://picocss.com/).\n\n## Salesforce Setup\nYou'll need some information from your Salesforce instance to get started.\n\n| Required Fields | Description |\n|-----------------|-------------|\n|Salesforce URL | The URL for your Salesforce instance. For example, `https://mycompany.my.salesforce.com`. |\n|Salesforce Username | A Salesforce username that has API access (will explain how to get this later). For production, you should create a dedicated user for this. |\n|Salesforce Password | The password for the Salesforce user. |\n|Salesforce Security Token | A security token for the Salesforce user. If you already have one, great! If not, follow the directions below. |\n\n### Getting a Salesforce Security Token\nYou may have already generated a security token. If you have it, use it! \n\nOtherwise, login into Salesforce with the username you chose, clicking on your profile picture, and selecting \"Settings\". From there, click on \"Reset My Security Token\". You'll receive an email with the token. It took about 5 minutes to get mine. Make sure to store it somewhere safe, because you can't see it again.\n\nNote that if you ever reset your password, the security token will be reset as well. This isn't the best route for production, but it's fine for this experiment.\n\nYour username, password and token should be stored in a `.env` file in the root of the project. Here's what mine looks like:\n\n```\nSF_LOGIN_URL={your Salesforce URL}\nSF_USERNAME={your Salesforce username}\nSF_PASSWORD={your Salesforce password}\nSF_TOKEN={your Salesforce security token}\n```\n\nThis information will not be included in the final published website. It's only used to generate the user directory data during build.\n\n## The Code\nThere's only a few main functions for this project:\n\n### Logging into Salesforce\nThe `loginToSalesforce` function uses the `jsforce` library to log into Salesforce. It uses the `SF_USERNAME`, `SF_PASSWORD`, and `SF_TOKEN` environment variables to authenticate.\n\nHere's the related code. JSForce makes it fairly simple. Note that I'm using V2.0 of JSForce, but it looks like V3.0 is coming out very soon.\n\n```javascript\nconst loginToSalesforce = () =\u003e {\n    return conn.login(process.env.SF_USERNAME, process.env.SF_PASSWORD + process.env.SF_TOKEN, (err, userInfo) =\u003e {\n        if (err) {\n            return console.error(err);\n        }\n        console.log(\"User ID: \" + userInfo.id);\n        console.log(\"Org ID: \" + userInfo.organizationId);\n    });\n};\n```\n\n### Searching Salesforce with SOQL + JSForce\nThe `searchUsers` function uses the `query` method from the `jsforce` library to run a SOQL query against Salesforce. The query is simple: `SELECT Id, FirstName, LastName, Email, SmallPhotoUrl`. This returns a list of users with their ID, Name, Title, Email and Profile Photo if it exists.\n\n```javascript\nconst searchUsers = (lastNameInitial) =\u003e {\n    return new Promise((resolve, reject) =\u003e {\n        conn.query(`\n            SELECT Id, FirstName, LastName, Email, SmallPhotoUrl FROM User LIMIT 100`, \n        (err, result) =\u003e {\n            if (err) {\n                reject(err);\n            }\n            resolve(result.records);\n        });\n    });\n};\n```\n\n### Generating the User Directory\nEleventy then works it's magic to generate the data we'll use for the directory. For this small experiment, I'm not writing the data to a file since I'm limiting the users to 100, but you could easily write it to a JSON file.\n\nHere's the core of the Eleventy config file. It adds the Salesforce users to the global data and creates a collection of users.\n\n```javascript\nmodule.exports = function(eleventyConfig) {\n    eleventyConfig.addGlobalData(\"env\", process.env);\n\n    eleventyConfig.addCollection(\"salesforceUsers\", async function(collectionApi) {\n        await loginToSalesforce();\n        const users = await searchUsers();\n        return users;\n    });\n\n    return {\n        dir: {\n            input: \"src\",\n            output: \"dist\"\n        }\n    };\n};\n```\n\n### The HTML Template\nThe `index.njk` file is the template for the user directory. It's a simple list of users with their name, title, email and profile photo. I'm using the `SmallPhotoUrl` field from Salesforce to display the profile photo.\n\nI'm a big fan of [microformats](https://developer.mozilla.org/en-US/docs/Web/HTML/microformats), so I've added some basic h-card markup to the user list. This is a simple way to add some semantic meaning to the user data. You can also use these to style the user list with CSS.\n\n```html\n\u003cdiv class=\"h-card\" role=\"listitem\"\u003e\n    \u003cimg src=\"{linkToProfileImage}\" alt=\"Profile Image\" class=\"u-photo\"\u003e\n    \u003cdiv class=\"user-info\"\u003e\n        \u003ca href=\"{linkToProfilePage}\" class=\"u-url p-name blur-text\"\u003eFirstName LastName\u003c/a\u003e\n        \u003cdiv class=\"p-email blur-text\"\u003eemail@address.com\u003c/div\u003e\n    \u003c/div\u003e\n\u003c/div\u003e\n```\n\u003e I noticed that eleventy is kind of messing up my HTML indentation. I'll have to look into that.\n\nSearch was added client side with some simple javascript. It's not perfect, but it works for this experiment.\n\n```javascript\nfunction filterUsers() {\n    const searchBox = document.getElementById('search-box');\n    const filter = searchBox.value.toLowerCase();\n    const userGrid = document.getElementById('user-grid');\n    const users = userGrid.getElementsByClassName('h-card');\n    let visibleCount = 0;\n\n    for (let i = 0; i \u003c users.length; i++) {\n        const name = users[i].getElementsByClassName('p-name')[0].textContent.toLowerCase();\n        const email = users[i].getElementsByClassName('p-email')[0].textContent.toLowerCase();\n        if (name.includes(filter) || email.includes(filter)) {\n            users[i].style.display = '';\n            visibleCount++;\n        } else {\n            users[i].style.display = 'none';\n        }\n    }\n\n    const noResults = document.getElementById('no-results');\n    if (visibleCount === 0) {\n        noResults.textContent = \"No users were found matching your criteria.\";\n        noResults.classList.remove('hidden');\n    } else {\n        noResults.textContent = \"\";\n        noResults.classList.add('hidden');\n    }\n}\n```\n\n### The CSS\nI used flex grid to layout the user list. It's simple and works well for this use case.\n\nYou may notice that I'm using a `blur-text` class. Because I wanted to demo this publicly with screenshots and video, I've blurred out the user data for everyone but myself. This is a simple way to do it with CSS.\n\n```css\n    .blur-text {\n        filter: blur(5px); /* Apply blur effect */\n    }\n```\n\nI have to say, for me, the styling is always the hardest part. I'm not a designer, so I tend to keep things simple. I'm a big fan of [Pico.css](https://picocss.com/) for these small projects.\n\n## Standards and Accessibility\nI'm a big fan of [microformats](https://developer.mozilla.org/en-US/docs/Web/HTML/microformats), so I've added some basic h-card markup to the user list. This is a simple way to add some semantic meaning to the user data. You can also use these to style the user list with CSS.\n\nIn addition, I attempted to make the site [voiceover accessible](https://www.bocoup.com/blog/getting-started-with-voiceover-accessibility). I'm not an expert in this area, but I tried to make sure the site was navigable with a screen reader.\n\n## Running the Project\nTo run the project, you'll need to exceute the following command at the terminal:\n\n```\nnpx eleventy --serve\n```\n\n## Known Issues\nWhen running locally, profile images may not appear. Images are loaded on SSL from Salesforce, but locally I'm running on HTTP. A work around would be to download all profile images and host them locally. \n\nNote that it works in Edge, but not in Chrome or Firefox.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeffsikes%2Fsalesforce-eleventy-directory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjeffsikes%2Fsalesforce-eleventy-directory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeffsikes%2Fsalesforce-eleventy-directory/lists"}