Salesforce学习笔记之代码若干

有几段试验性的代码因为公司要更新沙盒,删除了。在本地虽然还保存了副本,但怕以后刷新时误删,所以贴一份在这里,以便需要时拷贝。

1.用aura组件包装一个flow

foo.cmp:

<aura:component implements="flexipage:availableForAllPageTypes,lightning:isUrlAddressable,lightning:availableForFlowScreens,flexipage:availableForRecordHome,force:hasRecordId,force:lightningQuickAction"  access="global">
    <aura:handler name="init" value="{!this}" action="{!c.init}" />
    <lightning:flow aura:id="flowData" onstatuschange="{!c.handleStatusChange}" />   
    <lightning:workspaceAPI aura:id="workspace"/>     
</aura:component>    

fooController.js:

({
        init : function (component) {
            // Find the component whose aura:id is "flowData"
            var flow = component.find("flowData");
            // In that component, start your flow. Reference the flow's API Name.
            flow.startFlow("myFlow");
        },
        handleStatusChange : function (component, event) {
            if(event.getParam("status") === "FINISHED") {
                var workspaceAPI = component.find("workspace");
                workspaceAPI.getFocusedTabInfo().then(function(response) {
                    let focusedTabId = response.tabId;
                    workspaceAPI.closeTab({tabId: focusedTabId});
                })
             }
         }                  
})

上面的handleStatusChange的主要作用是因为缺省方式是flow执行完后,自动跳到开头,重复执行,所以用关闭tab页的方式退出flow。

2. 在tab页显示flow

上面的组件包装了flow之后,可以作为QuickAction放到页面上,或者Actions and Recommendations里,但是QuickAction缺省情况下显示在对话框里,这样有些Flow显示起来就很难看。下面的代码将Flow还是显示在tab页:

bar.cmp

<aura:component implements="flexipage:availableForAllPageTypes,lightning:isUrlAddressable,lightning:availableForFlowScreens,flexipage:availableForRecordHome,force:hasRecordId,force:lightningQuickAction"  access="global" >    
    <lightning:workspaceAPI aura:id="workspace"/>
    <aura:handler name="init" value="{!this}" action="{!c.init}" />
</aura:component>    

barController.js

({
    init: function(component, event, helper) {
        var workspaceAPI = component.find("workspace");
        workspaceAPI.getEnclosingTabId().then(function(enclosingTabId) {
            workspaceAPI.openSubtab({
                parentTabId: enclosingTabId,
                pageReference: {
                    "type": "standard__component",
                    "attributes": {
                        "componentName": "c__foo"
                    }
                }
            }).then(function(subtabId) {
                console.log("The new subtab ID is:" + subtabId);
                $A.get("e.force:closeQuickAction").fire();
             }).catch(function(error) {
                console.log("error");
            });
        });
    }
})

3. Process Builder里要删除一个Process,如果这个Process有多个版本,就必须手工一个个版本地删除。版本一多,颇为麻烦。查了资料,有个方法是手工编制一个destructiveChanges.xml,然后发布到服务器。但对于我这样的懒人来说,写这个xml都觉得费劲,于是写了个油猴插件,其实可以实现一键删除,但为了保险起见,避免误删,还是需要输入要删除的Process的标签,然后再一键删除:

