Salesforce Cross-Organization Data Sharing

As a long time Salesforce-to-Salesforce (S2S) advocate and interested follower of the Saleforce integration space the Winter ’14 pilot of cross org data sharing, or COD for short, caught my attention recently. This short post covers the essentials only, for the detail refer to the linked PDF at the end of the post.

Note, the pilot is currently available for developer edition (DE) orgs exclusively, which gives some indication of where this feature is in term of production state.

What is it?
Hub and spoke architecture; the hub provides the metadata definition (selected fields) and data for shared objects, the spoke gets a read-only copy (proxy version) of the object named with the __xo suffix, e.g. myObj__xo.

Setup Configuration takes place at:
Data Management – Cross-Organization Data – (Hub | Spoke) Configuration

Connections are established via an invitation email, generated at the Hub, sent to a Spoke administrator and accepted.

Synchronisation takes place via the [Sync with Hub] action on the Spoke Detail page, which also displays the status of the org connection.

Limitations?
Synchronisation is a manual process, in the pilot release at least.
All custom objects can be shared but only the Account standard object.
Not all standard fields on Account can be shared,
API limits apply in addition to specify platform limits applicable to COD (for example, Spokes per Hub, concurrent Spoke connections).

Exemplar Use cases?
Master data sharing in Multi-org architectures.
Reference data sharing (product catalog, price list etc.) with business partners.

Key Differences with S2S?
Uni-directional synchronisation of data.
Spoke data is read-only.
S2S is not limited to the Account standard object (other limits apply)
S2S implies record-level sharing, COD is object-level (sharing permissions withstanding)
S2S initial record sharing can be automated (via ApexTrigger).
S2S externally shared records synchronise automatically.

Final Thoughts
COD is definitely an interesting new addition to the native platform integration capabilities. Data sharing between Salesforce customers via ETL tools or file exchange can be error prone and time expensive to maintain. COD provides utility in terms of simplification, centralised administration and tracking. In considering the pilot release, S2S remains the best fit for transactional data, whilst COD provides coverage of reference data and some master data scenarios.

References
COD Pilot Guide PDF
COD Overview – Salesforce Help

Salesforce1 App Customisation

At the time I started writing this post we were living in a Chatter Mobile world, this is no longer the case. As announced recently at Dreamforce ’13 we have entered the Salesforce1 era, the direction of which will no doubt become clearer as we transition to the Spring ’14 release. What is clear now however is that the unified platform is the focus this time, providing mobile enablement (apps) via a rich set of platform services (APIs x 10). A rich set of robust APIs is always great news for any development community, whether you’re connecting a mobile device or sensor device (IoT). I won’t venture further into what Salesforce1 is in concept or the implications, some of this is yet to be seen. In any case this blog is focused on the practical application of the Salesforce and Force.com technologies, and as such the following summary is a point-in-time outline of the information available at the time of writing.

So, on to the topic in hand, customisation options for the Salesforce1 mobile application.

As with most things in life, to make good design decisions it helps to understand the full set of options available (and to see the big picture). This obvious point is fundamental in architecting Salesforce platform solutions; it’s key to have knowledge of the complete standard feature set and extension points (declarative build model) such that technical solution components are minimised. Simply put, in the mobile context this guiding principle means exhaust the potential of the standard apps (meaning Salesforce1) and the customisation options before building a new custom mobile app from scratch.

The following summary notes attempt to clarify the function of the Salesforce1 app and the various customisation options.

The Salesforce1 App – What is it?
In application development terms the Salesforce1 app provides a flexible mobile container into which a blend of standard and custom functionality can be presented on a variety of connected devices (mobile, tablet etc.). The container itself comes in 2 forms; downloadable native app (iOS and Android) and mobile browser app. In either form Salesforce1 employs a feed-first paradigm, with publisher actions and a notification platform. The mobile browser app must be enabled from the setup menu under Mobile Administration->Salesforce1 and becomes the default when accessing Salesforce from supported devices (as-per Salesforce Touch). Salesforce1 does not replace the standard Salesforce web app, or Full Site as it is referred. Salesforce1 does replace Salesforce Touch, Chatter Mobile and Salesforce mobile apps.

