关于Salesforce的15位ID与18位ID

众所周知,Salesforce的Id有15位和18位两种。
18位ID的前15位与15位版本相同。
比如,有一条Account,其URL上的Id为0016F00002Dbbt5。
其中头三位001为Account的prefix(关于prefix,参照《salesforce的prefix和suffix》)
四到六位的6F0为Org Id的第4到6位。由于OrgId的唯一性,所以每个ID在整个SFDC世界都是唯一的。

同样是这条Account,使用工具取得的Id为18位的0016F00002Dbbt5QAB。
前15位与15位版本的ID相同,后3位则是根据前15位计算得来。

插个题外话,Org Id为00D开头的15或18位ID,第四位代表org所属的Instance。
比如说,我的OrgId为00D6F0XXXXXXXXX,那么此org所属的Instance是6对应的NA4。
第四位代码与Instance的对应关系表,请参照《Instance代码对照表
通过这个对照表,可以轻松的通过Org Id知道其所属的Instance。

包括某些官方文档在内,都将15位ID称为15位大小写敏感ID(the 15-character case-sensitive ID),18位ID称为18位大小写不敏感ID(the 18-character case-insensitive ID)。
那么问题来了,假如18位ID大小写不敏感,那是否就意味着
1. 0016F00002Dbbt5QAB
2. 0016F00002DBBT5QAB (全大写)
3. 0016f00002dbbt5qab (全小写)
代表同一条数据吗?
继续阅读“关于Salesforce的15位ID与18位ID”

Salesforce的Custom Permission

Salesforce的权限体系我相信大家已经很熟了。
数据级别权限,表级别权限,字段级别权限,功能权限,分别坐落在Profile/Permission Set和role/Sharing rule当中,还有一些是作为Feature License放在了User上。

可是对于自定义功能,SFDC并没有提供太好的控制方式。
逼的广大开发人员不得不开各种脑洞。
比如在人事系统里,HR和领导能看见员工评价,普通员工看不到员工评价,
这用Profile的FLS简简单单地就能控制了。
但是在所有人都能看到的VF上,假如有个员工评价Section不想给普通员工看的话,就不好办了。
在VF里可以判断当前User的Profile,判断一下当前用户的Profile在不在允许列表里不就行了嘛。
好家伙,一打听,500个Profile能看。这不行啊,还是看看谁不能看吧,呦呵,800个Profile。
开发人员就开脑洞了,我把能看的Profile的末尾加一个“VIP”,不能看的都没有。接着就动手把500多个Profile一个一个的改了名字。
然后在VF里判断User的Profile结尾是VIP的才显示。
妥了,实现,下班。
第二天,经理过来搂住你的肩旁,说,昨天那个功能实现的很好,今天还要再加一个Section,和昨天的差不多,不过普通员工Level里的组长可以也可以看。开发人员想了想,总不能再给Profile加一个后缀吧。就算这次可以,那以后再来一个怎么办。。。。。
后来,听说开发人员被120拉走了。

像这种自定义功能的权限控制,SFDC提供了Custom Permission,作为Profile与Permission Set向自定义功能领域的延伸。
说白了,就是Profile与Permission Set原本能控制的都是标准功能,Custom Permission就是一个插件/拓展包,赋予了Profile与Permission Set控制自定义功能的能力。

所以遇见上面的场景,我们只需要针对每一个Section建一个Custom Permission,
然后将这些Custom Permission挂到需要此权限的Profile或者Permission Set,
然后在VF中写上Custom Permission的判断。
如下方官方文档提供的例子一样

<apex:pageBlock rendered="{!$Permission.[Your Custom Permission API Name]}">
   <!-- Confidential Content Here -->
</apex:pageBlock>

在Apex中想用Custom Permission控制业务逻辑的话,
就要使用下面的方法,官方文档

if(FeatureManagement.checkPermission([Your Custom Permission API Name])) {
    // Your Codes Here
}

这样一来,想控制一个群体,就挂在Profile上,想控制个别人,就挂在Permission Set上。
为什么叫“挂在”呢,因为只有在Profile和Permission Set下面才有添加Custom Permission的按钮。

