How do I detect the environment in Salesforce? - 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;
}

Related

Salesforce Tooling API - Deactivate Trigger

I am attempting to deactivate triggers using the tooling API. I have successfully in a developer ORG. But was unable to do this in a real developer org. Is this a Salesforce tooling api bug?
Here is the basis of the algorithm,
Create a MetadataContainer with a unique Name
save MetadataContainer
Create an ApexTriggerMember setting the Body, MetadataContainerId, ContentEntityId, and Metadata[apiVersion=33.0 packageVersions=[] status="Inactive" urls=nil>]
Modify Metadata["status"]="Inactive"
save ApexTriggerMember
Create/Save ContainerAsyncRequest
monitor container until completed.
display errors if appropriate
In the sandbox, I have confirmed after requerying the Apex enter code hereTriggerMember that the read-only field "Content" looks appropriate. I also confirmed that the MetadataContainerId now points to a ContainerAsyncRequest that has a State of "Completed"
Here are my results, it appears to be a success, but the ApexTrigger is never deactivated
ContentEntityId = 01q.............[The ApexTrigger I want deactivated]
Content="<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<ApexTrigger xmlns=\"urn:metadata.tooling.soap.sforce.com\">
<apiVersion>33.0</apiVersion>
<status>Inactive</status>
</ApexTrigger>"
Metadata={apiVersion=33.0 packageVersions=nil status="Inactive" urls=nil> attributes= {type="ApexTriggerMember"
url="/services/data/v33.0/tooling/sobjects/ ApexTriggerMember/401L0000000DCI8IAO"
}
}
I think you need to deploy the inactive Trigger from Sandbox to Production. You can't simply deactivate the Trigger in Production. This is true even in the UI.
There are other options, such as using a Custom Setting or Metadata Type to store a Run/Don't Run value. You would query that value in the Trigger to decide whether or not to run it.
https://developer.salesforce.com/forums/?id=906F0000000MJM9IAO

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.

ACAccount Facebook: An active access token must be used to query information about the current user

I am using iOS 6 Social framework for accessing user's Facebook data. I am trying to get likes of the current user within my app using ACAccount and SLRequest. I have a valid Facebook account reference of type ACAccount named facebook, and I'm trying to get user's likes this way:
SLRequest *req = [SLRequest requestForServiceType:SLServiceTypeFacebook requestMethod:SLRequestMethodGET URL:url parameters:nil];
req.account = facebook;
[req performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
//my handler code.
}
where url is #"https://graph.facebook.com/me/likes?fields=name"; In my handler, I'm getting this response:
{
error = {
code = 2500;
message = "An active access token must be used to query information about the current user.";
type = OAuthException;
};
}
Shouldn't access tokens be handled by the framework? I've found a similar post Querying Facebook user data through new iOS6 social framework but it doesn't make sense to hard-code an access token parameter into the URL, as logically the access token/login checking should be handled automatically by the framework. In all examples that I've seen around no one plays with an access token manually:
http://damir.me/posts/facebook-authentication-in-ios-6
iOS 6 Facebook posting procedure ends up with "remote_app_id does not match stored id" error
etc.
I am using the iOS6-only approach with the built in Social framework, and I'm not using the Facebook SDK. Am I missing something?
Thanks,
Can.
You need to keep a strong reference to the ACAccountStore that the account comes from. If the store gets deallocated, it looks like it causes this problem.
Try running on an actual device instead of a simulator. This worked for me.
Ensure that your bundle id is input into your Facebook app's configuration. You might have a different bundle id for your dev/debug build.

Why can't I update these custom fields in Salesforce?