Ok, so assuming we’re comfortable with the idea that Salesforce1 is a application container, how can we extend the functionality of the container to deliver custom application interactions?

Customisation Options
The following summary notes attempt to clarify the customisation options available with the Salesforce1 mobile app. Also included in the list are standard capabilities of the container app, provided for completeness. The Salesforce1 App Development Guide provides a comprehensive coverage of the topics including example code that can be loaded into a development org (via github) to experiment with.

— Mobile Navigation
Within the setup menu under Mobile Administration, the Mobile Navigation option provides the ability to define the content and structure of the left hand side menu of the container app (as shown in the screenshot below).

Salesforce1 Tablet

Note. The first item in the list becomes the home page.

— Custom Objects (declarative)
Self explanatory. Recently searched for objects appear under the Recent section in the mobile menu structure according to profile permissions. It would appear that page layouts are shared between Salesforce1 and the standard Salesforce web app. This may prove difficult in terms of providing a balanced structure that is effective in both views.

— Flexible Page Tabs (declarative and technical)
I’m not 100% clear on the use cases for Flexible pages, however they do exist and can be added to Flexible page Tabs and ultimately rendered in the container app via inclusion in the menu structure (via Mobile Navigation). I need to do some further investigation into the potential of Flexible Pages, perhaps I’m missing something obvious.

A Flexible Page can have global publisher actions, list views and scoped recently used items. A maximum of 25 components can be added to the page, this constraint applies to listviews and recently used items.

In terms of definition a Flexible page is manually created in XML and deployed to an org via the Force.com Migration Tool or IDE. Once a Flexible page exists in an org the Create->Tabs page will present a Flexible Page Tabs list (as per Visualforce tabs etc.).

— Publisher Actions (declarative and technical)
Publisher actions are accessed via the (+) button on the home page, Chatter tab, Chatter group and record detail pages.

Actions can be Standard or Custom and Global or Object scoped.

Standard actions include;
Create a Record – respects Validation Rules etc. A Create action defined for a object will automatically relate created child records.
Log a Call – record a completed Task
Update – update a record from the Chatter feed

Default standard actions are provided for the core standard objects (create and update), such actions have configurable layouts and the ability to predefine field values.

Custom actions include;
Visualforce pages
Canvas apps

Interestingly actions can be described and invoked via the REST API, resource paths below.
/services/data/v29.0/quickActions (Global)
/services/data/v29.0/sobjects/[object]/quickActions (Object)

— Compact Layouts (declarative)
Compact layouts are defined at the object level and are simply an ordered list of fields by definition. Compact layouts apply in the following areas;

Record highlights fields (first 4 fields in the ordered field list). The record highlights are displayed in the top region of a record detail display under the icon.
Defines the fields that appear in the Chatter feed if a record us created via publisher action.
Enhanced lookup mobile card (more on this below)
Object record preview card

— Mobile Cards (declarative)
Page layouts now have a section for Mobile Cards, the following items can be added which then appear as a swipe-to card within the record detail view within the Salesforce1 app only.

Visualforce Page
Expanded Lookup. Related object (via Lookup) Custom Layout fields are displayed.

— Visualforce Pages (technical)
There are a number of extension points within the Salesforce1 app where Visualforce pages can be employed.

Mobile Card
Custom Publisher Action
Visualforce Tab added to the Mobile Navigation

In all cases the page must be mobile enabled and designed specifically for rendering on constrained devices, i.e mobile plus tablet on a variety of form factors. A best practice to consider in this respect is the development of adaptable pages that support both Salesforce1 and standard Salesforce web app execution. There are 3 development models in this respect classic Visualforce, Visualforce mixed with JavaScript, JavaScript and static HTML only with JS remoting. In all models the recently added (to Visualforce) ability to pass through attributes (HTML5) will be of use. Additionally, Salesforce1 comes with a highly useful navigation framework JavaScript library.