那么,想创建一个Custom Permission,在哪创建呢?
第一种方式是官方文档常写的 —– 点击Setup,然后左上方Quick Find,输入Custom Permissions。
第二种方式是按照本博客的风格 —– Setup -> Build -> Develop -> Custom Permissions。
Custom Permission本身需要定义的内容倒是不多,就一个Label一个API Name是必须的,其他都可有可无。

虽然Custom Permission只是Profile与Permission Set的功能拓展,
但由于在VF中官方提供了方便的检查方式($Permission),倒是给我们提供了另外一个用途,
正所谓,有心栽花花不开,无心插柳柳成荫。

继续阅读“Salesforce的Custom Permission”

URL Hacking之动态report条件

上回书说道(《salesforce的field-id与url-hack》),URL Hacking可以用来跳转到指定的页面与设定新建页面的default值。

除此之外,URL Hacking还可以用来改变report的条件。
比如说,有这样的业务场景————客户想知道当前访问的Lead都被哪些特定种类Campaign纳入为Campaign Member。
有的同学说了,这个简单啊,不就是Releted List嘛,我知道。
但是,由于Releted List无法进行过滤筛选,所以所有种类的Campaign信息都混在了一起,客户不矫情的话也许勉强凑合着用了。

除了做VF页面之外,还可以使用URL Hacking来实现。
首先,在Lead的详细画面上放一个custom link,点击之后打开一个Campaign Member的report。
然后设置好report的Show范围为“All Campaigns”,如果是其他report,还要设定好时间范围。
只要实现每次点击custom link跳转到report时候,都能动态的设置campaign的record type与当前lead的Id,就可以实现。

// 接下来是重点
继续阅读“URL Hacking之动态report条件”

本博客引用Salesforce官方文档的说明

最近有一些同学说我写的文章骗人。
弄得我一脸黑人问号。

深入交流了一下才知道,是说我提供的所有官方链接都打不开。
无论点开哪个链接都是404。

这里我不得不澄清一下,
我所引用的所有链接都是基于英文版的sfdc文档。
由于并不是所有的官方英文文档都有对应的中文版,
所以,浏览器默认语言是非英语的同学,很容易会碰到404错误。

看到404没关系,如下图,将语言改成英语就可以了。

最后,还是想说一句,毕竟sfdc是英语母语者开发的,想好好做sfdc还是学好英语吧。

不小心Commit账户密码到本地仓库,并且push到了remote仓库怎么办

今天我就做了这个傻事。
不过还好是私有仓库,没有酿成泄密事件。

那么这种情况该怎么处理呢?
首先,把账户密码改掉,再Commit一次是肯定不行的。
看worktree,账户密码确实是没了,但是在Commit记录里仍然能看到的。

别急,Stackoverflow上我找到了一个简单粗暴的做法,直接抹掉全部历史!
https://stackoverflow.com/questions/13716658/how-to-delete-all-commit-history-in-github

首先你需要一个git命令行工具,Eclipse自带的git插件是玩具。

1.创建一个独立(孤儿)的分支,并Checkout

git checkout –orphan latest_branch

2.将目前的文件全部add到此分支

git add -A

3.全部Commit到孤儿分支

git commit -am “commit message”

4.删除带有历史信息的Master分支

git branch -D master

5.将没有任何历史的孤儿分支重命名为Master

git branch -m master

6.强制推送到Remote仓库

git push -f origin master

代价就是所有历史都没有了。
当然,我们有更精细的方式,可以剔除包含敏感信息的commit

Salesforce的Add Campaign Member

// 更新 2018-10-12
该Issue已经被解决。

// 最下有更新 2018-05-08

最近发现了一个好玩的现象。
作为Salesforce Marketing核心功能之一,标准功能Campaign的出镜率非常高。

这次的问题就出在向Campaign添加Campaign Member上。

除了使用DataLoader与Data Import Wizard等数据导入工具以外,Salesforce在标准画面上提供了三个添加Campaign Member的渠道。
第一种为最为常见的方式,通过Campaign详细画面下方Campaign Member关联列表上的Manage Members按钮进行添加。

第二种方式,通过Contact/Lead的ListView画面上的Add to Campaign按钮。

第三种方式,通过Report上的Add to Campaign按钮。

不过以上三种方式,只支持向单个Campaign添加Members。

问题是这样的,
假如我有一个Campaign叫做TestCampaign,有500个Contact叫做TestContact000~Test499。
如果没有任何意外发生,三种方式的行为是一致的,结果均是成功将500条Contact做成了Campaign Member。

