Saturday, October 14, 2017

Google Analytic Overview

I spent some time to understand Google Analytic(GA) as I have some project that might require it.

What is Google Analytic?
Google Analytics is a freemium web analyticsservice offered by Google that tracks and reports website traffic. Google launched the service in November 2005 after acquiring Urchin. Google Analytics is now the most widely used web analyticsservice on the Internet.Read more...
Simply say, it use to track our website such page view etc. which enable us to see the performace of our website.

There some pro and cons of using GA that has been mentioned in several sites.I just listed down what I want to know but I provide resource below so you can grab it for more info.

Pro
  1. It's free so everyone can use it.We can setup up to 100 analytic account  .
  2. It's almost complete feature even for free version .It has has almost every metric we may want to analyze such bounce rate, average time spent on page, etc.
  3. It can be used in different enviroment such as website or mobile.
  4. The information resources are  available widely.Google setup Google Analytic Academy . Wow, an academy just for this, surely there a lot to learn =)
Cons
  1. For free version, Google may have some advantage of retailer data.
  2. It might no so user friendly for none technical background user.Honestly, I feel a little bit difficult to start such as add new account and understand the term.But it's possible to learn and also I believe Google will improvise user interface.
  3. If you have high traffic website, you might want to consider Google Analytic 360 which is expensive ~$150,000 per year.

Some screenshot on creating analytic account.

The Create new account is nowhere to be found.You have to click on Admin then select on dropbox.

Some share setting that you might want to consider especially if your site is sensitive.

Sample of javascript that you might need to paste on your site header.


Analytic Account structure in Google Analytic
source:Account Structure
An Analytics account is a way to name and organize how you track one or more properties (e.g. websites, mobile apps, point-of-sale devices) using Analytics. Each Analytics user has access to at least one account, either one they created themselves, or one that they were given access to by someone else. In each Analytics account, at least one property (such as a website) is being tracked. As shown above, an Analytics account can be used to track a single property, or it can track many distinct ones, depending upon the requirements of its use.Read more...

Resources that I found useful :



Monday, October 02, 2017

Label on InputField and OutputField is not working

Actually I faced this issue quite few times and  because I didn't take note on it  I keep forgetting and asking the same question again.

Today, I got chance to do some testing to understand the behavior of apex:Inputfield and apex:outputField tag.

Note that apex:inputField / apex:outputField is tied to the field's object which mean we cannot use it without sObject.I like it's Calendar widget for date field but because it does not have capability to support class property, I need to tie it with Standard/Custom object's date field.

Here the example of usage

public class TestClass {

Date myDate {get;set;} //this will not work with  <apex:inputfield value="{!myDate}"  ...
Opportunity opp {get;set;} //this will work on <apex:inputfield value="{!opp.CloseDate}" ...

}

Other thing that we need to take note is the label will work in apex:pageBlock and only if the apex:pageBlock in the proper structure.

Checkout this code.


<apex:page standardController="Opportunity" showHeader="false" sidebar="false" >
    <apex:form>
        <apex:pageBlock title="Proper Page Block" mode="edit">
            <apex:pageBlockButtons>
                <apex:commandButton action="{!save}" value="Save"/>
            </apex:pageBlockButtons>
            <apex:pageBlockSection title="My Content Section" columns="2">
                <apex:inputField value="{!opportunity.name}"/>
                 <apex:inputField value="{!opportunity.type}" label="Custom Type"/>
                   <apex:outputField value="{!opportunity.accountId}" />
                 </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form>
    <apex:form>
        <apex:pageBlock title="Not Proper Page Block" mode="edit">
                 <apex:inputField value="{!opportunity.name}" label="Custom Name"/>
                 <apex:inputField value="{!opportunity.type}"/>
                   <apex:outputField value="{!opportunity.accountId}" label="Account"/>
                </apex:pageBlock>
        Experiment outside page block : <apex:inputField value="{!opportunity.name}" label="Custom Name 1"/>
          <apex:outputField value="{!opportunity.accountId}" label="Account"/>
    </apex:form>
</apex:page>

This is the output.Notice that apex:inputField and apex:outputField will not work without apex:pageBlock or improper structure apex:pageBlock.


Conclusion, in order to utilize apex:inputField and apex:outputField we need to use it with sObject and use it apex:pageBlock.

More info :