In short, where branded or complex UI interactions are required Visualforce provides a good option. Such pages must be carefully designed to deliver a optimal mobile experience and where possible be adaptable to also work in the standard Salesforce web app. This latter point relates to the cost of development and maintenance, adaptability is better than duplication – at the cost of smart design.

— Force.com Canvas Apps (technical)
There are a number of extension points within the Salesforce1 app where Force.com Canvas apps can be employed. Pilot program currently.

Custom Publisher action – post to the feed from a Canvas app
Chatter Feed – Canvas app rendered in a feed item

The use cases for the above will be varied and no doubt very interesting. Over time I think we’ll see more traction in the area of application composition. The ability to compose experiences and interactions across applications seamlessly (with context) and securely is a powerful concept.

— Smart Search Items
Recently searched for objects for the user. Searches performed in the full web app appear can appear In the left navigation menu, pin objects in the full site to keep at the top of this list.

— Notifications (declarative)
The Salesforce1 notifications platform supports 2 types of notification;

In App – Appear in the app itself and can be accessed manually via the bell icon (top right hand corner)
Push – Mobile device applications only, push notifications not supported by the mobile browser app. Such notifications will appear as badges on the app icon etc.

The notifications are limited to Chatter (mentions, posts, comments etc.) and Approval requests. Notifications are enabled at the org-level, not per-user.

Final Thoughts
In application development terms, Salesforce1 provides a rich set of declarative and programmatic techniques for the development of mobile interactions. Building out compelling mobile experiences that replace or augment existing Saleforce functionality (custom or standard) should be highly achievable whether you’re technically minded or not. This in my view is the real power of the Saleforce1 app, once you accept the proprietary approach there are no barriers to rapid mobile enablement.

Two-Factor Authentication

Winter ’14 introduces two-factor authentication (2FA) for both User Interface and API logins, a long-awaited security feature enabled through User Profile, or Permission Set. The relevant permissions are:

Two-Factor Authentication for User Interface Logins
Two-Factor Authentication for API Logins

The second factor in question being a time-based token generated by a Salesforce supplied, device specific authenticator app (something similar to the RSA SecurID soft-token mobile app I would expect).

User Interface Login

For User Interface logins the authentication process is shown in the screenshot below. A Salesforce generated code is entered (or scanned via QR Code) into the authenticator app which generates the 6-digit time-based token to authenticate with. At the time of writing the authenticator app download link redirects to salesforce.com/mobile path, so the end-to-end process can’t be tested as yet.

auth_time_based_token_entry

API Login

Two-factor authentication for User Interface logins is a pre-requisite for API logins. In this context the time-based token replaces the standard security token in the API login call.

The time-based token is generated via the mobile authenticator app and added to a field on the User record, as shown below.

user_time_vased_token_input

Identity Confirmation
Time-based tokens can also be used during login verification to activate a computer/device.

I’ll update this post once the authenticator apps become available and the end to end process can be tested. I need to do some investigation also into the specifics of the persisted tokens (how long-lived are they etc.?).

Update 2013-10-23
I notice that the authenticator app information is at the bottom of the salesforce.com/mobile page. Instead of providing a proprietary approach the commonly used Google Authenticator service and related apps are used. iOS screenshot below.

photo

For more information on Google Authenticator refer to here and here.

Winter 14 Embedded Analytics

Super quick post on Embedded Analytics just in case this has passed anyone by and because it’s probably my favourite non-technical new feature for some time.

embedded_analytics_screenshot

In short, it is now possible to embed two charts (not reports) into a standard page layout (custom objects and standard objects), blurring the lines slightly between layouts and dashboards. The source report must be of the summary or matrix type and include a chart. Note the displayed analytics support manual refresh, but this is a limited resource (a users can refresh up to 100 report charts every 60 minutes, at the org-level this is 3,000 report charts every 60 minutes).

