Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e5ec692
upgrade to jquery mobile 1.1 GA
akhileshgupta Apr 14, 2012
21f6b0f
Fixed typos
Apr 24, 2012
e0e49b7
Merge pull request #1 from metadaddy-sfdc/master
Apr 24, 2012
eb8bf68
our first native iOS UI component is…the follow button!
jhersh May 1, 2012
2866b0e
readme tweak
jhersh May 1, 2012
9071966
readme update
jhersh May 1, 2012
923ed25
Merge pull request #2 from jhersh/master
May 1, 2012
3993f78
tweak actionsheet click function
jhersh May 1, 2012
943ccbb
add followbutton image
jhersh May 1, 2012
a5de678
follow button cleanup
jhersh May 1, 2012
7f9aba7
removing pixel ratio check in splitview
akhileshgupta May 16, 2012
28d82c3
removing the metadata.zip that was added accidently
akhileshgupta May 16, 2012
ff57267
Move Detail component JS controller declaration inside component. Att…
chexxor May 21, 2012
6df929b
Change names: eid->compElemId, compHandler->jsCtlrName, cntrl->server…
chexxor May 22, 2012
85642a2
Modularize and add debug parameter to Page, List, Nav.
chexxor May 22, 2012
807a3c8
Modularize and add debug parameter to Page, List, Nav.
chexxor May 22, 2012
259b778
Add gitignore file.
chexxor May 22, 2012
527a7a7
Change goToPage attribute to nextPage on List component.
chexxor May 22, 2012
b0cda97
Add filterClause attribute, fix filter->follower bug on List comp.
chexxor May 22, 2012
9031344
Rename components to compIdtoCtlrMap in App JS.
chexxor May 24, 2012
3740367
Rename id to elemId and compElemId to elemId. Add comments.
chexxor May 24, 2012
6af845b
Rename 'fields' to 'sFieldNames' to show SF field vs JS/OOP field.
chexxor May 25, 2012
b1e62d3
Create ThumbnailList component. WIP: Next page doesn't load.
chexxor May 27, 2012
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*~
build.properties
build.xml
12 changes: 6 additions & 6 deletions Visualforce/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Mobile Components for Visualforce #

Mobile Components for Visualforce is a free, open-source force.com library to simplify the development of mobile apps. The framework contains lightweight Visualforce UI components that generate cross-platform HTML5 output that runs well on smartphones and tablets. The apps can be deployed in the browser or embedded inside Container from the Salesforce Mobile SDK.
Mobile Components for Visualforce is a free, open-source Force.com library to simplify the development of mobile apps. The framework contains lightweight Visualforce UI components that generate cross-platform HTML5 output that runs well on smartphones and tablets. The apps can be deployed in the browser or embedded inside Container from the Salesforce Mobile SDK.
Note: The library is still in heavy development and is missing certain features as well as complete documentation.
This document is intended to introduce you to the app's architecture and design and make it as easy as possible for you to jump in, run it, and start contributing.

Expand All @@ -27,7 +27,7 @@ This document is intended to introduce you to the app's architecture and design

## Installation Steps ##
1. Grab the source code: `git clone https://github.com/ForceDotCom/MobileComponents.git`
2. Deploy the force.com metadata under MobileComponents/Visualforce/src folder to your destination org. You can deploy that using [Force.com Migration Tool](http://wiki.developerforce.com/index.php/Force.com_Migration_Tool) or by using [Force.com IDE](http://wiki.developerforce.com/index.php/Force.com_IDE)
2. Deploy the Force.com metadata under MobileComponents/Visualforce/src folder to your destination org. You can deploy that using [Force.com Migration Tool](http://wiki.developerforce.com/index.php/Force.com_Migration_Tool) or by using [Force.com IDE](http://wiki.developerforce.com/index.php/Force.com_IDE)
3. Login into your destination org and setup following:
1. Remote Site: Under Setup -> Administration Setup -> Security Controls -> Remote Site Settings, create a new Remote Site and specify your org's instance URL for the Remote Site URL. Eg. if your org is on instance NA1, the Remote Site URL will be `https://na1.salesforce.com`.

Expand All @@ -54,7 +54,7 @@ For Example: When a List component’s item is selected, the following with open
});

