Salesforce关于Debug

做Salesforce开发,最让人头疼的事情,莫过于Debug。

不同于Java开发,已经有了一系列成熟的Debug工具,
Salesforce开发者最常用的手段只有输出log,就如同Javascript开发一样。
不过随着现代浏览器的兴起,就算是Javascript开发也可以debug了。

对于一个老手来说,添加System.debug()然后查log,虽然已经足够。
但是,面对巨型transaction的时候,就要面临Debug Log的限制
1. log总体大小不能超过3MB,否则随机截断(不管官网怎么说,我觉得是随机的,囧)
2. log只能存留七天
3. 15分钟内产生的log不能超过250MB

为了回避此问题,Salesforce提供了Class和Trigger的Debug Log级别自定义功能。官网点此
启用这个功能,可以在Class和Trigger的详细页面,找到TraceFlag,在此设定需要的Debug log级别,(一般全开最详细,或者全关)
自定义完成后,transaction中class和trigger产生log时的级别设定,就会覆盖默认的debug log级别设定。
使用此功能可以极大的减少不需要的log输出,从而避免被随机截断。

但是,System.debug必然不是万能的。

所以在DeveloperConsole中,Salesforce给开发者提供了CheckPoint功能,注意,不是BreakPoint,是CheckPoint。
Checkpoint不同于BreakPoint那样可以互动。只提供了信息查看功能。
就是说,在代码中打上Checkpoint之后,程序执行并不会暂停,而是执行到结束,但Salesforce会将Checkpoint标记位置的内存状态保留下来供开发者查看。
需要值得注意的是,Checkpoint全局只能打5个。

如果代码真的复杂到checkpoint和Debug log都无法搞定,
Salesforce提供了一个真正的Debug工具——Apex Debugger
有了Apex Debugger,Salesforce开发者也可以像Java程序员一样debug了。
不过越强大的武器,限制越多。
Apex Debugger只提供了一个免费的License,并且是全Org通用,就说Production和旗下所有sandbox合起来同时只能有一个人用。

不管使用哪种Debug方式,都应该从设计伊始就避免设计出如此巨大的自定义功能。

Salesforce的Sharing Rule中Criteria Rule如何判断空值

某日,客户提了一个需求,
说想把某个字段是非空的数据Share给另一个部门。

看起来是个很简单的需求,
就是一个Sharing Rule的事儿。

然后,立马新建了一个Sharing criteria rule,
设定条件为[ 字段 not equals to 空值 ],保存。唉?报错了。
那么空格呢?也不行。

Error: Empty value

难道,Sharing Rule不能判断空值?
正在百思不得其解之时,经高人指点,说空值的位置写成[“”]就可以了。

赶紧试了一下,果然成功了。

保存后的效果是这样的。

Salesforce如何用Import Wizard将空值插入PicklistValue字段

如果Picklist Value没有定义为必填字段。
则多出一个–None–选项可以选,
在DB里存为空。

我们知道Dataloader默认会无视所有值为空的字段,
除非我们在设置里将[Insert Null Values]选中。

但是在Import Wizard中并没有提供类似的选项。
如果尝试将字段的值留空,一样会被无视,空格等一切空白字符也会被无视。