Hopefully the embedded analytics capability will be enhanced over future releases to remove the 2 chart limit and to support non-chart report outputs. A blended approach to the composition of layouts where dashboard components and standard page sections could be intermingled would be great – particularly if this supported dynamically resizing Visualforce page components. I can dream for now at least. In the meantime the Winter ’14 functionality covers a key functional area typically addressed through technical customisation or un-secure off-platform solutions using image formula fields (Google Charts anyone?). Standard functionality that directly removes the need to build such workarounds is a big positive.

Salesforce Analytics API

With Winter ’14 the new Analytics API turns GA. This RESTful style of API consumes standard API call limits, uses OAuth authentication and JSON request/response message formats (representations).

The key functionality of the Analytics API is the execution of reports (in synchronous or asynchronous modes), the application of dynamic filtering and the inspection of report metadata.

Note, in experimenting/testing any of the RESTful APIs (Force.com REST API, Chatter REST API, Analytics API etc.) the Apigee Salesforce API Consoles are an extremely convenient and useful tool – screenshot below.

apigee_screenshot

Core API Resources

– Report metadata (GET /services/data/v29.0/analytics/reports/report_ID/describe)

– List recently viewed reports (GET /services/data/v29.0/analytics/reports/list)
Note to obtain a full list of reports the standard REST API should be used to query the Report resource (via Soql).

– Synchronous report execution (/services/data/v29.0/analytics/reports/report_ID)
GET for a simple execution (add includeDetail=true to the Querystring if required)
POST to apply filtering.

– Asynchronous report execution (/services/data/v29.0/analytics/reports/report_ID/instances) – POST to request an execution.
GET to retrieve a list of the execution instances.
GET to the ../instances/instance_ID to request the results of a single execution.

– Fact Maps
The JSON message format for a report execution, synchronous or asynchronous, provides the detail and summary data in a Fact Map structure. In short, the keys provided in the structured groupings (across and down) data provided in the embedded report metadata are concatenated to provide a composite lookup key to detail row (hasDetailRows response flag indicates the existence of detail rows) and aggregate data held in the data map.

For more on Fact Maps refer to the “Decode the Fact Map” section in the Analytics API Reference.

Use Cases
The Analytics API is a long awaited key enabler for mobile/tablet analytics use cases, allowing on-platform reports to be executed and the results accessed in a constrained-device friendly lightweight format. Without this API mobile app developers have been limited to the execution of Soql queries via the standard REST API. With the API such developers have access to the recently viewed reports for a specific user, the report metadata, the ability to execute reports and then to pass the report results up to the graphics library of choice using a standard data format.

Outside of the mobile context, the API offers the same patterns to composite applications such as complementary web applications and 3rd party tools where datasets can be defined on-platform using reports (by end-users perhaps) and the data picked up for services such as mass email marketing etc.

Key Limitations
– 500 synchronous report executions per hour
– 1200 synchronous report execution requests per hour
– Asynchronous report results are available for 24 hours from execution

Report Response Example – Matrix Report

HTTP/1.1 200 OK
Sforce-Limit-Info:api-usage=21/5000000
Date:Sat, 28 Sep 2013 18:30:16 GMT
Content-Length:8705
Content-Type:application/json;charset=UTF-8