// ==UserScript==
// @name         Whatever name you like
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Delete all versions of a process in a batch
// @author       you
// @match        *.lightning.force.com/lightning/setup/ProcessAutomation/home
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    function confirmError(doc, count) {
        if (count > -1) {
            count--;
            let toClick = [];
            let spans = doc.getElementsByTagName('SPAN');
            for (let i = 0; i < spans.length; i++) {
                if (spans[i].innerText == "OK") {
                    toClick.push(spans[i]);
                }
            }
            if (toClick.length < 1) {
                setTimeout(function() {
                    confirmError(doc,count);
                }, 2000);
            }
            else {
                toClick.forEach(function(item) {
                    item.click();
                });
            }
        }
    }

    function delVersions(doc) {
        let processLabel = prompt('Please enter the label of the process you want to delete');
        console.log(processLabel);
        var process = doc.getElementsByClassName('bodyRow processuimgntConsoleListRow versionOpen');
        if (process.length == 0) {
            alert('Please expand the process you want to delete');
        }
        else if (process.length > 1) {
            alert('Please expand only 1 process');
        }
        else {
            var versions = [];
            var versionTrs = doc.getElementsByClassName('bodyRow summary processuimgntVersionListRow processuimgntConsoleListRow');
            var hasActive = false;
            if (processLabel == versionTrs[0].children[0].getAttribute('title')) {
                for (let i = 0; i < versionTrs.length; i++) {
                    //console.log(versionTrs[i]);
                    versions.push(versionTrs[i].children[6].firstChild);
                    if (versionTrs[i].children[5].getAttribute('title') == 'Active') {
                        hasActive = true;
                        break;
                    }
                }
                if (hasActive) {
                    alert('cannot delete the process with an active version');
                }
                else {
                    console.log(versions.length);
                    versions.forEach(function(v) {
                        v.click();
                    });
                    setTimeout(function() {//'Confirm' dialogues may not pop up instantly, so delay a bit
                        let confirms = [];
                        let d = doc;
                        let spans = d.getElementsByTagName('SPAN');
                        for (let i = 0; i < spans.length; i++) {
                            if (spans[i].innerText == "Confirm") {
                                confirms.push(spans[i]);
                            }
                        }
                        confirms.forEach(function(c) {
                            c.click();
                        });
                        setTimeout(function() {//confirm the 'error' prompt
                            confirmError(d, 10);
                        }, 3000);

                    }, 5000);

                }
            }
        }
    }

    function addButton(count) {
        if (count > -1) {
            count--;
            let topmost = document.getElementsByClassName("viewport");
            //console.log(titleDiv);
            console.log(topmost.length);
            if (topmost != null && topmost.length > 0) {
                //let titleDiv = titleH2.parent.parent;
                //console.log(topmost[0]);
                let ifrm = topmost[0].getElementsByTagName('IFRAME');
                console.log(ifrm.length);
                if (ifrm.length > 0) {
                    //console.log(ifrm[0]);
                    var doc = ifrm[0].contentDocument ? ifrm[0].contentDocument: ifrm[0].contentWindow.document;
                    console.log(doc);
                    let titleDiv = doc.getElementsByClassName('myprocesses');

                    if (titleDiv == null || titleDiv.length < 1) {
                        setTimeout(function() {
                            addButton(10);
                        },1000);
                    }
                    else {
                        console.log(titleDiv[0]);
                        var btnDiv = document.createElement('div');
                        btnDiv.innerHTML = '&nbsp;&nbsp;&nbsp;&nbsp;<button type="button" id="btnDelVersions" value="true" ><em>Mass Delete Versions</em></button>';
                        titleDiv[0].appendChild(btnDiv);
                        let btnDelVersions = doc.getElementById('btnDelVersions');
                        btnDelVersions.addEventListener('click', function() {
                            delVersions(doc);
                        }, false);
                    }
                    return;
                }
                else {
                   setTimeout(function() {
                      addButton(10);
                   }, 2000);
                }

            }
            else {
                setTimeout(function() {
                    addButton(count);
                }, 2000);
            }
        }
        else {
            alert('Please refresh your page');
        }
    }

    addButton(10);//try 10 times
})();

4. Salesforce的Developer Console的查询器里不支持注释,这对于用惯了sql server的我来说感觉很不方便,另外,soql也不支持select * from,开始也颇不习惯,写soql查数据时,不得不查Salesforce的参考手册。后来安装了vs code的一个插件,Salesforce schema explorer,大致解决了select * from的问题,但不支持注释语句还是个问题。花了点时间改写了这个插件的代码,大体支持类似sql server里的注释符号--了。

修改了out\views目录下的soql.js:

"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SOQLView = void 0;
const vscode = require("vscode");
const sfAPIOperations_1 = require("../sfAPIOperations");
const fileUtil_1 = require("../fileUtil");
let SOQLView = /** @class */ (() => {
    class SOQLView {
        constructor(context) {
            this.currentPanel = undefined;
            SOQLView.isAppend = 'no';
            SOQLView.oldSoqlString = '';
            this.strippedSoql = '';
            this.promisifiedWithProgress = (soqlString, userName) => new Promise((resolve, reject) => {
                let message = 'Fetch successful';
                vscode.window.withProgress({
                    location: vscode.ProgressLocation.Notification,
                    title: "Fetching records......",
                    cancellable: false
                }, (progress, token) => __awaiter(this, void 0, void 0, function* () {
                    console.log(progress, token);
                    try {
                        const conn = yield sfAPIOperations_1.SFAPIOperations.getConnection(userName);
                        const records = yield sfAPIOperations_1.SFAPIOperations.fetchRecords(conn, soqlString);
                        records.forEach(function (index) { delete index.attributes; });
                        console.log('runSOQL: ', records); // This line is just to check connection validity
                        vscode.window.showInformationMessage(message, { modal: false });
                        resolve(records);
                    }
                    catch (error) {
                        message = 'Unable to fetch records';
                        vscode.window.showErrorMessage(error.message, { modal: false });
                        reject(error);
                    }
                }));
            });
            this.context = context;
        }
        getCommentStrippedSOQL(soqlString) {
              let soqls = soqlString.split(';');
              let result = soqlString;
              for (let i = soqls.length - 1; i > -1; i--) {
                    if (soqls[i].trim() != '' && soqls[i].trim().startsWith('--') == false) {
                          result = soqls[i].trim();
                          break;
                    }
              }
              console.log(result);
              return result.trim().startsWith('--') ? '' : result;
        }
        runSOQL(soqlString, username) {
            return __awaiter(this, void 0, void 0, function* () {
                let records = [];
                console.log("runSOQL.userName: ", username);
                this.strippedSoql = soqlString;
                let stripped = this.getCommentStrippedSOQL(soqlString);
                if (stripped == '') 
                    return records;
                records = yield this.promisifiedWithProgress(stripped, username);
                SOQLView.queryResult = records;
                SOQLView.oldSoqlString = SOQLView.isAppend == 'yes' ? SOQLView.oldSoqlString + soqlString : soqlString;
                this.strippedSoql = stripped;
                return records;
            });
        }
        generateWebView(soqlString, isAppend) {
              let soqlStr = isAppend == 'yes' ? SOQLView.oldSoqlString + soqlString : soqlString;
            return `<!DOCTYPE html>
                <html lang="en">
                <head>
                    <meta charset="UTF-8">
                    <meta name="viewport" content="width=device-width, initial-scale=1.0">
                    <title>SOQL</title>
                </head>
                <body>
                    <div style="width: 100%; mqrgin-top: 2% !important">
                        <textarea class="soql-textarea" id="soql-textarea" name="soql" rows="6" oninput="getCurrentSoqlString(this.value);">${soqlStr}</textarea>
                    </div>
                    <div class="buttons-div">
                        <button class="query-button" onclick="runSOQL();">Run Query</button>
                        <button class="clipboard-button" onclick="copyToClipboard();">Copy to Clipboard</button>
                        <input type="checkbox" id='isAppend' onclick="toggleAppendMode(this.checked);" >Append SOQL</input>
                        </div>
                    <div id="query-result-container" style="overflow-x:auto; overflow-y:auto;">
                        
                    </div>
                    <script>
                        const vscode = acquireVsCodeApi();
                        function runSOQL() {
                            const soqlString = document.getElementById("soql-textarea").value;
                            vscode.postMessage({
                                command: 'runSOQL',
                                text: soqlString
                            });
                        }

                        function copyToClipboard() {
                            const soqlString = document.getElementById("soql-textarea").value;
                            vscode.postMessage({
                                command: 'copyToClipboard',
                                text: soqlString
                            });
                        }

                        const flattenObject = function(ob) {
                            var toReturn = {};
                            
                            for (var i in ob) {
                                if (!ob.hasOwnProperty(i)) continue;
                                
                                if ((typeof ob[i]) == 'object') {
                                    var flatObject = flattenObject(ob[i]);
                                    for (var x in flatObject) {
                                        if (!flatObject.hasOwnProperty(x)) continue;
                                        
                                        toReturn[i + '.' + x] = flatObject[x];
                                    }
                                } else {
                                    toReturn[i] = ob[i];
                                }
                            }
                            return toReturn;
                        };

                        function splitSOQLString(soqlString) {
                              console.log('soql:' + soqlString);
                            let index = soqlString.search(/FROM/i);
                            console.log(index);
                            console.log(soqlString.indexOf('from'));
                            return soqlString.slice(0, index).replace(/^(SELECT)/i,"").trim().split(',').map(item => item.trim().toLowerCase());  
                        }

                        function renderTable(records,oldSOQLString) {
                            let soqlString = "";
                            if(!oldSOQLString) {
                                soqlString = document.getElementById("soql-textarea").value;
                            } else {
                                soqlString = oldSOQLString;
                            }
                            const fieldsArray = splitSOQLString(soqlString);
                            let flattenedRecords = [];
                            for(let record of records) {
                                  let newObj = keyToLowerCase(record);
                                flattenedRecords.push(flattenObject(newObj));
                            }

                            let queryContainer = document.getElementById("query-result-container");
                            queryContainer.innerHTML = "";
                            if(flattenedRecords.length > 0) {
                                queryContainer.appendChild(generateHTMLtable(fieldsArray, flattenedRecords));
                            } else {
                                queryContainer.innerHTML = "<H4>No Records Returned.</H4>";
                            }
                            
                        }

                        function keyToLowerCase(obj) {
                            let key, keys = Object.keys(obj);
                            let n = keys.length;
                            let newobj={};
                            while (n--) {
                                key = keys[n];
                                newobj[key.toLowerCase()] = obj[key];
                            }
                            return newobj; 
                        }
                        
                        function generateHTMLtable(fieldsArray, flattenedRecords) {
                            let soqlTable = document.createElement('TABLE');
                            soqlTable.classList.add("soql-table");
                            soqlTable.innerHTML = "";                    
                            var columnCount = fieldsArray.length;
                    
                            let theadRow = soqlTable.insertRow(-1);
                            for (let column of fieldsArray) {
                                var headerCell = document.createElement("TH");
                                headerCell.innerHTML = column;
                                theadRow.appendChild(headerCell);
                            }
                            soqlTable.appendChild(theadRow);
                            
                            let tBodyElement = "";
                            for (let record of flattenedRecords) {
                                row = soqlTable.insertRow(-1);
                                for (let field of fieldsArray) {
                                    var cell = row.insertCell(-1);
                                    cell.innerHTML = record[field] ? record[field] : "";
                                }
                                soqlTable.appendChild(row);
                            }
                            return soqlTable;
                        }

                        // Handle the message inside the webview
                        window.addEventListener('message', event => {
                            const message = event.data; // The JSON data our extension sent

                            switch (message.command) {
                                case 'displayQuery':
                                    console.log('displayQuery in html');
                                    console.log(message.records);
                                    renderTable(message.records, message.soqlString);
                                    break;
                                case 're-renderTable':
                                    console.log('SOQLView.queryResult: ',message.records);
                                    console.log('SOQLView.oldSoqlString: ',message.soqlString);
                                    renderTable(message.records, message.soqlString);
                                    break;
                                case 'setIsAppend':
                                    console.log('isappend:'), message.flag;
                                    document.getElementById('isAppend').checked = message.flag == 'yes' ? true : false;
                            }
                        });
                        
                        function toggleAppendMode(isChecked) {
                            let isAppend = isChecked ? 'yes' : 'no';
                            vscode.postMessage({
                                command: 'append',
                                text: isAppend
                            });                            
                        }
                        
                        function getCurrentSoqlString(soqlString) {
                            vscode.postMessage({
                                command: 'currentSoql',
                                text: soqlString
                            });                               
                        }
                    </script>
                    <style>
                        body.vscode-light {
                            color: black;
                        }

                        body.vscode-dark {
                            color: #a8abaff2;
                        }
                      
                        body.vscode-high-contrast {
                            color: red;
                        }

                        .query-button {
                            margin: 1%;
                            background-color: #0a77e8;
                            border-color: #0a77e8;
                            padding: 5px;
                        }

                        .clipboard-button {
                            margin: 1%;
                            background-color: #8a8f92;
                            border-color: #8a8f92;
                            padding: 5px;
                        }

                       .soql-textarea {
                            width: 100%;
                            font-size: medium;
                            color: inherit;
                        }


                        body.vscode-dark .query-button {
                            color: #eaf1f1;
                        }

                        body.vscode-light .query-button {
                            color: #f8f8f9;
                        }


                        body.vscode-dark .clipboard-button {
                            color: #eaf1f1; /*#f8f8f9*/
                        }

                        body.vscode-light .clipboard-button {
                            color: #f8f8f9;
                        }
                        

                        body.vscode-dark .soql-textarea {
                            color: #a8abaff2;
                            background-color: #2d38454f;
                        }
                        
                        body.vscode-light .soql-textarea {
                            color: #46484af2;
                            background-color: #bfc6ce4f;
                        }

                        body.vscode-dark .soql-table, td, th {
                            border: 1px solid #eaf1f185; /*#474a4a85*/
                        }

                        body.vscode-light .soql-table, td, th {
                            border: 1px solid #474a4a85;
                        }
                        
                        table.soql-table {
                            border-collapse: collapse;
                            width: 100%;
                            height: auto;
                            
                        }
                        
                        th {
                            height: 30px;
                        }
                    </style>
                </body>
                </html>`;
        }
        displaySOQL(soqlString, username) {
            console.log("userName: ", username);
            const columnToShowIn = vscode.window.activeTextEditor
                ? vscode.window.activeTextEditor.viewColumn
                : undefined;
            if (this.currentPanel) {
                // If we already have a panel, show it in the target column
                this.currentPanel.webview.html = this.generateWebView(soqlString, SOQLView.isAppend);
                this.currentPanel.name = `${username} - Query Runner`;
                console.log('SOQLView.queryResult in currentPanel: ', SOQLView.queryResult);
                console.log('SOQLView.oldSoqlString: ', SOQLView.oldSoqlString);
                if (SOQLView.queryResult) {
                    this.currentPanel.webview.postMessage({ command: 're-renderTable', soqlString: SOQLView.oldSoqlString, records: SOQLView.queryResult });
                }
                this.currentPanel.reveal(columnToShowIn);
            }
            else {
                // Otherwise, create a new panel
                this.currentPanel = vscode.window.createWebviewPanel('SOQL', `${username} - Query Runner`, vscode.ViewColumn.One, {
                    enableScripts: true
                });
                this.currentPanel.webview.html = this.generateWebView(soqlString, SOQLView.isAppend);
                this.currentPanel.webview.postMessage({ command: 're-renderTable', soqlString: SOQLView.oldSoqlString, records: SOQLView.queryResult });
                // Reset when the current panel is closed
                this.currentPanel.onDidDispose(() => {
                    this.currentPanel = undefined;
                }, null);
                // Handle messages from the webview
                this.currentPanel.webview.onDidReceiveMessage((message) => __awaiter(this, void 0, void 0, function* () {
                    switch (message.command) {
                        case 'copyToClipboard': {
                            fileUtil_1.FileUtil.copyToClipboard(message.text);
                            //this.currentPanel.webview.postMessage({ command: 'Copied'});
                            return;
                        }
                        case 'runSOQL':
                            {
                                console.log('message.command: ', message.command);
                                const records = yield this.runSOQL(message.text, username);
                                console.log('records in panel: ', records);
                                this.currentPanel.webview.postMessage({ command: 'displayQuery', records: records, soqlString: this.strippedSoql });
                                return;
                            }
                        case 'append':
                            {
                                  console.log('message.command: ', message.command);
                                  SOQLView.isAppend = message.text;
                                  if (SOQLView.isAppend == 'no') {
                                        SOQLView.oldSoqlString = '';
                                  }
                                  return;
                            }
                        case 'currentSoql':
                            {
                                  console.log('message.command: ', message.command);
                                  SOQLView.oldSoqlString = message.text;
                            }
                            return;
                    }
                }), undefined, this.context);
            }
            this.currentPanel.webview.postMessage({ command: 'setIsAppend', flag: SOQLView.isAppend });
        }
    }
    SOQLView.queryResult = undefined;
    return SOQLView;
})();
exports.SOQLView = SOQLView;
//# sourceMappingURL=soql.js.map

 

posted @ 2020-08-17 10:33  平静寄居者  阅读(331)  评论(0编辑  收藏  举报