解决方法是,将想设置成空的字段的值改为[#N/A]。
之后再次导入就可以了。

Salesforce如何查看RecordType的Assignment

我发现Salesforce只提供了Profile-RecordType-Layout三角关系(Assignment)的查看页面。
并没有提供查看某个RecordType都有哪些Profile可以创建的视角。
只能一个一个Profile去点开。

思来想去,如果不用第三方工具的话,只能直接去搜Metadata了。

首先用ForceIDE或者其他方式获取Profile的完整MetaData。

然后,打开全局检索,勾选启用正则表达式。
然后用下面的字符串搜索全部的Profile
<recordType>[ObjectName].[RecordType Name]</recordType>\n.*true

搜出的结果就是能创建这个RecordType的Profile了。

也许将来能够像FLS一样,以recordType的视角直接查看Profile的Assignment情况。

Salesforce的Tag(标签)-一天一个标准功能系列

这两天有小盆有提到了一个需求,
说客户一直在用MacBook,
MacOS提供了一个标签功能,客户可以将一组散落在不同位置的,不同类型的文件,打上同一个标签,
之后可以很方便的管理同一标签的文件。
所以客户也想在Salesforce里面使用这种功能。
问我怎么办才好。

我问他,“你想怎么实现呢”
“我想,首先要确认都有哪些obj需要被打标签”
“很好,然后呢”
“然后,我可以在这些obj上都加上一个自定义字段,叫Tag,然后在所有的Layout上把这个Tag字段放出来。”
“唔。。。可以实现,但是,用户怎么查看都有哪些数据被打了相同的Tag呢?而且,如果多个用户都需要打Tag,怎么保证互不干扰,并且互相保密呢?”
“啊。。。。我想想,查看的话,我可以写一个VF,通过查询把所有的表都查一遍,多个用户就麻烦了。。。我想想。。。”
(过了半个小时)
我问道,“有想法了没?”
“有了!我不加字段了,那样不科学!”
“很好,那你想怎么做呢?”
“首先,我建一张表叫Tag__c。然后关联所有需要打Tag的obj。之后Sharing Rule设置成private。查看Tag的时候,用ListView进行过滤,把所有相同Tag名字的数据都显示出来。(得意)”
“那打标签的动作怎么解决。就是打算如何在数据上点一下就加上一个标签。”
“我打算用Quick Action或者自定义Button,将它们添加到所有的Layout上,这样在Detail画面直接点一下就能打上标签了。”
“那如果有个标签我想给所有人看呢?(坏笑)”
“那样的话我得用Sharing Rule,将Share to All Flag为True的数据Share给所有人。”
“累不?”
“累。。。。”
“你听说过标准Tag功能吗?”
“我就知道!!!”

标签,无论是在现实世界还是计算机世界,都是管理资源的良好手段。
所以Salesforce也原生提供了Tag功能,我们要做的,就是将其开启就好。
官方文档,点击这里

开启之后,查看标签的效果是这样的

点击[Personal Tags]之后

开启之后,在数据的详细画面上,可以点击右上的[Edit Tag]添加标签。

不过,Tag也不是无限打的,是有个数限制的

开启的方法为,
Setup -〉Build -〉Customize -〉 Tags -〉Tag Setting
开启Personal Tag之后,可以选择是否在Report等之上使用Tag,与显示Tag功能的PageLayout。

需要留意的是,Salesforce官方文档提到了两种Tag,分别是Personal Tag和Public Tag,
但是DE只能开启一种Personal Tag。
只有真正的Production才能开启Public Tag。

有了这个功能,用户可以跨越表关联,方便的管理一组数据了。

有同学质疑我的“一天一个”,我的意思是。。。。我花了一天时间才能写一个的。。。。

关于Salesforce的组成

小时候看CCTV播放的纪录片,里面讲抗日战争日期中国空军的事情。
到现在都能很清晰的记起其中一段。
当时是采访一位老兵,说当时根本没有时间进行系统训练,
所有的飞行员都是简简单单练练起飞降落开机枪就上天作战了,
根本没机会像现代空军一样,要系统的学习机械原理,飞机构造之类的才能上天。
简单的比喻就像开车一样,能把车开走不一定需要知道车为什么能开走,引擎是什么烧的,动力是怎么传导的。

做开发也一样。
特别是Salesforce。
作为Salesforce从业者,也许精通admin,玩转401,熟练掌握501。
但不一定需要了解Salesforce平台是由什么构成的。

对于大部分从业人员来讲,了解了这些也不一定对日常工作有什么帮助。更何况还有可能占用宝贵的娱乐时间。
而且虽然作为Salesforce从业者,仍然有大量的英语困难症患者(日语也一样),更成为了止步不前的借口。

其实还是建议所有想在Salesforce平台好好做下去的从业者,认认真真的读一读官方提供的Salesforce基本原理
毕竟想跳的更高,就要蹲的更深。

干了这些年,根据经验总结。
我把Salesforce里面所有的东西分成三类。
第一类是云计算的核心,Metadata元数据
第二类是数据
第三类是设定

Metadata是其中最好理解的,也是最难深入理解的。
有经验的人知道我们对系统做出的大部分(注意,并不是所有)改动都会在Metadata上有所体现。
我们在Deploy的时候,Deloy的具体source也是MetaData文件。
MetaData可以视为是定义了Org中的一切。
老手甚至可以通过手动编辑MetaData让系统变成自己想要的样子。

如果MetaData使用来定义这个系统是什么样子。
那么数据就是这个系统的血肉,真正让系统的存在有了意义。
做Java系统时间长了的人,总觉得系统=代码。
而Salesforce将数据的重要性提到了一个新的高度。
几乎所有的改动都在围绕着数据。
收集数据,处理数据,显示数据。
数据还要分为业务数据,功能数据,系统数据。
业务数据就是系统用户日常业务开展需要数据,客户信息啊,商品列表啊,这些也是开发人员轻易不能动的内容之一。
功能数据,就是为了实现一些功能,需要储存参数的临时表, Custom Setting,或者记录功能执行记录的数据。
这部分数据用户是看不到也不需要看到的,但是又是系统正常运转的必须部分。
最后还有大量的系统数据,这些数据默默的维持着系统的基本运转,其中有一些表甚至不对开发者开放。

在这个世界上, 事情从来不会非黑即白。如果MetaData和数据泾渭分明,世界还是很美好的。
但现实是残酷的。并不是如理想一般,这个东西不是属于MetaData,就是属于数据。
在Salesforce里面,存在这大量双轨制的东西。
比如Profile,Role,Apex,VF等等,
这些既存在于MetaData,又存在于数据中。这样就等于赋予了这些东西两种截然不同的属性。
MetaData和数据的根本区别在于什么呢?
MetaData是定义,可以通过工具部署。是骨架。
数据不需要部署,可以直接导入,直接修改。是血肉。
这些东西就如粒子一般,拥有波粒二象性。
这里跑题太严重,就不展开了。

除了MetaData和数据以外,还有一些看得见摸得着的东西,不属于任何一种。
我称呼它们为设定,它们拥有这最顽固的性质,
除了手改,无法部署,不能导入。而且存在于各个角落。
最复杂的情况比如,在Profile本身已经是双规制。
结果Session Timeout设置和Password Policies这个两个俨然应该隶属于Profile的设定,
却不存在于MetaData和数据中任何一方。
甚至Profile的Copy功能也会将这两个设定无视掉。

具体分类的名单,等整理完之后再发吧。

// Upadate1

我还是想简单了,其实Metadata和数据的界限要更模糊一点。
有些我原来以为只能通过Metadata修改的东西,最后在sfdc后台其实还是一张表。

古德尔的哥德堡变奏曲

人生中第一次听说哥德堡变奏曲,是通过汉尼拔医生。

在《汉尼拔医生》那本小说里,变态吃人心理医生汉尼拔在杀人之后,
在教堂里优雅地弹奏了一曲哥德堡变奏曲,并在专注之处,情不自禁的哼唱。
那种大师沉醉与演奏的画面感,使我迫不及待的找来听听看。

当时互联网还不是很发达,很辛苦才找到一个低音质的不完整版本。
而且演奏者也不知道是哪个路人。
所以并没有太多感觉。

多年之后,再次想起了巴赫,再次想起了哥德堡变奏曲。
相比于贝多芬,斯特劳斯,莫扎特和肖邦等,我更喜欢虔诚的巴赫。

可惜,出生的年代太晚,没有机会亲耳听大师们演奏自己的作品。
所以对于后世的我们,只能不断的挑剔着演奏者及乐队指挥。

年少时,曾疯狂的收集维也纳爱乐乐团演奏的贝多芬。
指挥也必须是卡拉扬。
我觉得,只有这个组合,才能真正的表达出贝多芬。
那个与命运抗争的贝多芬。

同样,每个演奏巴赫的钢琴家都有着自己对巴赫的理解,
每个钢琴家对巴赫的每一部作品都有自己的演绎。
在演奏技巧上无懈可击的,只能叫琴师,
真正的大师才能赋予灵魂。

历史上演奏过哥德堡变奏曲的钢琴家有很多,
我却唯独喜欢古德尔1981年录制版本。

古德尔是年少成名的天才演奏家。喜欢巴赫,不喜欢肖邦。
录制的第一张唱片是哥德堡变奏曲,录制的最后一张唱片也是哥德堡变奏曲。
在录制最后一次哥德堡变奏曲之后,第二年便与世长辞。

他在22岁时第一次录制哥德堡变奏曲,节奏欢快,清朗。
81年最后一次录制哥德堡变奏曲,更加沉稳,内省。
不变的,是古德尔在弹奏到忘情时一起被录进唱片的哼哼声和椅子的嘎吱声。
那就是他与巴赫进行交流的方式。

哥德堡变奏曲,巴赫疯狂的进行了30次精彩绝伦的变奏。
所以汉尼拔医生等一众变态都喜欢它。

古德尔和巴赫一样生性孤独,这两个相差两个多世纪的灵魂在哥德堡变奏曲中相遇。
最后也如同巴赫一样,刚步入中年就因病离世。
只留下了对巴赫伟大的诠释。

Salesforce的SplashPage(启动页)-一天一个标准功能系列

最近有小盆友考验我,说客户想点进Tab的时候先进入一个Guide页面,然后点击继续进入list页面,点击Don’t show again下次不再显示此页面,直接进入List页面。
问我该怎么做。

我反问,”你想怎么做?”
“首先呢,建一个Tab”
“废话。哪种Tab。”
“当然是Visualforce Tabs。”
“然后呢。”
“然后我创建一个VF,并且使用标准标签和样式。”
“嗯,为了让用户觉得自己没跳进VF是么?”
“没错,然后我在VF里选中用户点的Tab,之后在页面里写上我要的内容。”
“这个简单,那俩按钮你想怎么实现呢?”
“当然是建两个Button,然后在Controller里实现两个Action。一个Button直接跳转到List页面。另一个Button。。。。”
“Don’t show again button你想怎么实现呢?”
“我在User表上加一个字段,如果用户点了Don’t show again,我就更新一下User身上的Flag,然后在VF初始化的时候就判断User身上的Flag,如果是true就直接跳转。”
“唔。。。可行,你觉得复杂么?”
“还好吧。”
“听说过Splash Page么?”
“啥?”

Splash Page,启动页,醒目页面。是Salesforce提供的标准功能之一。
其作用是在Tab上添加一个启动页面,并且原生支持Don’t show again。
其效果如图

点击Tab之后,会显示指定的内容,并提供Don’t show again与Continue按钮。
不过,我发现只有Custom Object的Tab才可以指定Splash Page,标准Object的Tab不可以。
可能Salesforce觉得标准Object应该不会像Custom Object那样复杂到需要启动页来介绍功能吧。

那么接下来介绍该如何创建一个启动页。

首先,你需要创建Custom Link,此Custom Link为Home的Custom Link,而不是该Custom Object上的Custom Link。

创建的过程与创建button或者link相同,种类有三种,JS,VF或者URL。

然后随便创建一个VF,在此选择。

值得注意的是,一定要记得关闭标准标签和标准样式,否则就会出现像下面这样奇怪的事情。

关闭标准标签标准样式的写法。

<apex:page showheader="false" standardStylesheets="false">
......
</apex:page>

Custom Link设定好之后,去Tab的设置页面,指定这个Custom Link,大功告成。

Salesforce作为云计算平台,提供了强大的自定义开发能力。但是,现代应用开发思想是轻开发,重配置。常年从事SI的前JAVA从业者,很容易就陷入开发解决一切的思维黑洞。付出高昂的成本反复造轮子,因为其价值也只是在反复造轮子上了。利用标准功能快速的实现客户需求,才是未来的发展方向。大型开发的市场会一直存在,但将将来绝对不会是主流。

Salesforce中Picklist与翻译的故事

不同与Java系统,salesforce给Select或者Drop down,whatever你叫它什么的那个元素提供了一个很好的组织方式———-Picklist。

Picklist作为一种基本数据类型,在salesforce里任何人都可以使用其轻松的创建下拉列表,并动态的控制其中的选项。要知道,在java系统里,为了达到这个目的,可是要上三大框架的。如果不能用自定义标签库,那么在不hard coding的前提下,在画面上可控的显示一个select对于一个新手来讲,将是一个堪比穿越撒哈拉沙漠的挑战。当然,大部分项目都选择直接写死,或者使用Js操纵DOM。

不过Salesforce的Picklist与我们印象中的Select元素还是不同的。

在Spring 17之前(Spring 17 release note),picklist的值只有一个value,所以表示文本也是它,值也是它。所以好多系统就顺水推舟的在代码,validation rule等地方直接用长长的value内容做判断。
突然有一天,客户想对选项的内容做一个小小的改动。。。。。灾难了。
以前的话,更改选项的值,既存数据是不会跟着变的,要手动修数据。现在已经能够做到既存的数据自动跟着变了。但是那些代码里写死的判断等就不行了。

我们知道,html中的select是要分别设定value和label的!在页面表示label,表单里传的是value。这样根据定义好的数据字典,在程序里随便判断value。无论画面表示的本文怎么改都不会影响到程序。

所以sfdc的先贤们发明了一个绝招————翻译。

比如picklist的一个项目的value是1,则在翻译控制台将1翻译成想显示的文本,比如“A”。那么就实现了HTML的Select的特性—–显示的文本和后台传值分开。

正当群众们洋洋得意的时候,Spring17来了。给我们带来了picklist选项的Api Name,参见官方博客

抱歉,说的不严谨,应该是给我们带来了Picklist选项的Label。

就是说现在Picklist的选项有两个设定值值,一个是Label,一个是Api Name。Api Name其实还是我们以前熟悉的那个选项value,老朋友。新增加的是Label。

那么好玩的地方来了。以前Picklist选项只有一个value的时候,翻译就直接那么翻译了。当Label和Api Name分开之后,翻译是针对谁来进行翻译呢?

这里直接公布答案—–是Label。

可以用下面的代码进行尝试。

 
设定好翻译之后

List<Schema.PicklistEntry> picklistValues = SomeSobject__c.SomePicklistField__c.getDescribe().getPicklistValues();
for (Schema.PicklistEntry pe: picklistValues) {
    system.debug('PicklistEntry Value:' + pe.getValue() + ' Translated Label:' + pe.getLabel());
}

value既Api Name,Label既Label

这样,我们是可以做到在Apex中获取Picklist选项的Label,而且获得的还是翻译后的Label。
可曾记起我们还有Formula呢,isPickVal函数判断的又是谁呢—————没错,还是老朋友,Api Name。
目前我还没有找到如何在formula里获取Label名的方法。