{
  "hasDetailRows": false,
  "attributes": {
    "describeUrl": "/services/data/v29.0/analytics/reports/00O300000041iFQEAX/describe",
    "instancesUrl": "/services/data/v29.0/analytics/reports/00O300000041iFQEAX/instances",
    "type": "Report",
    "reportId": "00O300000041iFQEAX",
    "reportName": "Example Report"
  },
  "groupingsDown": {
    "groupings": [
      {
        "value": "001Q000000PUcCtIAX",
        "key": "0",
        "groupings": [
          {
            "value": "2013-08-28",
            "key": "0_0",
            "groupings": [],
            "label": "2013/08/28 - 2013/09/03"
          }
        ],
        "label": "New Business"
      },
      {
        "value": "001Q000000Q0wLMIAX",
        "key": "1",
        "groupings": [
          {
            "value": "2013-09-18",
            "key": "1_0",
            "groupings": [],
            "label": "2013/09/18 - 2013/09/24"
          }
        ],
        "label": "Existing Business"
      },
      {
        "value": "001Q000000OwobqIAX",
        "key": "2",
        "groupings": [
          {
            "value": "2013-07-31",
            "key": "2_0",
            "groupings": [],
            "label": "2013/07/31 - 2013/08/06"
          }
        ],
        "label": "Prospects"
      }
    ]
  },
  "groupingsAcross": {
    "groupings": [
      {
        "value": "Product Sales",
        "key": "0",
        "groupings": [
          {
            "value": "a0AQ00000054Q0uMAX",
            "key": "0_0",
            "groupings": [],
            "label": "Product A"
          },
          {
            "value": "a0AQ0000004YV8xMAX",
            "key": "0_1",
            "groupings": [],
            "label": "Product B"
          }
        ],
        "label": "Product Sales"
      },
      {
        "value": "Services",
        "key": "1",
        "groupings": [
          {
            "value": "a0AQ0000005G0OpMAX",
            "key": "1_0",
            "groupings": [],
            "label": "Service A"
          }
        ],
        "label": "Services"
      }
    ]
  },
  "reportMetadata": {
    "name": "Example Report",
    "id": "00O300000041iFQEAX",
    "aggregates": [
      "RowCount"
    ],
    "groupingsDown": [
      {
        "name": "Client__c.Name",
        "sortOrder": "Asc",
        "dateGranularity": "None"
      },
      {
        "name": "Projection__c.Date__c",
        "sortOrder": "Asc",
        "dateGranularity": "Week"
      }
    ],
    "groupingsAcross": [
      {
        "name": "MyProduct__c.Classification__c",
        "sortOrder": "Asc",
        "dateGranularity": "None"
      },
      {
        "name": "MyProduct__c.Name",
        "sortOrder": "Asc",
        "dateGranularity": "None"
      }
    ],
    "reportType": {
      "type": "MyProduct_CRT__c",
      "label": "My Product CRT"
    },
    "reportBooleanFilter": null,
    "reportFilters": [],
    "detailColumns": [],
    "reportFormat": "MATRIX",
    "currency": null,
    "developerName": "Example_Report"
  },
  "factMap": {
    "1_0!T": {
      "aggregates": [
        {
          "value": 1,
          "label": "1"
        }
      ]
    },
    "0!1_0": {
      "aggregates": [
        {
          "value": 0,
          "label": "0"
        }
      ]
    },
    "1!0_0": {
      "aggregates": [
        {
          "value": 0,
          "label": "0"
        }
      ]
    },
    "1!0_1": {
      "aggregates": [
        {
          "value": 0,
          "label": "0"
        }
      ]
    },
    "2!T": {
      "aggregates": [
        {
          "value": 5,
          "label": "5"
        }
      ]
    },
    "0!0_0": {
      "aggregates": [
        {
          "value": 0,
          "label": "0"
        }
      ]
    },
    "0!0_1": {
      "aggregates": [
        {
          "value": 2,
          "label": "2"
        }
      ]
    },
    "1!1": {
      "aggregates": [
        {
          "value": 1,
          "label": "1"
        }
      ]
    },
    "T!1": {
      "aggregates": [
        {
          "value": 1,
          "label": "1"
        }
      ]
    },
    "1!0": {
      "aggregates": [
        {
          "value": 0,
          "label": "0"
        }
      ]
    },
    "2_0!1_0": {
      "aggregates": [
        {
          "value": 0,
          "label": "0"
        }
      ]
    },
    "2_0!0_1": {
      "aggregates": [
        {
          "value": 0,
          "label": "0"
        }
      ]
    },
    "0_0!1": {
      "aggregates": [
        {
          "value": 0,
          "label": "0"
        }
      ]
    },
    "0_0!0": {
      "aggregates": [
        {
          "value": 2,
          "label": "2"
        }
      ]
    },
    "2_0!0_0": {
      "aggregates": [
        {
          "value": 5,
          "label": "5"
        }
      ]
    },
    "T!0": {
      "aggregates": [
        {
          "value": 7,
          "label": "7"
        }
      ]
    },
    "1_0!1_0": {
      "aggregates": [
        {
          "value": 1,
          "label": "1"
        }
      ]
    },
    "2_0!0": {
      "aggregates": [
        {
          "value": 5,
          "label": "5"
        }
      ]
    },
    "2_0!1": {
      "aggregates": [
        {
          "value": 0,
          "label": "0"
        }
      ]
    },
    "1_0!0_0": {
      "aggregates": [
        {
          "value": 0,
          "label": "0"
        }
      ]
    },
    "1_0!0_1": {
      "aggregates": [
        {
          "value": 0,
          "label": "0"
        }
      ]
    },
    "0!1": {
      "aggregates": [
        {
          "value": 0,
          "label": "0"
        }
      ]
    },
    "T!1_0": {
      "aggregates": [
        {
          "value": 1,
          "label": "1"
        }
      ]
    },
    "0!0": {
      "aggregates": [
        {
          "value": 2,
          "label": "2"
        }
      ]
    },
    "1_0!1": {
      "aggregates": [
        {
          "value": 1,
          "label": "1"
        }
      ]
    },
    "1_0!0": {
      "aggregates": [
        {
          "value": 0,
          "label": "0"
        }
      ]
    },
    "2!1": {
      "aggregates": [
        {
          "value": 0,
          "label": "0"
        }
      ]
    },
    "2!0_1": {
      "aggregates": [
        {
          "value": 0,
          "label": "0"
        }
      ]
    },
    "2!0_0": {
      "aggregates": [
        {
          "value": 5,
          "label": "5"
        }
      ]
    },
    "0_0!0_0": {
      "aggregates": [
        {
          "value": 0,
          "label": "0"
        }
      ]
    },
    "0_0!0_1": {
      "aggregates": [
        {
          "value": 2,
          "label": "2"
        }
      ]
    },
    "2!0": {
      "aggregates": [
        {
          "value": 5,
          "label": "5"
        }
      ]
    },
    "T!T": {
      "aggregates": [
        {
          "value": 8,
          "label": "8"
        }
      ]
    },
    "0!T": {
      "aggregates": [
        {
          "value": 2,
          "label": "2"
        }
      ]
    },
    "1!T": {
      "aggregates": [
        {
          "value": 1,
          "label": "1"
        }
      ]
    },
    "2!1_0": {
      "aggregates": [
        {
          "value": 0,
          "label": "0"
        }
      ]
    },
    "0_0!T": {
      "aggregates": [
        {
          "value": 2,
          "label": "2"
        }
      ]
    },
    "T!0_0": {
      "aggregates": [
        {
          "value": 5,
          "label": "5"
        }
      ]
    },
    "1!1_0": {
      "aggregates": [
        {
          "value": 1,
          "label": "1"
        }
      ]
    },
    "T!0_1": {
      "aggregates": [
        {
          "value": 2,
          "label": "2"
        }
      ]
    },
    "2_0!T": {
      "aggregates": [
        {
          "value": 5,
          "label": "5"
        }
      ]
    },
    "0_0!1_0": {
      "aggregates": [
        {
          "value": 0,
          "label": "0"
        }
      ]
    }
  },
  "allData": true
}