Wednesday, September 13, 2017

My journey on converting Attachment to Files in Salesforce

Everyone know that soon Salesforce will remove related list of Attachment & Note in Page Layout(implementation expected in Winter'18). This is indicate that we should start using Salesforce Files that have better features especially for sharing.

I started researching on this exercise somewhere in 2016 and I feel very grateful that I found a very good tool developed by Douglas C. Ayers.I have been testing the tool  in my organization and thanks to him because this app helps a lot of organization šŸ˜ƒ.

I am writing here to share experience. I am happy to inform that we completed our conversion successfully.These are few steps that perhaps you find it useful.Bear in mind that this approach may not suitable for you.Please do some research before implementing it.
  1. Refresh UAT with the latest Production data , install the app and run a lot of testing till you gain confidence.We hit the Content Publication Limits Exceeded which is annoying because we know our total Attachment is not even reach 200000.So we called Salesforce and it turned up they set it to 25 000 as default.So it's good to give them a call if you know that you should not hit the limit.
  2. Install to Production and make sure your setting it based on your need.
  3. Before we run , we need to plan conversion properly and to do so we need a report.Data loader can be used to retrieve it.I found that converting to Excel is very useful.Basically you may want to know how many object that have Attachment ,how many Attachment that we need to convert,ParentId etc. The simple query like below might be helpful
  4. Select Id,ParentId,Parent.Type,OwnerId ,Owner.IsActive from Attachment
  5. I just get complicated by creating visual force which is something like below.I labeled ParentType as 'Unknown' if the field value is null. 
  6. Decide which object that we want to convert, we may want to skip Unknown or 00X.Also we might want to put the sequence on which object that we should convert first.I prefer this approach because we can easily reduce the risk if something went wrong.We might want to start converting the object that has less Attachment first.
  7. ObjectTotal AttachmentNeed to convertSequenceConvertedRemark
    Opportunity7366Y5
    Unknown3121NNA
    Strategy13693Y5
    EmailMessage386NNA
    Marketing Support524Y2
    Account1055Y3
    Case95Y2
    00X49NNA
    Operation1Y1
    Solution136Y2
    Contact91Y1
    Project52Y1
    Contract16Y1
    Lead95Y2
    Conga35NNA
    Account Plan4Y1
    Campaign7Y1

  8. Before start converting in Production, communicate with user.Ensure that they are aware on what happen to their Attachment and educate them about Files related list that they are going to see  in Page Layout once the conversion is completed.
  9. I copy ParentId  from the  query in previous step and use Excel to remove duplicate.For example, I copy ParentId of Opportunity and paste it into Excel and remove duplicate.We want to have unique parentId. As you can see, instead of running automate conversion for all, I manually paste the ParentId per object.
  10. Once the job is completed, I manually remove Attachment related list from Page Layout and replace it with Files related list.This will avoid user to keep uploading old Attachment in future.
  11. The job is faster that I expected. Once it completed, I checked Convert Attachment to Files log just in case there an error.Luckily we faced no error.
  12. Once I finished converted all of Attachment for object that we planned, I go to option Automate Conversion and run the schedule job in case there  old Attachment left over. We decided to run the schedule until end of this year.

  13. That's all. I hope you find this tips helpful.

Tuesday, August 29, 2017

Set Proxy for Force.com Migration Tool

When you  setup ANT and you believe it being setup correctly but still it still fail when you perform task, there are possibilities that your organization is using proxy.Example error that you receive when you run ant task.

Caused by: com.sforce.ws.ConnectionException: Failed to send request to https://
test.salesforce.com/services/Soap/u/39.0
 Here I list down of steps that I did to overcome this:

  1. Ask Network team what is the proxy host and port in your organization.Is username and password is required?
  2. Define proxy in target.In build.xml , create target name like below.
  3. <target name="proxy">
            <property name="proxy.host" value="xx.xx.xx.xx" />
            <property name="proxy.port" value="xxxx" />
            <!--If there username and password required
      <property name="proxy.user" value="UserName" />
            <property name="proxy.pwd" value="Password" />-->
            <setproxy proxyhost="${proxy.host}" proxyport="${proxy.port}"  />
        </target>
    

  4. Set depends to true for task that you going to perform.For example in given build.xml , I set depends for test and describeMetadata
  5.  <target name="test" depends="proxy"><!--set depends here-->
          <!-- Upload the contents of the "mypkg" package -->
          <sf:deploy username="${sf.username}" password="${sf.password}" sessionId="${sf.sessionId}" serverurl="${sf.serverurl}" maxPoll="${sf.maxPoll}" deployRoot="mypkg" rollbackOnError="true"/>
          <mkdir dir="retrieveOutput"/>
          <!-- Retrieve the contents into another directory -->
          <sf:retrieve username="${sf.username}" password="${sf.password}" sessionId="${sf.sessionId}" serverurl="${sf.serverurl}" maxPoll="${sf.maxPoll}" retrieveTarget="retrieveOutput" packageNames="MyPkg"/>
        </target>
    
    Another example, I set at describeMetadata task.
    <!-- Retrieve the information on all supported metadata type -->
        <target name="describeMetadata" depends="proxy">
          <sf:describeMetadata username="${sf.username}" password="${sf.password}" sessionId="${sf.sessionId}" serverurl="${sf.serverurl}"/>
        </target>
    
Now when I run ant test , the result will be like this :

C:\salesforce_ant_39.0\sample>ant test
Buildfile: C:\salesforce_ant_39.0\sample\build.xml
proxy:
test:
[sf:deploy] Using proxy: xx.xx.xx.xx:xxxx
[sf:deploy] Using proxy: xx.xx.xx.xx:xxxx
[sf:deploy] Request for a deploy submitted successfully.
[sf:deploy] Request ID for the current deploy task: 0Af4E00000QdHqLSAV
[sf:deploy] Waiting for server to finish processing the request...
[sf:deploy] Request Status: Pending
[sf:deploy] Request Status: Pending
[sf:deploy] Request Status: Pending
[sf:deploy] Request Status: Succeeded
[sf:deploy] *********** DEPLOYMENT SUCCEEDED ***********
[sf:deploy] Finished request 0Af4E00000QdHqLSAV successfully.
    [mkdir] Created dir: C:\salesforce_ant_39.0\sample\retrieveOutput
[sf:retrieve] Using proxy: xx.xx.xx.xx:xxxx
[sf:retrieve] Using proxy: xx.xx.xx.xx:xxxx
[sf:retrieve] Request for a retrieve submitted successfully.
[sf:retrieve] Request ID for the current retrieve task: 09S4E0000019PkVUAU
[sf:retrieve] Waiting for server to finish processing the request...
[sf:retrieve] Request Status: Pending
[sf:retrieve] Request Status: Succeeded
[sf:retrieve] Finished request 09S4E0000019PkVUAU successfully.
BUILD SUCCESSFUL
Total time: 1 minute 35 seconds
More details regarding Force.com Migration Tool can be found here.

Wednesday, August 16, 2017

Winmerge give me misguide message.

I am using Winmerge to compare code. For some reason, it give keep saying that the files are different while I really confident that I haven't touch the file.When I open the file , there no difference visibly shown which give me a little headache.


Turn out that I need to modify setting in Winmerge.Go to Edit, click on Options.


Now the view is better.




Saturday, July 01, 2017

Let it be...


Definition :

The let statement declares a block scope local variable, optionally initializing it to a value.

What different between var and let ?

It is mathematical statement.Remember in Math class, our teacher always mention let , for example:
If we let A be the statement "You are rich" and B be the statement "You are happy", then the negation of "A or B" becomes "Not A and Not B."
The question already answered in : Why was the name 'let' chosen for block-scoped variable declarations in JavaScript?

The different , notice the code below is using var, once you define same variable in local scope in second time , it will overwrite  the variable in global scope,


function varTest() {
  var x = 1;
  if (true) {
    var x = 2;  // same variable!
    console.log(x);  // 2
  }
  console.log(x);  // 2
}

Using let, help to identify which belong to which.
function letTest() {
  let x = 1;
  if (true) {
    let x = 2;  // different variable
    console.log(x);  // 2
  }
  console.log(x);  // 1
}
For details please refer to : let

Friday, May 26, 2017

Identify Tools to Build Your App



What is true about the Apex programming language?

  • It’s designed to work with custom objects you create declaratively
  • It’s the easiest way to build and customize your Salesforce application
  • Anyone can use it in GE, PE, PXE, and UE orgs
  • It’s just a replacement for Java or C#


What is a use case for the SOAP API?

  • Keeping your data integration squeaky clean
  • Embed a page from an external system into the UI
  • Connecting to an external database using OData
  • Moving data between Salesforce and an external app


How does Chatter foster collaboration?

  • By social interaction through groups, feeds, and record following
  • By enabling your customers to message you from their org
  • By creating a group video-chat environment for real-time collaboration
  • Through public chat forums only


You’re building a supply requisition ISVforce app for companies that want to manage complex projects. For each vendor being considered for a given component, a Lead record is created. Once the supplier submits a bid, the Lead is moved to an Opportunity. After talking to your customers, you know they will want the vendors to see the status of their own bids. What type of Community should be created for vendors and for what reason?

  • Customer Community, because it restricts the data that vendors can access
  • Partner Community, because vendors must view Opportunity objects
  • Customer Community, because approval processes must be able to specify vendors
  • Partner Community, because vendors must use Chatter


What is the fastest way to provide your Salesforce customers a mobile-ready version of the app you’ve built for Sales Cloud?

  • Build a mobile app on the Heroku platform using Heroku Connect
  • Optimize your pages for mobile and use the Salesforce1 mobile app
  • Create custom objects for Leads and Opportunities, then use custom code to develop the app
  • Create native device apps using the Salesforce Mobile SDK


Thursday, May 18, 2017

Refresh : If break in inner loop , will it break entire loop?

Sometimes it good to have refreshment.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
for(Integer i=0; i<20 ;i++){

    for(Integer j = 0;j<10;j++){
           system.debug('@@@ j ='+j + 'i ='+i);
        if(j==5){
            system.debug('@@@ FIVE');
            break;
            
        }
        
    }
  
}

Will it break entire loop if inner loop contains break?

As we can see, inner loop will have break at five, but outer loop still continue

Sunday, May 14, 2017

Spring ‘17 Platform Developer I Release Exam

I just passed my first Maintenance Exam.Actually, I doubt to take it today but as I went through the Release Note, I just asking myself... why not just take it now.I could not hold the stress anymore, it should been thrown somewhere.Luckily it was not hard as I thought.As long you reviewing the release note, you should be okay.=)

