Using Opportunity Selector to retrieve OpportunityContactRole - salesforce

I am very new to apex, I am trying to understand apex code written by someone else. Its retrieving opportunity data. It gets all the fields except opportunity contact role. Its always empty, even though there is data in it.
It calls the opportunity selector from the service class
Opportunity opp = cq.Opportunities.GetByIdWithOCR(new Set{opportunityId})[0];
in the opportunitySelector.apxc
public List<Opportunity> GetByIdWithOCR(Set<Id> idSet) {
return (List<Opportunity>) GetQueryFactory()
.WithCriteria(cq_Criteria.ValueIn(Opportunity.Id, idSet))
.WithRelatedField(Opportunity.AccountId, Account.Name)
.WithRelatedField(Opportunity.AccountId, Account.BillingStreet)
.WithRelatedField(Opportunity.AccountId, Account.BillingCity)
.WithRelatedField(Opportunity.AccountId, Account.BillingState)
.WithRelatedField(Opportunity.AccountId, Account.BillingPostalCode)
.WithRelatedField(Opportunity.AccountId, Account.BillingCountry)
.WithChildQuery(
cq.OpportunityContactRoles.GetQueryFactory()
.WithCriteria(cq_Criteria.Equals(OpportunityContactRole.Role, 'Decision Maker'))
.WithRelatedField(OpportunityContactRole.ContactId, Contact.Name)
.WithRelatedField(OpportunityContactRole.ContactId, Contact.Phone)
.WithRelatedField(OpportunityContactRole.ContactId, Contact.Email))
.Execute();
}
all the values are there but contact role is empty. Please advice

This is all custom written by your colleague, some attempt at ORM layer. "Normal" apex would be to write the query directly, better performance, some compile-time checks... Hard to say what lurks in that code.
Which community it is? Customer or Partner? Last I checked customer community has no access to Opportunity. Assuming it's Partner - do you know how to check the Profile of the community user and access to whole OpportunityContactRole table and all fields in it?
There are some hacks to let code jump out of the Salesforce security model (without sharing etc) but I wouldn't recommend unless you really know what you're doing and normal ways of controlling data visibility (profiles/permission sets, sharing rules, like sharing sets) failed you.
Try to capture debug logs. Open developer console (if possible, you tagged this as communities) and run your page or experiment with Setup -> Debug Logs. You should be able to find the query and try to execute it manually.
If you want to rule out issues with this query factory try to swap the code with this and see if it's any better:
return [SELECT Account.Name, Account.BillingStreet, Account.BillingCity,
Account.BillingState, Account.BillingPostalCode, Account.BillingCountry,
(SELECT Contact.Name, Contact.Phone, Contact.Email
FROM OpportunityContactRoles
WHERE Role = 'Decision Maker')
FROM Opportunity
WHERE Id IN :idSet];

Related

Exception in Site.createExternalUser in Apex RESTclass: Site.ExternalUserCreateException: [That operation is only allowed from within an active site.]

I have a Non-Salesforce Auth System which holds usernames and passwords for a few thousand users. I am willing to migrate these users to Salesforce and give access to these users to my Experience Cloud site. I am developing an apex REST Resource which will take username and password as arguments and create a user with that username and password with a community profile. I am planning to call this API from my Non-Salesforce system and migrate all these users. I am using Site.createExternalUser method in this API. I am getting the exception
Site.ExternalUserCreateException: [That operation is only allowed from within an active site.]
The reason I am using Site.createExternalUser is because I don't want to send the welcome email/reset password email to my users since they already have signed up successfully long ago.
I am open to any alternatives for achiving this.
Below is my code:
#RestResource(urlMapping='/createUser/*')
global with sharing class createUserRestResource {
#HttpPost
global static String doPost(){
Contact con=new Contact();
con.Firstname="First";
con.LastName= "Last";
con.Email="first.last#example.com";
con.AccountId='/Add an account Id here./';
insert con;
usr.Username= "usernameFromRequest#example.com";
usr.Alias= "alias123";
usr.Email= "first.last#example.com";
usr.FirstName= "First";
usr.IsActive= true;
usr.LastName= "Last";
usr.ProfileId='/Community User Profile Id/';
usr.EmailEncodingKey= 'ISO-8859-1';
usr.TimeZoneSidKey= 'America/Los_Angeles';
usr.LocaleSidKey= 'en_US';
usr.LanguageLocaleKey= 'en_US';
usr.ContactId = con.Id;
String userId = Site.createExternalUser(usr, con.AccountId, 'Password#1234', false);
return userId;
}
}
You can suppress sending emails out in whole org (Setup -> Deliverability) or in the Community config there will be way to not send welcome emails (your community -> Workspaces -> Administration -> Emails).
Without running on actual Site I don't think you can pull it off in one go. In theory it's simple, insert contact, then insert user. In practice depends which fields you set on the user. If it's Partner community you might be setting UserRoleId too and that's forbidden. See MIXED DML error. In Customer community you might be safe... until you decide to assign them some permission sets too.
You might need 2 separate endpoints, 1 to create contact, 1 to make user out of it. Or save the contact and then offload user creation to #future/Queueable/something else like that.