Salesforce Winter 14 – Platform

Slightly depressing to be saying this in late August but Winter is almost upon us in the shape of the Winter ’14 (or v29.0) release. The Winter ’14 release notes are now available, find below a brief introduction to 10 Force.com highlights (in no order of significance).

1. Increased Sandbox Storage Limits. Developer sandboxes now enjoy a 200MB data storage capacity limit (and yes that is a massive increase from 10MB) enabling record storage to increase from 5k to 100k records. Configuration-only sandboxes double in capacity from 500MB to 1GB, and are renamed to Developer Pro. I understand that existing sandboxes will only increase in capacity post-refresh and will therefore require the production pod (NA1, EU3 etc.) to be upgraded to Winter ’14 before related existing sandboxes can be increased.

2. Performance Edition. Not exclusively a platform highlight but the introduction of Performance Edition is definitely worth a mention. It appears that Performance Edition will replace Unlimited Edition for new customers, in that UE will no longer be available to buy after November 2013. Performance Edition is essentially a bundled (or suite) offering comprised of core CRM, platform, Data.com, Work.com, Identity, Live Agent, Salesforce Knowledge plus at least one full-copy sandbox. The list price differential from UE appears to be $50 ($250 per user/per month increasing to $300), which seems reasonable in relative terms. I had hoped that “Performance” related to high-performance, very large data volume (VLDV) support etc. not this time apparently.

