diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e2517a8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (c) 2024, Warren Walters + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index f2297d8..78bb436 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # why-salesforce + This extension allows users to create custom tabs in Setup for their most-used settings [Demo Video](https://youtu.be/BtlKRvac9ZQ) @@ -6,19 +7,23 @@ This extension allows users to create custom tabs in Setup for their most-used s [Install on Chrome Web Store](https://chrome.google.com/webstore/detail/why-salesforce/ghakkjfjpnhpggbkfkeplbefkipfoaod) ### Roadmap + - [x] Ability to customize tab - - [x] Salesforce SLDS - - Feedback on save and delete - - Update tabs onSave without refresh - - Disable save button if tabs list empty + - [x] Salesforce SLDS + - [ ] Feedback on save and delete + - [ ] Update tabs onSave without refresh + - [x] Disable save button if tabs list empty + - [ ] Reorder tabs - [x] Minify SLDS files -- Dark mode for flows -- Org specific tab customization -- Highlight tab when user is on that url -- Open full urls in new tab -- Better solutions for waiting until Salesforce setup is completely loaded -- Utils class for templates or other shared code +- [ ] Dark mode for flows +- [ ] Org specific tab customization +- [ ] Highlight tab when user is on that url +- [x] Open full urls in new tab +- [ ] Better solutions for waiting until Salesforce setup is completely loaded +- [ ] Utils class for templates or other shared code Contributors + - [Warren Walters](https://www.linkedin.com/in/walters954/) -- [Chris Rouse (Firefox port)](https://www.linkedin.com/in/chris-rouse/) \ No newline at end of file +- [Chris Rouse (Firefox port)](https://www.linkedin.com/in/chris-rouse/) +- [Jennifer Olson](https://www.linkedin.com/in/olsonjenn05/) diff --git a/content.js b/content.js index 7b7cb45..a921a14 100644 --- a/content.js +++ b/content.js @@ -1,10 +1,11 @@ const storageKey = 'sfmWhySF'; -function init(setupTabUl){ - if (setupTabUl){ +function init(setupTabUl) { + if (setupTabUl) { let rows = []; + browser.storage.sync.get([storageKey], function(items) { - let rowObj = items[storageKey]; + const rowObj = items[storageKey] || []; if (!rowObj) { //Did not find data inside browser storage rowObj = initTabs(); @@ -12,25 +13,27 @@ function init(setupTabUl){ for (const rowId in rowObj) { let row = rowObj[rowId]; - rows.push(generateRowTemplate(row.tabTitle,row.url)) + rows.push(generateRowTemplate(row.tabTitle, row.url, row.openInNewTab)); } setupTabUl.insertAdjacentHTML('beforeend', rows.join('')); + + // Add click event listeners after rows are inserted + addClickEventListeners(); }); - } } function delayLoadSetupTabs(count) { - const setupTabUl = document.getElementsByClassName("tabBarItems slds-grid")[0]; + const setupTabUl = document.getElementsByClassName("tabBarItems slds-grid")[0]; count++; - if (count > 5){ + if (count > 5) { console.log('Why Salesforce - failed to find setup tab.'); return; } if (!setupTabUl) { - setTimeout(function() { delayLoadSetupTabs(0); }, 3000); + setTimeout(function() { delayLoadSetupTabs(count); }, 3000); // Fixed to pass count correctly } else { init(setupTabUl); } @@ -38,24 +41,44 @@ function delayLoadSetupTabs(count) { setTimeout(function() { delayLoadSetupTabs(0); }, 3000); - -function generateRowTemplate(tabTitle, url){ - return `` +function generateRowTemplate(tabTitle, url, openInNewTab) { + const target = openInNewTab ? '_blank' : '_self'; + return ``; } -function initTabs(){ +function initTabs() { let tabs = [ - {tabTitle : 'Flow', url: '/lightning/setup/Flows/home'}, - {tabTitle : 'User', url: '/lightning/setup/ManageUsers/home'} - ] + { tabTitle: 'Home', url: '/', openInNewTab: false }, + { tabTitle: 'Flow', url: '/lightning/setup/Flows/home', openInNewTab: true }, + { tabTitle: 'User', url: '/lightning/setup/ManageUsers/home', openInNewTab: false } + ]; + + browser.storage.sync.set({ storageKey: tabs }, function() { - browser.storage.sync.set({storageKey: tabs}, function() { //TODO combine with popup.js with background service }); return tabs; -} \ No newline at end of file +} + +function addClickEventListeners() { + chrome.storage.sync.get([storageKey], function(items) { + const rowObj = items[storageKey] || []; + for (const rowId in rowObj) { + let tab = rowObj[rowId]; + document.querySelectorAll(`a[href="${tab.url}"]`).forEach(link => { + link.addEventListener('click', function(event) { + if (tab.openInNewTab) { + event.preventDefault(); + window.open(tab.url, '_blank'); + } + }); + }); + } + }); +} + diff --git a/manifest.json b/manifest.json index 11b3b26..0ab7091 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Why Salesforce", - "version": "1.0", + "version": "1.4", "permissions": ["storage"], "description": "Stuff that Salesforce should have added already... Adding flow and user tabs into setup.", "homepage_url": "https://github.com/walters954/why-salesforce/tree/firefox", diff --git a/popup.html b/popup.html index d8a46c6..d60fb71 100644 --- a/popup.html +++ b/popup.html @@ -1,19 +1,20 @@ - - - - - - - + + + + + + + Tab Manager +
- + Why Salesforce Why Salesforce
@@ -32,18 +33,22 @@