Updating user info in liferay database

I need to update info of an existing user in my database programmaticaly
I need to update user name birth date values in user_ table in Liferay database
basically I need to run an update query.
It is not recommended to update the liferay database directly, you should use Liferay API instead to do these things. As per this liferay forum post:
The Liferay database is not published for a reason. The reason is the API does significantly more stuff than just simple SQL insert statements. There are internally managed foreign keys, there are things which are updated not just in the database but also in the indices, in jackrabbit, etc.
Since all of this is managed by the code and not by the database, any updates to the code will change how and when the database is updated. Even if it did work for you in a 6.1 GA1 version, GA2 is coming out in a couple of weeks and the database/code may change again.
Sticking with the API is the only way to insure the changes are done correctly.
Ok enough preaching and back to your problem, here are some ways you can do these:
you can either build a custom portlet and use liferay's services and update the username, birthdate etc using UserLocalServiceUtil.updateUser() method.
Or you can build a web-service client based on SOAP or JSON to update the details which would call the same method
Or you can use Liferay's Beanshell tool to do this from the control panel, following is some code to update the user (created just for you ASAP):
import com.liferay.portal.model.Company;
import com.liferay.portal.model.Contact;
import com.liferay.portal.model.ContactConstants;
import com.liferay.portal.model.User;
import com.liferay.portal.service.CompanyLocalServiceUtil;
import com.liferay.portal.service.ContactLocalServiceUtil;
import com.liferay.portal.service.UserLocalServiceUtil;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
long companyId = 10135; // this would be different for you
User user = UserLocalServiceUtil.getUserByEmailAddress(companyId, "test#liferay.com");
// Updating User's details
user.setEmailAddress("myTest#liferay.com");
user.setFirstName("First Test");
user.setLastName("Last Test");
user.setScreenName("myTestScreenName");
UserLocalServiceUtil.updateUser(user, false);
// Updating User's Birthday
// December 12, 1912
int birthdayMonth = 11;
int birthdayDay = 12;
int birthdayYear = 1912;
Calendar cal = new GregorianCalendar();
cal.set(birthdayYear, birthdayMonth, birthdayDay, 0, 0, 0);
cal.set(Calendar.MILLISECOND, 0);
Date birthday = cal.getTime();
System.out.println("Updated User: " + user + "\nBirthdate to be updated: " + birthday);
long contactId = user.getContactId();
Contact contact = ContactLocalServiceUtil.getContact(contactId);
if(contact == null) {
contact = ContactLocalServiceUtil.createContact(contactId);
Company company = CompanyLocalServiceUtil.getCompany(user.getCompanyId());
contact.setCompanyId(user.getCompanyId());
contact.setUserName(StringPool.BLANK);
contact.setCreateDate(new Date());
contact.setAccountId(company.getAccountId());
contact.setParentContactId(ContactConstants.DEFAULT_PARENT_CONTACT_ID);
}
contact.setModifiedDate(new Date());
contact.setBirthday(birthday);
ContactLocalServiceUtil.updateContact(contact, false);
System.out.println("Users birthdate updated successfully");
The contact code is built with the help of Liferay's source code for UserLocalServiceImpl#updateUser method
In case you are wondering what is bean-shell and where to put this code, here is where you can find it in Liferay Control Panel Control Panel --> Server --> Server Administration --> Script
It depends on whether you have to do this in a portlet code or by sending a direct query to db.
Liferay basically caches everything, so if you update a record in the Liferay database while the portal is running, most likely that record is already in cache, and so the new column values won't be read at all. You will have to clear the database cache by going to Control Panel -> Server Administration.
On the contrary, if you have to do such a thing in a portlet code, you should call one of the methods of the Liferay services. You're trying to update a User, so you should call the method UserLocalServiceUtil.updateUser (or UserServiceUtil.updateUser if you also want to check permissions).
You can see there are some different updateUser methods, one of them has a lot of parameters and another has only the bean as a parameter. While the first one contains all the business logic (validation, reindexing, update of related entities, etc.), the second one was just autogenerated and should not be used (except when you absolutely know what you're doing). So, use the method with a lot of parameters, simply passing user.getCOLUMN() (eg. user.getFacebookId()) if you don't want to change the value of that column.
Hope it helps, and sorry for my bad English...
update user_ set firstName="New First Name", lastName="New Last Name" where emailAddress="test#test.com";
update contact_ set birthday="date string" where contactId in(select contactId from user_ where emailAddress="test#test.com");
By first update query you can change firstName, lastName of user and by second query you can change birthdate of user.
Hope its clear!
Try this code..
Here i am updating only user First name(rest you can do by your own way)
userId = you can get this using theme display
User user = UserLocalServiceUtil.getUser(userId);
user.setFirstName("new name");
UserLocalServiceUtil.updateUser(user);
Hope this will help you !!!

way to script an export of all AD users vcards

i'm looking for an easy way to export all active directory users info into unique vcards for each. there is some info i'd like to leave out of the vcard like home phone, and emergency contact. i've looked around the web and have little luck finding anything. any help would be appreciated.
I doubt there will be a very easy way. Ultimately, you need to
enumerate all your users (or a subset therefore)
iterate over the resulting list of users
export each user's data to a VCard
For the searching & iterating part, you can use a PrincipalSearcher to do your searching:
// create your domain context
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
{
// define a "query-by-example" principal - here, we search for a UserPrincipal
// this "QBE" user would give you the ability to further limit what you get back
// as results from the searcher
UserPrincipal qbeUser = new UserPrincipal(ctx);
// create your principal searcher passing in the QBE principal
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
// find all matches
foreach(var found in srch.FindAll())
{
UserPrincipal foundUser = found as UserPrincipal;
if(foundUser != null)
{
ExportToVCard(foundUser);
}
}
}
And now all that's left to do is create the ExportToVCard function :-) See e.g. this blog post with code samples and further links for help.
If you haven't already - absolutely read the MSDN article Managing Directory Security Principals in the .NET Framework 3.5 which shows nicely how to make the best use of the new features in System.DirectoryServices.AccountManagement. Or see the MSDN documentation on the System.DirectoryServices.AccountManagement namespace.
If you just want the data itself, I would take a look at Softerra's free LDAP Browser, found here.
Setup a profile for your directory server - once it's connected in the browser, you'll see the default schema for the BaseDN you've provided during the initial setup. On the server icon, right click, and hit "Export Data".
The export wizard will walk you through most of the process, but the important part is Step 3. If you want to find all users, just set your search filter to (objectClass=user), make sure your search scope is SubTree, and then then edit what attributes you want to return.
You'll have to process the results into VCards, but this is the easiest\fastest way of getting all the users and attributes that you want.

Salesforce- Creating contact and user by importing CSV file & send notification email to User

I want to create the contact & user related to account by importing csv file.After creating user, generated username & password should be send to the respective user's email address.
I am planning to use Vf page for accepting the csv file & the Contact & User API for adding contacts & Users. But I am not sure regarding the email notification to user through API.
So can anyone please provide me the best solution for this?
You'll need to use Database.insert() flavor instead of straightforward insert users;
Check out the DML options help topic, especially the "emailHeader" property
Something like this should do the trick:
List<User> users = new List<User>(); // fill in with data from your CSV
Database.DMLOptions dlo = new Database.DMLOptions();
dlo.EmailHeader.triggerUserEmail = true;
database.insert(users, dlo);
Be aware though that you can't insert an inactive user. You can deactivate them straight after the insert but then they can't login so you'll probably want to suppress the sending of email, not enforce it. Make sure you have sufficient amount of licenses before you start!
Last but not least - you can cheat by setting the same password for them with System.setPassword('user id', 'new password') :) It's not recommended to expose this functionality to your end users though, can process only 1 user at a time and wastes 1 DML statement...