接下来我们做实验。
首先,在Contact上添加一个Text字段,叫testValidation__c。
然后在Campaign Member上添加一个Validation Rule,拦截条件为Contact.testValidation__c等于“E”。
之后将TestContact000的testValidation__c设置为”E”。

分别使用三种方式,将TestContact001~500添加到TestCampaign。

结果,行为出现了偏差。
对于第二种和第三种方式,

由于有一条“错误”数据,导致整个导入Campaign Member动作失败。

而最常见的第一种方式,却出现了不同的结果。

首先,错误信息与其他两种方式一致,并且没有进行画面跳转。


但是,如果我们进入Existing Members画面,就会赫然发现,里面居然进去了99条。

观察数据编号,发现进去的数据为TestContact001到TestContact099。就是说,除了“错误”的TestContact000,前100条的剩余数据都成功做成,100条以后的全都被放弃了。

这样的话,我猜想Add Member功能的机制是这样的———————选完数据,开始执行之后,后台会将数据按顺序分为100条一个批次,加入某个批次出现了错误数据,则其余数据插入,处理终止。

为了证实这个猜想,我又实验了将TestContact101设定为“错误”数据的情况,果然如我所料,进入了199条数据,为TestContact000~TestContact100及TestContact102~TestContact199。

对于同样一个功能,不同入口却出现了不同的行为模式与处理方式,实在是无法理解。
关于100条一个批次的问题,我猜应该是一个Salesforce的历史遗留问题,因为曾经Trigger虽然最多一起处理200条数据,但曾经后台还是按100一批处理的,导致有一些代码写法会莫名的只对前100条生效。

也许,又是一个Bug。

// 2018-05-08 Update

SFDC承认了这是一个Bug。Agent还贴心的提了一个Known Issue。
https://success.salesforce.com/issues_view?id=a1p3A000000nXgTQAU
遇到相同问题的同学请mark一发。

关于example.com

每当刷完Sandbox,Salesforce为了防止从Sandbox发出的邮件使用户混肴,
所以将所有User的邮箱的@替换为=,然后加上@example.com
比如,User A的邮箱为[email protected],则变换为[email protected]

如果向这种邮箱发出邮件,邮件会发到哪去呢?

有同学说了,这个邮箱肯定不存在的,所以谁也收不到。
这可未必。如果了解SMTP服务器的话,就知道有种catch all账号,可以收到所有发往此邮件服务器的邮件,无论收件地址是否存在。
所以用公司邮箱乱发东西的同学们注意了,千万别用公司邮箱乱说话呦。

所以我们就来看看这个example.com
首先用浏览器访问,还真的能直接访问,白白的背景上,正中间方方正正的写着

Example Domain

This domain is established to be used for illustrative examples in documents. You may use this domain in examples without prior coordination or asking for permission.

More information…

大概意思是说,这个域名就是给你在文档里做说明示例用的。随便用,不用经过我同意。
呦!谁这么大方?

顺手Whois了一下这个example.com
注册商是RESERVED-Internet Assigned Numbers Authority

这个Internet Assigned Numbers Authority是哪路神仙?
原来是大名鼎鼎的IANA(互联网号码分配局)。
什么?没听过,那么ICANN总听过吧,
Internet Corporation for Assigned Names and Numbers,互联网名称与数字地址分配机构。
如果购买域名的话,每年都要向ICANN交管理费的。
这个IANA就是ICANN的下属机构。
这就是说,这个域名居然还是官方所有的。

随后,点了一下页面上的More information…
了解到了整个事情的始末。

原来根据RFC2606
一级域名
.test
.example
.invalid
.localhost
二级域名
example.com
example.net
example.org
都为特殊目的所保留。

RFC6761则规定了具体的做法。

// 小插曲,很Interesting的是,RFC2606的作者是IBM雇员 Donald E. Eastlake 3rd与Aliza R. Panitz, 而RFC6761的作者为Apple Inc.的雇员Stuart Cheshire与Apple Inc.的雇员Marc Krochmal

网页中明确的说明了Certain domains are set aside, and nominally registered to “IANA”, for specific policy or technical purposes.
甚至提供了IDN的保留域名。