Here some questions that I remember to capture.I have the answer but I won't display here =).You can try to answer it in Comment below.

Question 1 : Which three types of content can shortcut be created for using Favorites.Choose 3 answers.
A.Dashboards
B.Chatter Groups
C.Global Actions
D.Record  home pages
E.Custom tabs

Question 2:Which two options are available to display detailed information about the status of an Apex Job?Choose two answers.
A.Submitted Date
B.Heap Size
C.Submitted by user
D.Elapsed time

Question 3:Which feature enables creating,updating and deleting records in other Salesforce org?
A.Database.WriteableObject interface
B.Apex Trigger for Writeable External Objects
C.Lightning to Lightning Connector
D.Salesforce Connect Cross-Org Adapter

Question 4:Which three actions can be taken from the Global Actions Menu?Choose 3 answers.
A.Launch a custom Visual force page
B.Launch a custom Lightning Component
C.Post to a Chatter Feed
D.Execute Anonymous blocks of Apex code
E.Launch a custom Canvas App

Question 5:Which component type will display the details of the parent Account from a custom Lightning record page for the Contact object?
A.Parent object component
B.Filter list component
C.Hierarchy component
D.Related Record component

Question 6:Custom Lightning Record Pages can be assigned to which three items?Choose 3 answers.
A.Public Group
B.Profiles
C.Apps
D.Record Types
E.Roles

