How to...
=========
Set up automatic periodic synchronization
-----------------------------------------
Here is one way to automatically trigger the sync via a cron job. The sync will
run in a separate process from your standard backend instances, so you must be
using ZEO or RelStorage to allow multiple database connections.
1. In the root of your buildout, create a file ``cronviews.py.in``::
from AccessControl.SecurityManagement import newSecurityManager
from AccessControl.SecurityManager import setSecurityPolicy
from Testing.makerequest import makerequest
from Products.CMFCore.tests.base.security import PermissiveSecurityPolicy, OmnipotentUser
from zope.app.publication.interfaces import BeforeTraverseEvent
from zope.event import notify
import transaction
# Get our variables from buildout in order.
site_path = '${site-path}'
view_names = """${views}"""
site_path = site_path.strip().strip('/')
views = [v.strip() for v in view_names.split() if v.strip()]
# Set up a generic manager user.
_policy=PermissiveSecurityPolicy()
_oldpolicy=setSecurityPolicy(_policy)
newSecurityManager(None, OmnipotentUser().__of__(app.acl_users))
# Set a request so that traversal succeeds.
app = makerequest(app)
# Fire a before traversal event so that browser layers get applied.
site = app.restrictedTraverse(site_path)
notify(BeforeTraverseEvent(site, site.REQUEST))
# Try to open the views.
for view in views:
try:
site.restrictedTraverse(view)()
transaction.commit()
except:
import traceback
traceback.print_exc()
2. Add sections to buildout.cfg to install this script and run it via cron
(be sure to replace ``path/to/site``)::
[buildout]
parts =
cronviews
sfsync
[cronviews]
recipe = collective.recipe.template
input = ${buildout:directory}/cronviews.py.in
output = ${buildout:directory}/etc/cronviews.py
site-path = path/to/site
views =
@@sf_sync
[sfsync]
recipe = z3c.recipe.usercrontab
times = 00 1 * * *
command = ${buildout:executable} ${buildout:bin-directory}/instance run ${buildout:directory}/etc/cronviews.py
3. Run the buildout to install the cron job.
Sync a single object on demand
------------------------------
Go to the Salesforce Content control panel in Site Setup.
In the `Salesforce object Id` box, enter the Id of the Salesforce object you
want to sync (you can obtain it from the URL when looking at the object in
Salesforce). You must also select the content type corresponding to the
Salesforce object type of the item.
Click Synchronize Now to sync the single item.
There is also an API to trigger this sync for a particular object from an
external system (such as a button on an object in Salesforce). To set it up,
go to the Configuration Registry and set a value for the
``collective.salesforce.content.sync_key`` key. Then you can run a sync via,
e.g., http://path/to/plone/sf_sync?token=TOKEN&types:list=OBJTYPE&sf_object_id=ID
where TOKEN is the sync key you configured, OBJTYPE is the Salesforce object type
of the object you want to sync, and ID is its Salesforce Id.
Obtain the Salesforce Id of a Plone item
----------------------------------------
Use the behavior adapter::
from collective.salesforce.content.interfaces import ISalesforceObject
sf_object_id = ISalesforceObject(item).sf_object_id
This works as long as ``item`` is an item whose type has the ISalesforceObject
behavior enabled.
Retrieve a Plone item by Salesforce Id
--------------------------------------
Query the ``sf_object_id`` catalog index::
from Products.CMFCore.utils import getToolByName
catalog = getToolByName(context, 'portal_catalog')
res = catalog.searchResults(sf_object_id=my_id)
if res:
item = res[0].getObject()
Set up vocabularies based on Salesforce picklists
-------------------------------------------------
If you want to set up a Choice field using a vocabulary from Salesforce,
you can use the ``SalesforcePicklist`` field instead::
Experience
False
The picklist values will be automatically retrieved from Salesforce when
needed, and cached in the ZODB.
For a multi-select choice (Set of Choice) that obtains its vocabulary this way,
use ``SalesforceMultiPicklist``::
Industries
False
Perform custom actions when objects are synced, or no longer synced
-------------------------------------------------------------------
Two events may be raised for an object during sync:
``collective.salesforce.content.events.NotFoundInSalesforceEvent``
An object event that indicates that this object was not returned by
Salesforce when its Dexterity type was synchronized.
There is an included, optional behavior which handles this event:
``collective.salesforce.content.interfaces.IPublishUpdated``
Causes an object to be published after it is updated from Salesforce during a sync.
``collective.salesforce.content.events.UpdatedFromSalesforceEvent``
An object event that indicates that this object was updated from Salesforce.
There are optional behaviors which handle this event:
``collective.salesforce.content.interfaces.IDeleteNotFound``
Causes an item with this behavior to be deleted from Plone if it is not
found during a Salesforce sync.
``collective.salesforce.content.interfaces.IRejectNotFound``
Causes an item to be rejected (in the workflow sense, i.e. made private) if
it is not found during a Salesforce sync.
Sync objects in a particular order
----------------------------------
Since the ``criteria`` directive is added to the end of the generated SOQL query,
it can be abused to specify an ORDER BY clause. For example, this schema::
would result in the following SOQL::
SELECT Id FROM Contact ORDER BY Name
There is not currently any mechanism to control the order in which multiple content
types using the ISalesforceObject behavior are synced, aside from triggering the
sync multiple times specifying different sets of content types.