就像好莱坞电影里以555区号开头的电话一样,互联网也保留了这种安全域名用来举例子。

根据RFC6761中的提到的,

IANA currently maintains a web server providing a web page explaining the purpose of example domains.

所以只有http请求才会被受理以展示那个说明页面。
你的邮件无法找到example.com的MX记录,
虽然可以落到A记录,顺利的拿到IP,但IANA并没有建立监听25端口的SMTP服务,
所以,邮件发送会失败。

Salesforce关于Update Records with Inactive Owners权限

// 老鸟直接去下面看重点

Salesforce作为平台,无论是Standard Object还是Custome Object,都提供了默认的系统字段。
这些字段普通用户无法直接更新与编辑,只有系统才可以修改,以此作为审计信息,保证数据的可靠性。

但是,由于并不是每个使用Salesforce的组织都刚刚步入信息化。
所以,从Legacy System迁移到我们全球最先进的CRM系统——Salesforce的时候,必然会涉及到数据迁移。
有的同学说了,数据迁移简单啊,把数据从原来的系统抽出来,然后插入到Salesforce里。
我们知道,如果直接导出,再插入的后果是导致创建日期等一众字段都会变成数据导入那天。
那么,如果有功能是基于创建先后顺序的时候该怎么办,五年前的数据将会比昨天创建的数据更靠前。

为了应对这种情况,Salesforce提供了Set Audit Fields upon Record Creation与Update Records with Inactive Owners两个权限。
开启的方法为:
1. Setup -> Build -> Customize -> User Interface -> Enable “Set Audit Fields upon Record Creation” and “Update Records with Inactive Owners” User Permissions 勾选
2. 创建一个PermissionSet将Set Audit Fields upon Record Creation与Update Records with Inactive Owners分别勾选。将此PermissionSet分配给需要的User。
3. 在Custom Profile里将Set Audit Fields upon Record Creation与Update Records with Inactive Owners分别勾选。
这里需要注意的是,System Admin是标准Profile,所以只能使用PermissionSet。

Set Audit Fields upon Record Creation顾名思义,就是在Insert数据的时候,可以设定Audit Fields。一次操作成型,不能再Update。
继续阅读“Salesforce关于Update Records with Inactive Owners权限”

Salesforce的Prefix和Suffix

行家一出手,就知有没有。
判断一个Salesforce从业者是不是老手的一个标准,
就是能不能对Governor Limit,Sobject的Prefix和Suffix倒背如流。

Governor Limit之前已经介绍过了,
Salesforce说了,当你觉得Governor Limit限制了你,那么意味着你的业务流程没有达到最优。

那么Prefix是什么呢。
Prefix就是俗称的3位码。
每个SObject都有一个3位编码,每一条该SObject的数据的头三位,都是以此3位编码开头。
如果把三位编码放在salesforce的url后面,可以快速的访问SObject的ListView页面。
https://[your instance name].salesforce.com/[prefix]

标准SObject的prefix都是固定的,
比如,Account一定是001
Contact一定是003
Lead一定是00Q
而自定义SObject就没有固定的编码了,但都以aXX开头。

Salesforce提供了一本速查手册
列出了所有标准SObject的prefix。
如果想得到一个全体SObject的花名册,可以去实现一下tooling api。
所有标准的,自定义的,还有不可见的SObject的Prefix都列出来了。

那么,Suffix呢,大家最熟悉的莫过于”__c”,
所有的自定义SObject的Suffix,所有自定义字段的Suffix,都是它。
那么除了__c之外,还有哪些呢。
国外大神总结在此
防丢失转载如下:

__c Custom Object or Custom field
__r Custom relationship field as used in a SOQL query to traverse the relationship
__ka KnowledgeArticle
__kav KnowledgeArticleVersion
__Feed Article Type Feed or Custom Object Feed
__ViewStat KnowledgeArticleViewStat
__VoteStat KnowledgeArticleVoteStat
__DataCategorySelection Article Type__DataCategorySelection
__x External Object
__xo Salesforce-to-Salesforce (S2S) spoke/proxy object
__mdt Custom Metadata Type
__Share Custom object sharing object
__Tag Salesforce Tags
__History Field History Tracking for Custom Objects
__pc Custom Person Account Field
__pr Used for traversing custom Person Account relationship fields
__hd Historical Data
__hqr, __hst Start/End of the Datetime range
__b BigObject
__latitude__s Geolocation Latitude Coordinate
__longitude__s Geolocation Longitude Coordinate
__e Platform Events – a.k.a. EventBus event (Winter ’17 pilot)
__p Custom Person Object (Spring ’15 pilot)