- + +
+
Tab Name
Tab URL
+
New Tab
+
Actions

@@ -53,24 +58,18 @@

- -
+ +
- + +
+ + +
+
- diff --git a/popup.js b/popup.js index 693aa35..cdced9c 100644 --- a/popup.js +++ b/popup.js @@ -8,18 +8,20 @@ function loadTabs(){ const elements = new Set(); browser.storage.sync.get(['sfmWhySF'], function(items) { - const rowObj = items['sfmWhySF']; + const rowObj = items['sfmWhySF'] || []; + for (const rowId in rowObj) { let tab = rowObj[rowId]; const element = template.content.firstElementChild.cloneNode(true); element.querySelector(".tabTitle").value = tab.tabTitle; element.querySelector(".url").value = tab.url; + element.querySelector(".openInNewTab").checked = tab.openInNewTab || false; element.querySelector(".delete").addEventListener("click", deleteTab); elements.add(element); } document.querySelector(tabAppendElement).append(...elements); + updateSaveButtonState(); }); - } function addTab(){ @@ -27,6 +29,7 @@ function addTab(){ const element = template.content.firstElementChild.cloneNode(true); element.querySelector(".delete").addEventListener("click", deleteTab); document.querySelector(tabAppendElement).append(element); + updateSaveButtonState(); } function saveTab(){ @@ -40,9 +43,10 @@ function processTabs(){ Array.from(tabElements).forEach(function (tab) { let tabTitle = tab.querySelector('.tabTitle').value; let url = tab.querySelector('.url').value; + let openInNewTab = tab.querySelector('.openInNewTab').checked; if (tabTitle && url){ - tabs.push({tabTitle, url}); + tabs.push({tabTitle, url, openInNewTab}); } }); return tabs; @@ -51,6 +55,7 @@ function processTabs(){ function deleteTab(){ this.closest(".tab").remove(); saveTab(); + updateSaveButtonState(); } function setBrowserStorage(tabs){ @@ -60,6 +65,11 @@ function setBrowserStorage(tabs){ }); } +function updateSaveButtonState() { + const saveButton = document.querySelector(".save"); + const tabElements = document.getElementsByClassName('tab'); + saveButton.disabled = tabElements.length === 0; +} const saveButton = document.querySelector(".save"); saveButton.addEventListener("click", saveTab); @@ -67,7 +77,6 @@ saveButton.addEventListener("click", saveTab); const addButton = document.querySelector(".add"); addButton.addEventListener("click", addTab); - function clearBrowserStorage(){ browser.storage.sync.remove(["sfmWhySF"],function(){ var error = browser.runtime.lastError; @@ -76,3 +85,6 @@ function clearBrowserStorage(){ } }) } + +// Initial check to set the state of the save button +updateSaveButtonState(); \ No newline at end of file