Skip to content

Instantly share code, notes, and snippets.

@metadaddy
Last active February 20, 2016 18:38
Show Gist options
  • Select an option

  • Save metadaddy/7443834 to your computer and use it in GitHub Desktop.

Select an option

Save metadaddy/7443834 to your computer and use it in GitHub Desktop.
Mobile Inventory page showing HTML5 photo upload - works in Chrome on the desktop, Chatter Mobile 4.2, PhoneGap app etc.
<apex:page standardStylesheets="false" showHeader="false" sidebar="false"
standardController="Merchandise__c" extensions="MobileInventoryExtension"
recordSetVar="products">
<head>
<title>Mobile Inventory</title>
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
<apex:stylesheet value="{!URLFOR($Resource.jquerymobile130, 'jquery.mobile-1.3.0.min.css')}" />
<apex:stylesheet value="{!$Resource.style}" />
<apex:includeScript value="{!$Resource.jquery191}"/>
<apex:includeScript value="{!URLFOR($Resource.jquerymobile130, 'jquery.mobile-1.3.0.min.js')}"/>
<apex:includeScript value="{!$Resource.forcetk}"/>
<script>
var dataChanged = false;
var forcetkClient = new forcetk.Client();
forcetkClient.setSessionToken('{!$Api.Session_ID}');
// Log JS errors to console
window.onerror = function(message, url, lineNumber) {
console.log("Error: "+message+" in "+url+" at line "+lineNumber);
};
$(document).ready(function() {
$(".reloadLink").click(function() {
// true forces the page to be reloaded from the server
location.reload(true);
});
$(".updateButton").click(function() {
var id = $(this).parent().attr('data-id');
var merchRecord = {
id: id,
Quantity__c: parseInt($("#quantity"+id).val()),
Price__c: parseFloat($("#price"+id).val())
};
$.mobile.showPageLoadingMsg();
MobileInventoryExtension.updateMerchandiseItem(merchRecord, handleUpdate);
});
// Wire a button to the file control
$(".photoButton").click(function() {
var id = $(this).parent().attr('data-id');
$("#file"+id).click();
});
// Handle the file change event - user has selected a photo
$(".photoFile").change(handlePhoto);
});
$(document).on('pageinit', function() {
// Once jQuery Mobile is all done initializing the page
// We can't style the file control the way we'd like to
// so we hide it (via the div.ui-input-text element that
// jQuery Mobile inserts)...
$(".photoFile").closest('div.ui-input-text').hide();
});
function handlePhoto(evt) {
var file = evt.target.files[0];
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
// Extract raw base64 data from data URL
var imageData = e.target.result.split(',')[1];
// Upload the image data to Chatter files
$.mobile.showPageLoadingMsg();
forcetkClient.create('ContentVersion', {
"Origin": "H",
"PathOnClient": file.name,
"VersionData": imageData
}, function(data) {
makeContentVersionPublic(data.id, function(){
$.mobile.hidePageLoadingMsg();
alert('Success! Created ContentVersion ' + data.id);
}, onErrorSfdc);
}, onErrorSfdc);
};
})(file);
reader.readAsDataURL(file);
}
function handleUpdate(result,event) {
console.log('Response from server: '+result);
alert(result);
if (result == 'Item Updated') {
dataChanged = true;
}
$.mobile.hidePageLoadingMsg();
}
function onErrorSfdc(error) {
$.mobile.hidePageLoadingMsg();
console.log("onErrorSfdc: " + JSON.stringify(error));
alert(JSON.stringify(error));
}
function makeContentVersionPublic(contentVersionId, callback, error) {
MobileInventoryExtension.makeContentPublic(contentVersionId,
function(result, event){
if (event.status) {
callback();
} else {
error(event);
}
});
}
</script>
</head>
<body>
<!-- Main page, to display list of Merchandise once app starts -->
<div data-role="page" data-theme="b" id="mainpage">
<!-- page header -->
<div data-role="header">
<!-- button for reloading the page -->
<a href='#' class="reloadLink" data-role="button" data-icon='refresh'>
Reload
</a>
<!-- page title -->
<h2>Merchandise</h2>
</div>
<!-- page content -->
<div id="#content" data-role="content">
<img class="logo" width="200" src="{!$Resource.logo}"></img>
<!-- list of merchandise, links to detail pages -->
<ul data-inset="true" data-role="listview" data-theme="a"
data-dividertheme="a">
<apex:repeat value="{!products}" var="product">
<li>
<a href="#detailpage{!product.Id}">
{!product.Name}
</a>
</li>
</apex:repeat>
</ul>
</div>
</div>
<!-- Detail page, to display details when user clicks specific Merchandise record -->
<apex:repeat value="{!products}" var="product">
<div data-role="page" data-theme="b" id="detailpage{!product.Id}">
<!-- page header -->
<div data-role="header">
<!-- button for going back to mainpage -->
<a href='#mainpage' id="backInventory" class='ui-btn-left'
data-icon='home'>
Home
</a>
<!-- page title -->
<h1>Edit</h1>
</div>
<!-- page content -->
<div id="#content" data-role="content" data-id="{!product.Id}">
<h2 id="name">
<apex:outputField value="{!product.Name}"/>
</h2>
<div data-role="fieldcontain">
<label for="quantity{!product.Id}">Quantity:</label>
<input type="text" id="quantity{!product.Id}"
value="{!ROUND(product.Quantity__c, 0)}"/>
</div>
<div data-role="fieldcontain">
<label for="price{!product.Id}">Price ($):</label>
<input type="text" id="price{!product.Id}"
value="{!product.Price__c}"/>
</div>
<a href="#" data-role="button" class="updateButton" data-theme="b">
Update
</a>
<!-- File input control - we hide this when the page inits -->
<input class="photoFile" type="file" id="file{!product.Id}" accept="image/*"/>
<!-- Photo upload button - we wire this to the file input control -->
<a href="#" data-role="button" class="photoButton" data-theme="b">
Upload Photo
</a>
</div>
</div>
</apex:repeat>
</body>
</apex:page>
public class MobileInventoryExtension {
// Constructors. Needed to use as an extension.
public MobileInventoryExtension(ApexPages.StandardController c) {}
public MobileInventoryExtension(ApexPages.StandardSetController c) {}
// Remote Action function lets JavaScript call Apex directly.
// Method to update a given Merchandise record passed in from
// a Visualforce page JavaScript function
@RemoteAction
public static String updateMerchandiseItem(Merchandise__c merchRecord) {
try {
update merchRecord;
return 'Item Updated';
} catch (Exception e) {
return e.getMessage();
}
}
@RemoteAction
public static void makeContentPublic(String contentVersionId) {
ContentVersion cv = [SELECT ContentDocumentId
FROM ContentVersion
WHERE Id = :contentVersionId];
ContentDocumentLink cdl = new ContentDocumentLink(
ContentDocumentId = cv.ContentDocumentId,
LinkedEntityId = UserInfo.getOrganizationId(),
ShareType = 'V');
insert cdl;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment