Last active
February 20, 2016 18:38
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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