### Extending Mobile Web SDK Javascript Components
For more extensive customizations, Mobile Web SDK’s Javascript components may be extended to override or provide additional functionality to standard component behavior or styling. For example, the Visualforce.Mobile.ListComponent Javascript component provides a basic list item template for each row in a list. To customize the template, create a new Javascript class by extending Visualforce.Mobile.ListComponent and re-implement the getTemplate method. Lastly, register the new class with the List component’s compHandler attribute. When the List component is instantiated, an instance of the custom ListComponent will be created and its Mobile Web SDK lifecycle methods invoked. Class customizations may call the parent class’s implementation before or after additional functionality or replace the underlying implementations altogther. Custom implementations must extend Visualforce.Mobile.Component to hook into Mobile Web SDK lifecyle.
For more extensive customizations, Mobile Web SDK’s Javascript components may be extended to override or provide additional functionality to standard component behavior or styling. For example, the Visualforce.Mobile.ListComponent Javascript component provides a basic list item template for each row in a list. To customize the template, create a new Javascript class by extending Visualforce.Mobile.ListComponent and re-implement the getTemplate method. Lastly, register the new class with the List component’s compHandler attribute. When the List component is instantiated, an instance of the custom ListComponent will be created and its Mobile Web SDK lifecycle methods invoked. Class customizations may call the parent class’s implementation before or after additional functionality or replace the underlying implementations altogether. Custom implementations must extend Visualforce.Mobile.Component to hook into Mobile Web SDK lifecycle.

For Example: To provide a custom list item template:

Expand Down Expand Up @@ -83,7 +83,7 @@ This library makes use of a number of third-party components:
## FAQ ##

Q: Why did we create this library?
A: HTM5 developers need a robust set of components to build mobile apps. This library provides a way to share the lessons learned creating Contact Viewer and provide re-usable components that can be plugged into Visualforce.
A: HTML5 developers need a robust set of components to build mobile apps. This library provides a way to share the lessons learned creating Contact Viewer and provide re-usable components that can be plugged into Visualforce.

Q: Is this library dependent on jQuery Mobile?
A: jQuery Mobile is primarily used for transitions and UI components and makes it easy to incorporate other JQM components and plugins in your apps. Since the primary data components, such as List and Detail component, are independent of jQuery Mobile, one can also rip out and integrate with Sencha Touch or other frameworks.
Expand All @@ -95,13 +95,13 @@ Q: Can I customize the components?
A: Absolutely! Please share your experience

Q: Where should I provide feedback and bug reports?
A: We’re going to use github for all collaboration.
A: We’re going to use GitHub for all collaboration.

Q: Can I distribute this in my app?
A: Yes

Q: How is this framework supported?
A: This is unsupported software from the force.com development community. We will make our best efforts to fix bugs and add enhancements. We also encourage the community to fork the code and make independent changes.
A: This is unsupported software from the Force.com development community. We will make our best efforts to fix bugs and add enhancements. We also encourage the community to fork the code and make independent changes.