Greetings,
Well I am bewildered. I have been tasked with updating a PHP script that uses the BulkAPI to upsert some data into the Opportunity entity.
This is all going well except that the Bulk API is returning this error for some clearly defined custom fields:
InvalidBatch : Field name not found : cv__Acknowledged__c
And similar.
I thought I finally found the problem when I discovered the WSDL version I was using was quite old (Partner WSDL). So I promptly regenerated the WSDL. Only problem? Enterprise, Partner, etc....all of them...do not include these fields. They're all coming from the Common Ground package and start with cv_
I even tried to find them in the object explorer in Workbench as well as the schema explorer in Force.com IDE.
So, please...lend me your experience. How can I update these values?
Thanks in advance!
Clif
Screenshots to prove I have the correct access:
EDIT -- Here is my code:
require_once 'soapclient/SforcePartnerClient.php';
require_once 'BulkApiClient.php';
$mySforceConnection = new SforcePartnerClient();
$mySoapClient = $mySforceConnection->createConnection(APP.'plugins'.DS.'salesforce_bulk_api_client'.DS.'vendors'.DS.'soapclient'.DS.'partner.wsdl.xml');
$mylogin = $mySforceConnection->login('redacted#redacted.com', 'redactedSessionredactedPassword');
$myBulkApiConnection = new BulkApiClient($mylogin->serverUrl, $mylogin->sessionId);
$job = new JobInfo();
$job->setObject('Opportunity');
$job->setOpertion('upsert');
$job->setContentType('CSV');
$job->setConcurrencyMode('Parallel');
$job->setExternalIdFieldName('Id');
$job = $myBulkApiConnection->createJob($job);
$batch = $myBulkApiConnection->createBatch($job, $insert);
$myBulkApiConnection->updateJobState($job->getId(), 'Closed');
$times = 1;
while($batch->getState() == 'Queued' || $batch->getState() == 'InProgress')
{
$batch = $myBulkApiConnection->getBatchInfo($job->getId(), $batch->getId());
sleep(pow(1.5, $times++));
}
$batchResults = $myBulkApiConnection->getBatchResults($job->getId(), $batch->getId());
echo "Number of records processed: " . $batch->getNumberRecordsProcessed() . "\n";
echo "Number of records failed: " . $batch->getNumberRecordsFailed() . "\n";
echo "stateMessage: " . $batch->getStateMessage() . "\n";
if($batch->getNumberRecordsFailed() > 0 || $batch->getNumberRecordsFailed() == $batch->getNumberRecordsProcessed())
{
echo "Failures detected. Batch results:\n".$batchResults."\nEnd batch.\n";
}
And lastly, an example of the CSV data being sent:
"Id","AccountId","Amount","CampaignId","CloseDate","Name","OwnerId","RecordTypeId","StageName","Type","cv__Acknowledged__c","cv__Payment_Type__c","ER_Acknowledgment_Type__c"
"#N/A","0018000000nH16fAAC","100.00","70180000000nktJ","2010-10-29","Gary Smith $100.00 Single Donation 10/29/2010","00580000001jWnq","01280000000F7c7AAC","Received","Individual Gift","Not Acknowledged","Credit Card","Email"
"#N/A","0018000000nH1JtAAK","30.00","70180000000nktJ","2010-12-20","Lisa Smith $30.00 Single Donation 12/20/2010","00580000001jWnq","01280000000F7c7AAC","Received","Individual Gift","Not Acknowledged","Credit Card","Email"
After 2 weeks, 4 cases, dozens of e-mails and phone calls, 3 bulletin board posts, and 1 Stackoverflow question, I finally got a solution.
The problem was quite simple in the end. (which makes all of that all the more frustrating)
As stated, the custom fields I was trying to update live in the Convio Common Ground package. Apparently our install has 2 licenses for this package. None of the licenses were assigned to my user account.
It isn't clear what is really gained/lost by not having the license other than API access. As the rest of this thread demonstrates, I was able to see and update the fields in every other way.
If you run into this, you can view the licenses on the Manage Packages page in Setup. Drill through to the package in question and it should list the users who are licensed to use it.
Thanks to SimonF's professional and timely assistance on the Developer Force bulletin boards:
http://boards.developerforce.com/t5/Perl-PHP-Python-Ruby-Development/Bulk-API-So-frustrated/m-p/232473/highlight/false#M4713
I really think this is a field level security issue. Is the field included in the opportunity layout for that user profile? Field level security picks the most restrictive option, so if you seem to have access from the setup screen but it's not included in the layout, I don't think the system will give you access.
If you're certain that your user's profile has FLS access to the fields and the assigned layouts include the fields, then I'd suggest looking into the definition of the package in question. I know the bulk API allows use of fields in managed packages normally (I've done this).
My best guess at this point is that your org has installed multiple versions of this package over time. Through component deprecation, it's possible the package author deprecated these custom fields. Take a look at two places once you've logged into salesforce:
1.) The package definition page. It should have details about what package version was used when the package was first installed and what package version you're at now.
2.) The page that has WSDL generation links. If you choose to generate the enterprise WSDL, you should be taken to a page that has dropdown elements that let you select which package version to use. Try fiddling with those to see if you can get the fields to show up.
These are just guesses. If you find more info, let me know, and I can try to provide additional guidance.

Resources