How do I detect the environment in Salesforce?

I am integrating our back end systems with Salesforce using the web services. I have production and stage environments running on different URLs. I need to be able to have the endpoint of the web service call be different depending on whether the code is running in the production or sandbox Salesforce instance.
How do I detect the environment.
Currently I am considering looking up a user to see if there user name ends in 'devsandbox' as I have been unable to identify a system object that I can query to get the environment.
Further clarification:
The location I need to determine this is within the Apex code that is invoked when I select a button in Salesforce. My custom controller needs to know if it running in the production or sandbox Salesforce environment.
For y'all finding this via search results, there is an important update. As Daniel Hoechst pointed out in another post, SF now directly provides sandbox vs. production information:
In Summer '14, (version 31.0), there is a new field available on the
Organization object.
select Id, IsSandbox from Organization limit 1
From the release notes under New and Change Objects:
The Organization object has the following new read-only fields.
InstanceName
IsSandbox
Based on the responses it appears that Salesforce does not have a system object that can tell me if my Apex code is running in production or a sandbox environment.
I am going to proceed based on the following assumptions:
I can read the organisation id of the current environment
The organisation id of my production system will always remain constant.
The organisation id of a sandbox will always be different to production (as they are unique)
The current organization ID can be found with System.getOrganizationId()
My solution is to have my code compare the current org id to the constant value representing production.
I'm performing necromancy here and the answer is already accepted, but maybe somebody will benefit from it...
Use one of these merge fields on your Visualforce page / S-Control:
{!$Api.Enterprise_Server_URL_180}, {!$Api.Partner_Server_URL_180}, {!$Api.Session_ID}
You can easily parse out organization ID out of them.
In Apex code: UserInfo.getOrganisationId()
I know this is an old post, but just for the sake of people looking for an updated answer as of Spring '11 release there is a new method System.URL.getSalesforceBaseUrl().toExternalForm() that returns the current url.
You can work from there to get all the info you need.
Here's the link to docs: http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_methods_system_url.htm
The Login API call returns a sandbox element in the returned LoginResult structure that indicates if its a sandbox environment or not, from the WSDL.
<complexType name="LoginResult">
<sequence>
<element name="metadataServerUrl" type="xsd:string" nillable="true"/>
<element name="passwordExpired" type="xsd:boolean" />
<element name="sandbox" type="xsd:boolean"/>
<element name="serverUrl" type="xsd:string" nillable="true"/>
<element name="sessionId" type="xsd:string" nillable="true"/>
<element name="userId" type="tns:ID" nillable="true"/>
<element name="userInfo" type="tns:GetUserInfoResult" minOccurs="0"/>
</sequence>
</complexType>
Sandboxes may have a personalized url (e.g. acme.cs1.my.salesforce.com), or might be hosting a visualforce page (cs2.visual.force.com) or both (acme.cs2.visual.force.com) so I use this method:
public static Boolean isRunningInSandbox() {
String s = System.URL.getSalesforceBaseUrl().getHost();
return (Pattern.matches('(.*\\.)?cs[0-9]*(-api)?\\..*force.com',s));
}
I think the easiest way to do this would be to create a custom object in Salesforce, and then store a value indicating sandbox or production there. Your Apex code can then query that object. One suggestion would be to use Apex static constructors to load this information and cache it for the request.
Another thought I had (but hate to suggest) is to use an external service to determine where your Apex code is executing. This would probably be difficult to pull off, as every time the SalesForce server farm changes there is a change your code would break, but I just thought I'd throw this out there.
HttpRequest req = new HttpRequest();
req.setEndpoint('http://www.whatismyip.com/automation/n09230945.asp');
req.setMethod('GET');
Http http = new Http();
HTTPResponse res = http.send(req);
System.debug(res.getBody());
You have to add "http://www.whatismyip.com" to the Remote Site settings to get this to work (Setup > Administration Setup > Security Controls > Remote Site Settings). This code should run in the debug window when you click "System Log".
In your apex code you can use the following to get the instance of SF that you are in.
Keeping it dynamic will make sure you don't have to update your code when your org is migrated to a different instance.
String s = System.URL.getSalesforceBaseUrl().getHost();
//this will return "na1.salesforce.com" or "na1-api.salesforce.com",
// where na1 is your instance of SF, and -api may be there depending on where you run this
s = s.substring(0,s.indexOf('.'));
if(s.contains('-'))
{
s = s.substring(0,s.indexOf('-'));
}
system.debug(s);
There is a similar question on the Salesforce StackExchange for detecting if you are in a Sandbox or not - Can we determine if the Salesforce instance is production org or a Sandbox org?
In the solutions in search of a problem category, you could use the pod identifier from the OrgId to determine if you are dealing with a sandbox.
string podId = UserInfo.getOrganizationId().substring(3,4);
boolean isSandbox = 'TSRQPONMLKJZVWcefg'.contains(podId);
System.debug('IsSandbox: ' + isSandbox);
Caveat Confector: The big weakness here is that you will need to update the list of know sandbox pods as and when Salesforce brings new ones online (so it might be safer sticking with the other solutions).
You can use the following code block from Michael Farrington an authority on Salesforce.
Original blog post here: Michael Farrington: Where Am I Method
This method will return true if you are in a test or sandbox environment and false otherwise.
public Static Boolean isSandbox(){
String host = URL.getSalesforceBaseUrl().getHost();
String server = host.substring(0,host.indexOf('.'));
// It's easiest to check for 'my domain' sandboxes first
// even though that will be rare
if(server.contains('--'))
return true;
// tapp0 is a unique "non-cs" server so we check it now
if(server == 'tapp0')
return true;
// If server is 'cs' followed by a number it's a sandbox
if(server.length()>2){
if(server.substring(0,2)=='cs'){
try{
Integer.valueOf(server.substring(2,server.length()));
}
catch(exception e){
//started with cs, but not followed by a number
return false;
}
//cs followed by a number, that's a hit
return true;
}
}
// If we made it here it's a production box
return false;
}

Resources