## Mobile Components for Visualforce License ##
Copyright (c) 2012, salesforce.com, inc. All rights reserved.
Expand Down
19 changes: 8 additions & 11 deletions Visualforce/src/classes/AppController.cls
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public class AppController extends BaseConfigController {
private final static String JQUERY_TEMPLATE_JS = 'http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js';
private final static String JQUERY_TEMPLATE_JS_DEBUG = 'http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.js';
private final static String JQUERY_MOBILE_JS = 'jqm11js';
private final static String I_CAN_HAZ_JS = 'ICanHaz';
private final static String MOBILE_SPLIT_VIEW_JS = 'jqmSVjs';
private final static String MOBILE_SDK_JS = 'MobileVisualforceSDKjs';

Expand All @@ -42,7 +43,7 @@ public class AppController extends BaseConfigController {
*/
public virtual class AppConfig extends BaseConfig {
public Boolean debug { get; set; }
String cntrl = getFullyQualifiedClassName(AppController.class);
String serverCtlrName = getFullyQualifiedClassName(AppController.class);
}

public AppController() {
Expand All @@ -64,6 +65,10 @@ public class AppController extends BaseConfigController {
public String getJqueryMobileJs() {
return getConfig().debug ? JQUERY_MOBILE_JS : (JQUERY_MOBILE_JS + 'Min');
}

public String getICanHazJs() {
return getConfig().debug ? I_CAN_HAZ_JS : (I_CAN_HAZ_JS + 'Min');
}

public String getMobileSplitViewJs() {
return getConfig().debug ? MOBILE_SPLIT_VIEW_JS : (MOBILE_SPLIT_VIEW_JS + 'Min');
Expand All @@ -75,15 +80,7 @@ public class AppController extends BaseConfigController {

public Component.Apex.OutputPanel getAdditionalScripts() {
Component.Apex.OutputPanel panel = new Component.Apex.OutputPanel(layout='none');

Component.Apex.IncludeScript script = new Component.Apex.IncludeScript();
script.expressions.value = '{!URLFOR($Resource[\'ListComponentJS\'])}';
panel.childComponents.add(script);

script = new Component.Apex.IncludeScript();
script.expressions.value = '{!URLFOR($Resource[\'DetailComponentJS\'])}';
panel.childComponents.add(script);


return panel;
}

Expand All @@ -101,4 +98,4 @@ public class AppController extends BaseConfigController {
public static Map<String,SObjectController.FieldMetadata> getFieldMetadata(List<String> fullyQualifiedFields) {
return SObjectController.getFieldMetadata(fullyQualifiedFields);
}
}
}
6 changes: 3 additions & 3 deletions Visualforce/src/classes/BaseConfig.cls
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
*/
public virtual class BaseConfig {

public String eid { get; set; }
public String compHandler { get; set; }
public String elemId { get; set; }
public String jsCtlrName { get; set; }

protected List<String> toArray(String str) {
return str.contains(',') ? str.split(',') : new List<String>{str};
Expand All @@ -43,4 +43,4 @@ public virtual class BaseConfig {
String typeStr = clazz.toString();
return typeStr.substring(typeStr.indexOf('=')+1, typeStr.lastIndexOf(']'));
}
}
}
27 changes: 17 additions & 10 deletions Visualforce/src/classes/DetailController.cls
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ layouts: [{
recordTypeName:
defaultRecordTypeMapping:
name: <not available>
fields:['field1', 'field2']
sFieldNames:['field1', 'field2']
detailSections: [{
heading:
columns:
Expand All @@ -49,8 +49,11 @@ Once layout info is fetched, future calls are just query calls directly from SOb

public with sharing class DetailController extends SObjectController {

private final static String DETAIL_COMPONENT_JS = 'DetailComponentJS';

public virtual class DetailConfig extends SObjectConfig {
public String cntrl = getFullyQualifiedClassName(DetailController.class);
public Boolean debug { get; set; }
public String serverCtlrName = getFullyQualifiedClassName(DetailController.class);
}

public DetailController() {
Expand All @@ -64,7 +67,11 @@ public with sharing class DetailController extends SObjectController {
public DetailConfig getConfig() {
return (DetailConfig)config;
}


public String getDetailComponentJS() {
return getConfig().debug ? DETAIL_COMPONENT_JS : (DETAIL_COMPONENT_JS + 'Min');
}

public class LayoutResponse extends RemoteResponse {
List<Layout> layouts;
public LayoutResponse(List<Layout> layouts) {
Expand All @@ -79,11 +86,11 @@ public with sharing class DetailController extends SObjectController {
String recordTypeName;
Boolean defaultRecordTypeMapping;
List<LayoutSection> detailSections;
Set<String> fields;
Set<String> sFieldNames;

public Layout() {
this.detailSections = new List<LayoutSection>();
this.fields = new Set<String>();
this.sFieldNames = new Set<String>();
}
}

Expand Down Expand Up @@ -114,12 +121,12 @@ public with sharing class DetailController extends SObjectController {
}
private void addComponent(SObjectType objectType, Layout layout, SFDCPartnerSoap.DescribeLayoutComponent comp) {
if (comp.type_x.equalsIgnoreCase('Field')) {
layout.fields.add(comp.value);
layout.sFieldNames.add(comp.value);
Schema.DescribeFieldResult fieldDesc = SchemaManager.getFieldDescribe(objectType, comp.value);
if (fieldDesc.getType() == Schema.DisplayType.Reference) {
String nameField = SchemaManager.getNameFieldForReferenceField(fieldDesc);
this.valueTemplate += '{{if '+ fieldDesc.getRelationshipName() +'}}${' + nameField + '}{{/if}}';
layout.fields.add(nameField);
layout.sFieldNames.add(nameField);
} else {
this.valueTemplate += '{{if typeof('+ comp.value +') != "undefined"}}' +
'{{html $item.value("' + fieldDesc.getType() + '",' + comp.value + ')}}' +
Expand Down Expand Up @@ -153,12 +160,12 @@ public with sharing class DetailController extends SObjectController {
}

@RemoteAction
public static SObjectResponse querySObject(DetailConfig config, Id sobjectId, List<String> fields) {
public static SObjectResponse querySObject(DetailConfig config, Id sobjectId, List<String> sFieldNames) {
DetailController controller = new DetailController(config);

Set<String> fieldsToQuery = new Set<String>();
fields.add('id');
for (String field : fields) fieldsToQuery.add(field.toLowerCase());
sFieldNames.add('id');
for (String fieldName : sFieldNames) fieldsToQuery.add(fieldName.toLowerCase());

return new SObjectResponse(controller.getDescribe().getSObjectType(),
controller.querySObject(fieldsToQuery, new Id[] {sobjectId}), DateTime.now());
Expand Down
40 changes: 30 additions & 10 deletions Visualforce/src/classes/ListController.cls
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@
* Controller for List component.
*/
public class ListController extends SObjectController {


private final static String LIST_COMPONENT_JS = 'ListComponentJS';

// config for list component
public virtual class ListConfig extends SObjectConfig {

public Boolean debug { get; set; }
public String labelField { get;
set {
labelField = value;
Expand All @@ -45,17 +48,18 @@ public class ListController extends SObjectController {
public String filter { get; set; }
public String listItemStyleClass { get; set; }
public String listDividerStyleClass { get; set; }
public String goToPage { get; set; } // FIXME: rename

public String nextPage { get; set; }

public String cntrl = getFullyQualifiedClassName(ListController.class);
public String serverCtlrName = getFullyQualifiedClassName(ListController.class);
}

// incoming list-based remote request
public class ListRequest extends RemoteRequest {
protected ListConfig config;
public ListRequest(ListConfig config, Map<String, String> values) {
super(values);
System.debug('config: ' + config);
System.debug('values: ' + values);
this.config = config;
}

Expand All @@ -76,7 +80,11 @@ public class ListController extends SObjectController {
public ListConfig getConfig() {
return (ListConfig)config;
}


public String getListComponentJS() {
return getConfig().debug ? LIST_COMPONENT_JS : (LIST_COMPONENT_JS + 'Min');
}

@RemoteAction
public static SObjectResponse invoke(ListRequest listRequest) {
ListController controller = new ListController(listRequest.getConfig());
Expand All @@ -87,7 +95,8 @@ public class ListController extends SObjectController {
public List<SObject> getList() {
DescribeSObjectResult descInfo = this.getDescribe();
Map<String, SObjectField> fieldMap = descInfo.fields.getMap();

ListConfig config = (ListConfig)this.config;

String soql = 'SELECT ';
String sortByFilter = '';

Expand All @@ -108,17 +117,28 @@ public class ListController extends SObjectController {
soql = soql.subString(0, soql.length()-1);

soql += (' FROM ' + this.getName());

String whereClause = '';

if (getConfig().filter != null && getConfig().filter.length() > 0) {
if (getConfig().filter.equalsIgnoreCase('owner')) soql += (' WHERE ownerId = \'' + UserInfo.getUserId() + '\'');
else if (this.isFeedEnabled() && getConfig().filter.equalsIgnoreCase('follow')) {
soql += (' WHERE Id IN (SELECT ParentId FROM EntitySubscription WHERE parent.type = \'' + this.getName() + '\'' +
else if (this.isFeedEnabled() && getConfig().filter.equalsIgnoreCase('follower')) {
whereClause += (' Id IN (SELECT ParentId FROM EntitySubscription WHERE parent.type = \'' + this.getName() + '\'' +
' AND SubscriberID = \'' + UserInfo.getUserId() + '\')');
}
}

if (config.filterClause != null && config.filterClause != '') {
whereClause += (whereClause != '') ? ' AND ' : '';
whereClause += config.filterClause;//REVIEWME: Worry about escaping this?
}

if (whereClause.length() > 0) {
soql += ' WHERE ' + whereClause;
}

soql += (sortByFilter + ' LIMIT 500'); // FIXME: reduce to much less, say 25 or 50, at most
System.debug('soql: ' + soql);

return Database.query(soql);
}
}
}
9 changes: 8 additions & 1 deletion Visualforce/src/classes/NavController.cls
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,24 @@
*/
public class NavController extends BaseConfigController {

private final static String NAV_COMPONENT_JS = 'NavComponentJS';

/**
* Nav component config options.
*/
public virtual class NavConfig extends BaseConfig {
public Boolean debug { get; set; }
public String pages { get; set; }
}

public NavController() {
super(new NavConfig());
}


public String getNavComponentJS() {
return getConfig().debug ? NAV_COMPONENT_JS : (NAV_COMPONENT_JS + 'Min');
}

public NavConfig getConfig() {
return (NavConfig)config;
}
Expand Down
13 changes: 10 additions & 3 deletions Visualforce/src/classes/PageController.cls
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,29 @@
* Controller for Page component.
*/
public class PageController extends BaseConfigController {


private final static String PAGE_COMPONENT_JS = 'PageComponentJS';

/**
* Page component config options.
*/
public virtual class PageConfig extends BaseConfig {
// behavior and styling
public Boolean debug { get; set; }
public String transition { get; set; }
public String css { get; set; }

String cntrl = getFullyQualifiedClassName(PageController.class);
String serverCtlrName = getFullyQualifiedClassName(PageController.class);
}

public PageController() {
super(new PageConfig());
}


public String getPageComponentJS() {
return getConfig().debug ? PAGE_COMPONENT_JS : (PAGE_COMPONENT_JS + 'Min');
}

public PageConfig getConfig() {
return (PageConfig)config;
}
Expand Down
2 changes: 1 addition & 1 deletion Visualforce/src/classes/SFDCApiClient.cls
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ public with sharing class SFDCApiClient {

return soapClient.describeLayout(sObjectType, recordTypeIds);
}
}
}
Loading