————By Daniel Ballinger

Salesforce的Field Id与URL Hack

默认值是个好东西。
别说是普通用户,就算是开发者,在面对满满一屏的空白字段时,也是要不禁皱皱眉头的。

设置得当的默认值可以极大的提高业务流畅度与用户满意度。
反之,每次都不得不改的默认值,会带来加倍的苦恼。(甚至会惹恼用户)

当然,Salesforce提供了字段的Default Value功能。
我们可以为大部分字段类型设置默认值。

Default Value必须为公式,并且不能使用其他字段的值,只可以使用预设的系统变量和环境变量。
因为这样的限制,使得我们在使用自定义button或者link跳转到Object标准编辑画面的时候,无法根据情况显示不同的默认值。

举个例子,比如Case上面有个字段用来储存Case的类型,然后在Case的详细画面上放置一个自定义Button,这个button用来新建一个order。
为了提高Agent的工作效率,弹出order的新建页面之后,我们希望order的类型是Case上所选择的值,并且与Case关联同一个Account。

这种场景下,字段的Default Value已经无法满足需求。
我们需要使用一种Salesforce并不提倡的技巧———-URL Hack。

URL Hack就是通过在Salesforce的URL上加上额外的参数,来实现我们的需求。
其中一种技巧,就是给Standard Pagelayout赋值。

那么让我们来看看URL Hack到底是什么样子。
首先,点击Case标签里的新建Button。
跳转到Case的新建画面之后,观察地址栏的URL如下
无RecordType: https://[InstanceName].salesforce.com/500/e?retURL=%2F500%2Fo
有RecordType: https://[InstanceName].salesforce.com/500/e?retURL=%2F500%2Fo&RecordType=0126F000000v348&ent=Case

其中,500代表Case,每个Object都有一组三位码,同时也意味着所有的数据的Id都是由这三位码开头。
e代表编辑页面。 500/e代表新建数据,500XXXXXXXXX/e代表修改既存数据。
retURL代表回调页面,当点击取消Button之后,会返回到指定的页面。在例子中,%2F代表“/”,所以会返回500/o。
有RecordType的URL多了一个RecordType参数,值0126F000000v348就是recordType的Id。

所以,RecordType以外的字段,我们也可以通过同样的方式进行赋值。

首先,与RecordType不同的是,我们需要用的并不是字段的API名,而是Field ID。
如果不使用工具的话,Field Id并没有快捷的查看方式,所以应急的话,可以直接打开编辑画面,然后用浏览器的F12开发者工具直接查看。

标准字段是字母+数字的形式,自定义字段则是以00N开头的ID。
就拿上面的例子来说,如果将“123456”赋给Account Number,URL应写为
https://[InstanceName].salesforce.com/500/e?acc5=123456&retURL=%2F500%2Fo

如果是Lookup字段的话,上面那样写就行不通了,要在Field Id后面加上_lkid后缀
标准字段的话,我们以Account的Parent Account为例
https://[InstanceName].salesforce.com/001/e?acc3_lkid=001XXXXXXXXX

自定义字段的话,Lookup字段还需要在00N前加上CF
https://[InstanceName].salesforce.com/001/e?CF00NXXXXXXXXX_lkid=001XXXXXXXXX

有的时候只写lkid结尾的参数看起来并不管用,在编辑页面上,Lookup字段仍然是空白的,但实际上保存之后是已经被赋值的。
如果遇到这种现象,就要加额外加一个参数
https://[InstanceName].salesforce.com/001/e?acc3_lkid=001XXXXXXXXX&acc3=RecordName
https://[InstanceName].salesforce.com/001/e?CF00NXXXXXXXXX_lkid=001XXXXXXXXX&CF00NXXXXXXXXXX=RecordName

最后记住,一定要养成随时设置retURL的好习惯。

上述所有的值在Custome Button或者Link中,都可以用表达式替换,比如
00NXXXXXXXXXXXX={!Case.Status}等。

如果想了解URL Hack还能做哪些事情,之后会写一篇专访。