Question 7:A Platform Developer wants to reference an image included in the Salesforce Lightning Design System from a Visualforce page.
Which two references should be included in the page markup?Choose 2 answers.
A.<force:slds>
B.$Asset
C.$SLDS
D.<apex:slds>

Friday, May 05, 2017

Javascript in Salesforce Formula Custom Field

Starting with the Spring ‘17 release*, we will begin a three-phase effort to eliminate the use of JavaScript in hyperlink formula fields.Read more...
With this implementation, we will no longer to use javascript in hyperlink custom field formula and will retrieve this error :
Error: Incorrect argument value for function operator '"xxxx"'. Javascript is not allowed.
But still for certain condition , we still need to implement javascript.In order to achieve this, we can call Visualforce page or html page(static resource) and execute javascript inside the page itself.

Example : I want a link that can open two window .Previously it can be achieve by using hyperlink below:

HYPERLINK("javascript:window.open('tel:+6xxxxxxxxx'); window.open('http://www.salesforce.com'); window.close();", IMAGE("/servlet/servlet.FileDownload?file=01526000000GsNh","Click To Call") , '_blank')

But now the above code no longer work.The workaround for this is to create a new Visualforce page that contains javascript like below :


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<apex:page standardController="Lead" showHeader="false" standardStylesheets="false" sidebar="false">
<script>
window.onload = function () {
 
    window.onload = function () {
    window.open("tel:+6xxxxxxxxx");
    window.open("http://www.salesforce.com");
    window.close()
}
</script>
</apex:page>

In custom field we just point to our Visualforce page.

HYPERLINK("/apex/my_vf?Id=" & Id, IMAGE("/servlet/servlet.FileDownload?file=01526000000GsNh", "Click To call"))

The concept also work by using html page that store in Static Resource, but I personally think Visualforce page might be useful especially we can use standard controller which enable us to use object fields.

Thursday, April 20, 2017

Save Visualforce Chart as Image in Attachment

The issue that brought me here is a user wants to render chart as PDF as we all know that Visualforce Chart is not renderable into PDF. Visualforce Chart is renderd by Javascript that is why it is not PDF renderable.

Please refer here for limitations:



In order to bypass those limitation, we need to save chart as image and after that use the image in PDF.Currently there a lot chart library that offers saving the chart to user machine.In my scenario, user want to be able to get chart along with Opportunity information in PDF.So saving chart into the local machine and upload it back as Attachment is not ideal solution.

In order to achieve this, we must include canvg library that you can get it here.This library will help to convert SVG to PNG[also please checkout other format].I get this information from here . Note that Visualforce Chart only render in browsers that support SVG, which mean this is a good sign.


1
2
3
<script type="text/javascript" src="http://canvg.github.io/canvg/rgbcolor.js"></script> 
<script type="text/javascript" src="http://canvg.github.io/canvg/StackBlur.js"></script>
<script type="text/javascript" src="http://canvg.github.io/canvg/canvg.js"></script> 

In order Visualforce page , add canvas element in page.In this example, I use demo from Visualforce Chart for pie chart.

1
2
3
4
5
6
7
<input type="button" id="save_img_att" value="Save as Attachment"/>
<div id="container" style="min-width: 400px; max-width: 600px; height: 400px; margin: 0 auto"></div>
    <apex:chart height="350" width="450" data="{!pieData}" id="pieChart" renderTo="container">
        <apex:pieSeries dataField="data" labelField="name" id="pieSeries"/>
        <apex:legend position="right"/>
    </apex:chart>
    <canvas id="canvas" style="display:none;"></canvas>

In Javascript,note 'container' is the div element where chart is render to.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$("#save_img_att").click(function(){
        var mainDiv=document.getElementById('container');
        var svg = mainDiv.children[0].children[0].innerHTML;
        console.log('@@svg .... ' + svg);
        canvg(document.getElementById('canvas'),svg);
        var img = canvas.toDataURL("image/png"); //img is data:image/png;base64
        img = img.replace('data:image/png;base64,', '');
        var data = "bin_data=" + img;
        console.log('@@oppId =' +     '{!Opportunity.Id}');
        Visualforce.remoting.Manager.invokeAction('{!$RemoteAction.VFChart_Server.saveChartAsAttachment}',
       '{!Opportunity.Id}',img,
          function(result,event){
             if(event.status){
             
               console.log('@@@ success');
               
             }
             else {
               console.log('@@@ fail');
             
             }
          
          
          } );
        
      
    });

We use var svg = mainDiv.children[0].children[0].innerHTML; in order to get the div that generated by Salesforce.You can right click at the chart and search for something like below.We don't want to hardcode so we need to find it from 'container' div.


Create @RemoteAction in controller class.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@RemoteAction
    global static void saveChartAsAttachment(String oppId,String attachmentBody){
       system.debug('@@@saveChartAsAttachment  oppId ='+ oppId);
       List<Attachment> listAttachment =[Select Name,Id,Body from Attachment Where ParentId=:oppId];
       Attachment att;
       if(listAttachment.size()>0){
          att =listAttachment[0];
          att.Body = EncodingUtil.base64Decode(attachmentBody);
          update att;
       
       }
       else{
       
           att = new Attachment();
           att.Name='spiderChart_VF';
           att.ParentId=oppId;
           att.ContentType='image/png';
           att.Body = EncodingUtil.base64Decode(attachmentBody);
           insert att;
    
       }
  
    }

I include this visualforce page in Opportunity page layout  like below.This is nothing to do with saving it as attachment, it just because I am lazy to create my own custom page.


Click on Save as Attachment. Refresh it, you should see the image is save in Attachment.Here the sample of image that generated from Visualforce Chart.


Once it save as Attachment, it easy for us to render as PDF which I will update in other entry.

For complete source code please check it out here.



Wednesday, April 19, 2017

Determine the Salesforce Edition for Your App



How does Enterprise Edition differ from Professional Edition?

  • Enterprise Edition has features that aren't available in Professional Edition
  • Professional Edition is only sold to Accountants, Dentists, and Lawyers
  • Enterprise Edition supports more users than Professional Edition
  • Professional Edition has higher limits than Enterprise Edition


Which list correctly orders editions from smallest to largest?

  • Enterprise Edition, Professional Edition, Group Edition, Performance Edition
  • Professional Edition, Group Edition, Enterprise Edition, Performance Edition
  • Group Edition, Professional Edition, Enterprise Edition, Performance Edition
  • Professional Edition, Group Edition, Performance Edition, Enterprise Edition



You’re building an ISVforce app that reuses Account, Contact, Lead, and Opportunity objects to vet vendors who resell products for your customers. You use Record Types to ensure that the fields you add don’t interfere with the customers existing use of those objects. What's the lowest edition that you can target?

  • Group Edition
  • Enterprise Edition
  • Performance Edition
  • Professional Edition



For an ISVforce app, what determines the features you should use in your solution?

  • The type of user licenses you're embedding in your application
  • The size of the customer you're targeting and the industry they are from
  • The complexity of your application and the amount of resources it uses
  • The customer’s edition, user licenses, and feature and permission set licenses
  • You want your customers to buy user licenses directly from Salesforce



When should you decide which editions your app supports?

  • After you develop your app, but before you sell it to customers
  • Before you design your app and before you build your app
  • After you’ve designed your app, but before you begin building it
  • Before you determine your target audience and before you design your app


Wednesday, April 12, 2017

To check if the parent have child using SOQL

Use Left Inner Join

SELECT Id FROM Opportunity WHERE Id IN (SELECT Opportunity__c FROM Opportunity_Child__c)

Reference
A Deeper look at SOQL and Relationship Queries on Force.com
SOQL to determine if parent has child records

Friday, April 07, 2017

Global Picklist fail to deploy : Duplicate label: xx xx xx

I am deploying Global Picklist and got Duplicate label error.I am really confident that there is no duplicate label except deactivated picklist. The issue is known as raised here : Deploying picklist values via change set causes "Duplicate Label" error.

It seem like not my case and it said that it's already fixed.I already deactivated the duplicate picklist.Now I suspect, the deactivated picklist causing the error.So I go to Change Set Detail, click at View Source on the picklist.Surely, it will deploy the deactivated picklist.So I delete all the deactivated picklist and redeploy again.It's really annoying because uploading Change Set is not so fast procedure.

The result is SUCCESS. Okay take note if you just deactivate the picklist similar label with the active picklist because it will make your deployment life miserable until you find this blog. =)

Have a nice day.Take care.

Chatter Basic : Getting Started with Chatter

Group types

  • Public groups: Public groups are visible and open to all employees. Anyone in the company can join a public group, post, comment, and add files.
  • Private groups: Private groups are open only to group members. People must request to join a private group. Only group members can post, comment, and add files. Nonmembers can see the group’s picture and description, but they can’t see the group feed or files.
  • Unlisted groups: Unlisted groups are invitation-only and don’t appear in list views and search results. An unlisted group is hidden from everyone except group members. Only the group’s owner or manager can invite people to join an unlisted group.
  • Groups with customers: Groups with customers are private or unlisted groups that allow external users to be members. The group owner or manager must invite external users to join. Need to Allow Customers option to private and unlisted groups.
  • Broadcast only: In broadcast only groups, only group owners and managers can create posts. Group members can comment on them. Apply the Broadcast Only option to private, unlisted, and public groups.