3. Developer Console Improvements. Some nice Visualforce related enhancements including a page preview feature within the console and the ability to inspect the viewstate (beta only). Additionally it is now possible to add and edit Static Resource files, a nice convenience particularly for JavaScript and CSS resources.

4. Apex Statement Limit Elimination. The script statement limit previously applied to an Apex context has now been replaced with an execution time limit; 10 seconds for synchronous contexts, 60 seconds for asynchronous. Time spent waiting for synchronous callouts to complete is not counted nor is time taken by the database to service queries and DML. I’ve always considered the governor limits applied by the platform to be a positive encouragement to write efficient code respectful of well defined and constant, predictable limits. The CPU time limit appears to be more variable in nature, i.e. Application server load may impact on context execution performance, and therefore could potentially be more problematic for developers to mitigate.

Excellent blog post on this topic by Dan Appleman

5. Flexible API Limits (Pilot). In short the platform now supports up to a 50% spike in inbound API calls (SOAP or REST) versus calculated allowance (function of edition and selected license counts). This additional flexibility makes a great deal of sense given the growth of off-platform composite apps such as mobile, where the API call utilisation can be inconsistent. This flexibility isn’t intended for continued use more to prevent limit exceptions on an occasional basis.

6. Visualforce – HTML5 Development. A new HTML5 friendly component , some additional attributes, plus the ability to pass-through attributes to the rendered HTML markup (key for JavaScript library interactions) have been added to Visualforce to enhance the HTML5 developer experience.

7. Visualforce – Server Side Viewstate (Pilot). There isn’t much in the way of detail available on this one, however it would seem that pages will be able to opt for a server-side viewstate, making the page weight considerably less. Quite how this change to a stateful server-side will be implemented is unclear, ideally the server side state cache would be persisted somehow across controllers within a Session. This would be extremely usedful (and hardly uncommon), avoiding a custom object solution to persisting state across multiple pages that do not share a common controller.

8. ISVforce – Delete Managed Package Component (Pilot). ISVs can now delete obsolete custom fields and tabs from a released managed package. A subscribing org that upgrades to the new package version will not have deletions applied automatically, instead deleted components will list as Unused Components on the Package Details page. It is therefore up to the ISV to inform their customers. For the ISV this is a great improvement in terms of maintainability.

9. Environment Hub (GA). Provides a central view on all orgs across a multi-org estate, including related orgs such as sandboxes and patch-orgs (which are auto-discovered), but also completely unrelated orgs. This feature requires activation via Salesforce support and will be extremely useful on both large implementation programmes and to ISVs juggling multiple QA, localisation, development, packaging orgs etc. Note, the hub org requires a My Domain to be configured which may be related to single sign-on (SSO), an area of Environment Hub functionality unclear from the release notes.

10. Canvas Apps as Chatter Actions and Chatter Feed Items (Pilot). The UI integration options for standard Canvas apps (i.e. not Visualforce page hosted) is now extended to enable apps to be integrated directly into the Chatter feed as either feed items or publisher actions. In the former case the initial view is collapsed (thumbnail, link and description), clicking the link opens the full canvas app (still in the Chatter feed) to enable a larger display you may not want to see in the feed otherwise. In both cases this is a useful extension to the integration potential for Canvas apps, it will be very interesting to see the real world use cases for this over the coming months.