var properties = {
"name": "ProductsOfflineStore",
"host": applicationContext.registrationContext.serverHost,
"port": getServerPort(),
"https": applicationContext.registrationContext.https,
"serviceRoot": appId,
"definingRequests": {
"ProductsDR": "/Products",
"SuppliersDR": "/Suppliers"
}
};
store = sap.OData.createOfflineStore(properties);
store.open(openStoreSuccessCallback, errorCallback);
sap.OData.applyHttpClient();
//sap.OData.removeHttpClient();
cordova plugin add cordova-plugin-network-information
cordova plugin add kapsel-plugin-logger --searchpath %KAPSEL_HOME%/plugins
cordova plugin add kapsel-plugin-odata --searchpath %KAPSEL_HOME%/plugins
or
cordova plugin add kapsel-plugin-logger --searchpath $KAPSEL_HOME/plugins
cordova plugin add kapsel-plugin-odata --searchpath $KAPSEL_HOME/plugins
[endpoint]
name=com.kapsel.gs
allow_omitting_max_length_facet=Y
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
<script type="text/javascript" charset="utf-8" src="datajs-1.1.2.min.js"></script>
<script type="text/javascript" charset="utf-8" src="serverContext.js"></script>
<script type="text/javascript" charset="utf-8" src="cordova.js"></script>
<script>
var applicationContext = null;
var online = false;
var store = null; //Offline OData store
var startTime = new Date();
var initTime = null;
var unlockTime = null;
var resumeTime = null;
window.onerror = onError;
function onError(msg, url, line) {
var idx = url.lastIndexOf("/");
var file = "unknown";
if (idx > -1) {
file = url.substring(idx + 1);
}
alert("An error occurred in " + file + " (at line # " + line + "): " + msg);
return false; //suppressErrorAlert;
}
function init() {
updateStatus2("Calling Logon.init");
initTime = new Date();
var endTime = new Date();
var duration = (endTime - startTime)/1000;
console.log("EventLogging: Time from onload to deviceready " + duration + " seconds");
if (sap.Logger) {
sap.Logger.setLogLevel(sap.Logger.DEBUG); //enables the display of debug log messages from the Kapsel plugins.
sap.Logger.debug("EventLogging: Log level set to DEBUG");
}
if (navigator.notification) { // Override default HTML alert with native dialog. alert is not supported on Windows
window.alert = navigator.notification.alert;
}
register();
console.log("EventLogging: init completed");
}
function logonSuccessCallback(result) {
updateStatus2("logonSuccessCallback called");
var endTime = new Date();
if (unlockTime) {
var duration = (endTime - unlockTime)/1000;
console.log("EventLogging: Unlock Time " + duration + " seconds");
unlockTime = null;
}
console.log("EventLogging: logonSuccessCallback " + JSON.stringify(result));
applicationContext = result;
showScreen("MainDiv");
}
function logonErrorCallback(error) { //this method is called if the user cancels the registration.
alert("An error occurred: " + JSON.stringify(error));
if (device.platform == "Android") { //Not supported on iOS
navigator.app.exitApp();
}
}
function read() {
updateStatus2("Read request started");
startTime = new Date();
if (!applicationContext) {
alert("Register or unlock before proceeding");
return;
}
clearTable();
sUrl = applicationContext.applicationEndpointURL + "/CarrierCollection?$format=json&$orderby=carrid"; //JSON format is less verbose than atom/xml
var oHeaders = {};
//oHeaders['X-SMP-APPCID'] = applicationContext.applicationConnectionId; //not needed as this will be sent by the logon plugin
var request = {
headers : oHeaders,
requestUri : sUrl,
method : "GET"
};
if (device.platform == "windows") { //provided by the authproxy and logon plugins on Android and iOS but not on Windows https://support.wdf.sap.corp/sap/support/message/1680272744
request.user = applicationContext.registrationContext.user;
request.password = applicationContext.registrationContext.password;
}
OData.read(request, readSuccessCallback, errorCallback);
}
function readSuccessCallback(data, response) {
var endTime = new Date();
var duration = (endTime - startTime)/1000;
updateStatus2("Read " + data.results.length + " records in " + duration + " seconds");
var carrierTable = document.getElementById("carrierTable");
for (var i = data.results.length -1; i >= 0; i--) {
var row = carrierTable.insertRow(1);
var cell1 = row.insertCell(0);
var cell2 = row.insertCell(1);
cell1.innerHTML = data.results[i].carrid;
cell2.innerHTML = data.results[i].CARRNAME;
}
}
function clearTable() {
var carrierTable = document.getElementById("carrierTable");
while(carrierTable.rows.length > 1) {
carrierTable.deleteRow(1);
}
}
function errorCallback(e) {
alert("An error occurred: " + JSON.stringify(e));
showScreen("MainDiv");
}
function register() {
updateStatus2("Calling Logon.init");
sap.Logon.init(logonSuccessCallback, logonErrorCallback, appId, context);
}
function unRegister() {
showScreen("LoadingDiv");
updateStatus2("Calling deleteRegistration");
sap.Logon.core.deleteRegistration(logonUnregisterSuccessCallback, errorCallback);
clearTable();
}
function logonUnregisterSuccessCallback(result) {
updateStatus2("Successfully Unregistered");
console.log("EventLogging: logonUnregisterSuccessCallback " + JSON.stringify(result));
applicationContext = null;
register();
}
function lock() {
sap.Logon.lock(logonLockSuccessCallback, errorCallback);
clearTable();
}
function logonLockSuccessCallback(result) {
console.log("EventLogging: logonLockSuccessCallback " + JSON.stringify(result));
applicationContext = null;
showScreen("LockedDiv"); //sap.Logon.unlock(function () {},function (error) {}); //alternatively show the unlock screen
}
function unlock() {
unlockTime = new Date();
sap.Logon.unlock(logonSuccessCallback, errorCallback);
}
function managePasscode() {
sap.Logon.managePasscode(managePasscodeSuccessCallback, errorCallback);
}
function managePasscodeSuccessCallback() {
console.log("EventLogging: managePasscodeSuccess");
}
function showScreen(screenIDToShow) {
var screenToShow = document.getElementById(screenIDToShow);
screenToShow.style.display = "block";
var screens = document.getElementsByClassName('screenDiv');
for (var i = 0; i < screens.length; i++) {
if (screens[i].id != screenToShow.id) {
screens[i].style.display = "none";
}
}
}
function openStore() {
console.log("EventLogging: openStore");
startTime = new Date();
updateStatus2("store.open called");
var properties = {
"name": "CarrierOfflineStore",
"host": applicationContext.registrationContext.serverHost,
"port": applicationContext.registrationContext.serverPort,
"https": applicationContext.registrationContext.https,
"serviceRoot" : appId,
//"storePath" : cordova.file.externalRootDirectory,
"definingRequests" : {
"CarriersDR" : "/CarrierCollection"
}
};
if (device.platform == "windows") {
var authStr = "Basic " + btoa(applicationContext.registrationContext.user + ":" + applicationContext.registrationContext.password);
properties.streamParams = "custom_header=Authorization:" + authStr;
}
store = sap.OData.createOfflineStore(properties);
var options = {};
store.open(openStoreSuccessCallback, errorCallback, options, progressCallback);
}
function openStoreSuccessCallback() {
var endTime = new Date();
var duration = (endTime - startTime)/1000;
updateStatus2("Store opened in " + duration + " seconds");
updateStatus1("Store is OPEN.");
console.log("EventLogging: openStoreSuccessCallback. Stored opened in " + duration);
sap.OData.applyHttpClient(); //Offline OData calls can now be made against datajs.
}
function closeStore() {
if (!store) {
updateStatus2("The store must be opened before it can be closed");
return;
}
console.log("EventLogging: closeStore");
updateStatus2("store.close called");
store.close(closeStoreSuccessCallback, errorCallback);
}
function closeStoreSuccessCallback() {
console.log("EventLogging: closeStoreSuccessCallback");
sap.OData.removeHttpClient();
updateStatus1("Store is CLOSED.");
updateStatus2("Store closed");
}
//Removes the physical store from the filesystem
function clearStore() {
console.log("EventLogging: clearStore");
if (!store) {
updateStatus2("The store must be closed before it can be cleared");
return;
}
store.clear(clearStoreSuccessCallback, errorCallback);
}
function clearStoreSuccessCallback() {
console.log("EventLogging: clearStoreSuccessCallback");
updateStatus1("");
updateStatus2("Store is CLEARED");
store = null;
}
function refreshStore() {
console.log("EventLogging: refreshStore");
if (!store) {
updateStatus2("The store must be open before it can be refreshed");
return;
}
startTime = new Date();
updateStatus2("Store refresh called");
store.refresh(refreshStoreCallback, errorCallback, null, progressCallback);
}
function refreshStoreCallback() {
console.log("EventLogging: refreshStoreCallback");
var endTime = new Date();
var duration = (endTime - startTime)/1000;
updateStatus2("Store refreshed in " + duration + " seconds");
}
function flushStore() {
console.log("EventLogging: flushStore");
if (!store) {
updateStatus2("The store must be open before it can be flushed");
return;
}
startTime = new Date();
updateStatus2("Store flush called");
store.flush(flushStoreSuccessCallback, errorCallback, null, progressCallback);
}
function flushStoreSuccessCallback() {
console.log("EventLogging: flushStoreSuccessCallback");
var endTime = new Date();
var duration = (endTime - startTime)/1000;
updateStatus2("Store flushed in " + duration + " seconds");
refreshStore();
}
function viewLog() {
if (sap.Logger) {
sap.Logger.getLogEntries(getLogEntriesSuccess, errorCallback)
}
else {
alert("ensure the kapsel-logger plugin has been added to the project");
}
}
function getLogEntriesSuccess(logEntries) {
var stringToShow = "";
var logArray;
if (device.platform == "windows") {
logArray = logEntries.split("\n");
if (logArray.length > 0) {
for (var i = 0; i < logArray.length; i++) {
stringToShow += logArray[i] + "\n";
}
}
}
else if (device.platform == "iOS") {
logArray = logEntries.split("\n");
if (logArray.length > 0) {
for (var i = 0; i < logArray.length; i++) {
logLineEntries = logArray[i].split(" ");
for (var j = 7; j < logLineEntries.length; j++) {
stringToShow += logLineEntries[j] + " ";
}
stringToShow = stringToShow + "\n";
}
}
}
else { //Android
logArray = logEntries.split('#');
if (logArray.length > 0) {
var numOfMessages = parseInt(logArray.length / 15);
for (var i = 0; i < numOfMessages; i++) {
stringToShow += logArray[i * 15 + 1] + ": " + logArray[i * 15 + 3] + ": " + logArray[i * 15 + 14] + "\n";
}
}
}
alert(stringToShow);
console.log("EventLogging: Device Log follows " + stringToShow);
}
function updateStatus1(msg) {
document.getElementById('statusID').innerHTML = msg + " " + getDeviceStatusString();
console.log("EventLogging: " + msg + " " + getDeviceStatusString());
}
function updateStatus2(msg) {
var d = new Date();
document.getElementById('statusID2').innerHTML = msg + " at " + addZero(d.getHours()) + ":" + addZero(d.getMinutes()) + "." + addZero(d.getSeconds());
console.log("EventLogging: " + msg + " at " + addZero(d.getHours()) + ":" + addZero(d.getMinutes()) + "." + addZero(d.getSeconds()));
}
function addZero(input) {
if (input < 10) {
return "0" + input;
}
return input;
}
function getDeviceStatusString() {
if (online) {
return "Device is ONLINE";
}
else {
return "Device is OFFLINE";
}
}
function progressCallback(progressStatus) {
var status = progressStatus.progressState;
var lead = "unknown";
if (status === sap.OfflineStore.ProgressState.STORE_DOWNLOADING) {
lead = "Downloading ";
}
else if (status === sap.OfflineStore.ProgressState.REFRESH) {
lead = "Refreshing ";
}
else if (status === sap.OfflineStore.ProgressState.FLUSH_REQUEST_QUEUE) {
lead = "Flushing ";
}
else if (status === sap.OfflineStore.ProgressState.DONE) {
lead = "Complete ";
}
else {
alert("Unknown status in progressCallback");
}
updateStatus2(lead + "Sent: " + progressStatus.bytesSent + " Received: " + progressStatus.bytesRecv + " File Size: " + progressStatus.fileSize);
}
function deviceOnline() {
online = true;
updateStatus1("");
}
function deviceOffline() {
online = false;
updateStatus1("");
}
function onLoad() {
console.log("EventLogging: onLoad");
}
function onBeforeUnload() {
console.log("EventLogging: onBeforeUnLoad");
}
function onUnload() {
console.log("EventLogging: onUnload");
}
function onPause() {
console.log("EventLogging: onPause");
}
function onResume() {
resumeTime = new Date();
console.log("EventLogging: onResume");
}
function onSapResumeSuccess() {
console.log("EventLogging: onSapResumeSuccess");
var endTime = new Date();
var duration = (endTime - resumeTime)/1000;
console.log("EventLogging: Time from onresume to onSapResumeSuccess " + duration + " seconds");
}
function onSapLogonSuccess() {
console.log("EventLogging: onSapLogonSuccess");
}
function onSapResumeError(error) {
console.log("EventLogging: onSapResumeError " + JSON.stringify(error));
}
document.addEventListener("deviceready", init, false);
document.addEventListener("pause", onPause, false);
document.addEventListener("resume", onResume, false);
document.addEventListener("online", deviceOnline, false);
document.addEventListener("offline", deviceOffline, false);
document.addEventListener("onSapResumeSuccess", onSapResumeSuccess, false);
document.addEventListener("onSapLogonSuccess", onSapLogonSuccess, false);
document.addEventListener("onSapResumeError", onSapResumeError, false);
</script>
</head>
<body onload="onLoad()" onunload="onUnload()" onbeforeunload="onBeforeUnload()">
<div class="screenDiv" id="LoadingDiv">
<h1>Loading ...</h1>
</div>
<div class="screenDiv" id="LockedDiv" style="display: none">
<h1>Locked</h1>
<button id="unlock2" onclick="unlock()">Unlock</button>
</div>
<div class="screenDiv" id="MainDiv" style="display: none">
<h1>Offline Sample</h1>
<button id="read" onclick="read()">Read</button>
<button id="unregister" onclick="unRegister()">Unregister</button>
<button id="openStore" onclick="setTimeout(openStore, 10);">Open Offline Store</button>
<button id="closeStore" onclick="closeStore()">Close Offline Store</button>
<button id="clearStore" onclick="clearStore()">Clear Offline Store</button>
<button id="sync" onclick="flushStore()">Flush and Refresh</button>
<button id="clearLog" onclick="sap.Logger.clearLog();updateStatus2('Cleared the Log')">Clear Log</button><br>
<button id="viewLog" onclick="viewLog()">View Log</button><br>
<span id="statusID"></span><br>
<span id="statusID2"></span>
<table id="carrierTable"><tr><th>Carrier ID</th><th>Carrier Name</th></tr></table>
</div>
</body>
</html>
cordova run android
or
cordova run windows --archs=x86
or
cordova run ios
"storePath" : cordova.file.externalRootDirectory,
cordova plugin add cordova-plugin-file
adb shell
cd /sdcard
ls CarrierOfflineStore.*
exit
adb pull /sdcard/CarrierOfflineStore.udb
adb pull /sdcard/CarrierOfflineStore.rq.udb
adb root
adb shell
cd /data/data/com.kapsel.gs/files
ls -l
exit
adb pull /data/data/com.kapsel.gs/files/CarrierOfflineStore.rq.udb
adb pull /data/data/com.kapsel.gs/files/CarrierOfflineStore.udb
C:\Users\i8xxxx\AppData\Local\Packages\com.kapsel.gs_h35559jr9hy9m\LocalState
C:\SAP\MobileSDK3\ilodata.exe store_name=./CarrierOfflineStore
get /CarrierCollection?$top=1
var options = {
"storeEncryptionKey" : "myVerySecretKey123!"
//"storeEncryptionKey" : applicationContext.registrationContext.password //if using the Logon plugin, the user entered password for the registration can be used if it never changes
};
store.open(openStoreSuccessCallback, errorCallback, options);
var otp = null; //one time password. Must be set once the first time the app starts
function logonSuccessCallback() {
getOneTimePassword();
...
}
function getOneTimePassword() {
sap.Logon.get(getSuccess, errorCallback, "key");
}
function getSuccess(val) {
if (val == null) {
val = Math.random().toString(36).substring(16);
sap.Logon.set(val);
return;
}
otp = val;
}
function openStore() {
...
var options = {
"storeEncryptionKey" : otp
};
store.open(openStoreSuccessCallback, errorCallback, options);
}
com.kapsel.gs2
https://hcpms-i82XXXXtrial.hanatrial.ondemand.com/SampleServices/ESPM.svc
cordova create C:\Kapsel_Projects\KapselGSDemo2 com.kapsel.gs2 KapselGSDemo2
cd C:\Kapsel_Projects\KapselGSDemo2
cordova platform add android
cordova platform add windows
or on iOS
cordova create ~/Documents/Kapsel_Projects/KapselGSDemo2 com.kapsel.gs2 KapselGSDemo2
cd ~/Documents/Kapsel_Projects/KapselGSDemo2
cordova platform add ios
cordova plugin add cordova-plugin-network-information
cordova plugin add cordova-plugin-dialogs
cordova plugin add kapsel-plugin-odata --searchpath %KAPSEL_HOME%/plugins
cordova plugin add kapsel-plugin-logon --searchpath %KAPSEL_HOME%/plugins
cordova plugin add kapsel-plugin-logger --searchpath %KAPSEL_HOME%/plugins
or
cordova plugin add kapsel-plugin-odata --searchpath $KAPSEL_HOME/plugins
cordova plugin add kapsel-plugin-logon --searchpath $KAPSEL_HOME/plugins
cordova plugin add kapsel-plugin-logger --searchpath $KAPSEL_HOME/plugins
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
<!--<meta http-equiv="Content-Security-Policy" content="default-src http://10.7.171.234:8080 'self' 'unsafe-inline' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">-->
<script type="text/javascript" charset="utf-8" src="datajs-1.1.2.min.js"></script>
<script type="text/javascript" charset="utf-8" src="serverContext.js"></script>
<script type="text/javascript" charset="utf-8" src="cordova.js"></script>
<script>
var applicationContext = null;
var store = null; //Offline OData store
var startTime = new Date();
var initTime = null;
var unlockTime = null;
var resumeTime = null;
window.onerror = onError;
function onError(msg, url, line) {
var idx = url.lastIndexOf("/");
var file = "unknown";
if (idx > -1) {
file = url.substring(idx + 1);
}
alert("An error occurred in " + file + " (at line # " + line + "): " + msg);
return false; //suppressErrorAlert;
}
function init() {
updateStatus2("Calling Logon.init");
initTime = new Date();
var endTime = new Date();
var duration = (endTime - startTime)/1000;
console.log("EventLogging: Time from onload to deviceready " + duration + " seconds");
if (sap.Logger) {
sap.Logger.setLogLevel(sap.Logger.DEBUG); //enables the display of debug log messages from the Kapsel plugins.
sap.Logger.debug("EventLogging: Log level set to DEBUG");
}
if (navigator.notification) { // Override default HTML alert with native dialog. alert is not supported on Windows
window.alert = navigator.notification.alert;
}
sap.Logon.init(logonSuccessCallback, logonErrorCallback, appId, context);
console.log("EventLogging: init completed");
}
function logonSuccessCallback(result) {
updateStatus2("logonSuccessCallback called");
var endTime = new Date();
if (unlockTime) {
var duration = (endTime - unlockTime)/1000;
console.log("EventLogging: Unlock Time " + duration + " seconds");
unlockTime = null;
}
applicationContext = result;
showScreen("MainDiv");
}
function logonErrorCallback(error) { //this method is called if the user cancels the registration.
alert("An error occurred: " + JSON.stringify(error));
if (device.platform == "Android") { //Not supported on iOS
navigator.app.exitApp();
}
}
function readProducts(isLocal, inErrorState) {
showScreen("MainDiv");
updateStatus2("Read request started");
startTime = new Date();
if (!applicationContext) {
alert("Register or unlock before proceeding");
return;
}
clearTable();
var sUrl = applicationContext.applicationEndpointURL + "/Products?$format=json&$orderby=Category,ProductId"; //JSON format is less verbose than atom/xml
if (isLocal === true) {
if (!store) {
updateStatus2("The store must be opened before reading local entities.");
return;
}
sUrl = sUrl + "&$filter=sap.islocal()";
}
if (inErrorState === true) {
if (!store) {
updateStatus2("The store must be opened before reading error entities.");
return;
}
sUrl = sUrl + "&$filter=sap.inerrorstate()";
}
var oHeaders = {};
//oHeaders['X-SMP-APPCID'] = applicationContext.applicationConnectionId; //not needed as this will be sent by the logon plugin
var request = {
headers : oHeaders,
requestUri : sUrl,
method : "GET"
};
if (device.platform == "windows") { //provided by the authproxy and logon plugins on Android and iOS but not on Windows https://support.wdf.sap.corp/sap/support/message/1680272744
request.user = applicationContext.registrationContext.user;
request.password = applicationContext.registrationContext.password;
}
OData.read(request, readSuccessCallback, errorCallback);
}
function createProducts() {
var updateForm = document.forms["CreateForm"];
updateForm.ID.value = "";
updateForm.prodName.value = "";
updateForm.currency.value = "";
updateForm.description.value = "";
updateForm.price.value = "";
showScreen("CreateDiv");
}
function readSuccessCallback(data, response) {
var endTime = new Date();
var duration = (endTime - startTime)/1000;
updateStatus2("Read " + data.results.length + " records in " + duration + " seconds");
var productsTable = document.getElementById("productsTable");
for (var i = data.results.length -1; i >= 0; i--) {
var row = productsTable.insertRow(1);
var dresult = data.results[i];
applyColor(data.results[i], row);
var cell1 = row.insertCell(0);
var cell2 = row.insertCell(1);
var cell3 = row.insertCell(2);
var cell4 = row.insertCell(3);
var cell5 = row.insertCell(4);
var productID = data.results[i].ProductId
cell1.innerHTML = data.results[i].ProductId;
cell2.innerHTML = '<a href="javascript:void(0)" onclick="showProductUpdateScreen(\'' + productID + '\')">' + data.results[i].Name + '</a>';
cell3.innerHTML = data.results[i].ShortDescription;
cell4.innerHTML = Math.round(data.results[i].Price * 100) / 100;
cell5.innerHTML = data.results[i].CategoryName;
}
}
function applyColor(data, row) {
if (data["@com.sap.vocabularies.Offline.v1.isLocal"]) {
row.style.color = "green";
}
if (data["@com.sap.vocabularies.Offline.v1.inErrorState"]) {
row.style.color = "red";
}
if (data["@com.sap.vocabularies.Offline.v1.isDeleteError"]) {
row.style.color = "orange";
}
}
function showProductUpdateScreen(productID) {
updateStatus2("Read product request started");
startTime = new Date();
showScreen("UpdateDiv");
var sUrl = applicationContext.applicationEndpointURL + "/Products('" + productID + "')";
//HTTP headers
var oHeaders = {};
//HTTP Request
var request = {
headers : oHeaders,
requestUri : sUrl,
method : "GET"
};
OData.read(request, readProductItemSuccessCallback, errorCallback);
}
function readProductItemSuccessCallback(data, response) {
var endTime = new Date();
var duration = (endTime - startTime)/1000;
updateStatus2("Read item in " + duration + " seconds");
var updateForm = document.forms["UpdateForm"];
//Add Properties that should be updated
updateForm.ID.value = data.ProductId;
updateForm.prodName.value = data.Name;
updateForm.description.value = data.ShortDescription;
updateForm.currency.value = data.CurrencyCode;
updateForm.price.value = Math.round(data.Price * 100) / 100;
}
function createRecord() {
var updateForm = document.forms["CreateForm"];
var updateProductsURL = applicationContext.applicationEndpointURL + "/Products";
var oHeaders = {};
var params = {};
//The below values will be set when we issue a patch
params.ProductId = updateForm.ID.value;
/*params.Name = updateForm.prodName.value;
params.CurrencyCode = updateForm.currency.value;
params.ShortDescription = updateForm.description.value;
params.Price = updateForm.price.value;*/
//HTTP Headers
/*if (csrf) {
oHeaders['X-CSRF-TOKEN'] = csrf;
}*/
oHeaders['Content-Type'] = "application/json";
oHeaders['accept'] = "application/json";
oHeaders['If-Match'] = "*"; //Some services needs If-Match Header for Update/delete
var request = {
headers : oHeaders,
requestUri : updateProductsURL,
method : "POST",
data : params
};
//Since Products has a hasStream = "true", the initial create or Post succeeds but the values are not null, need to do a Patch to set the values.
OData.request(request, patchEntity, errorCallback);
}
function patchEntity(data, response) {
var localOfflinePrimaryKey = data.__metadata.etag;
var updateForm = document.forms["CreateForm"];
var updateProductsURL = data.__metadata.uri; //applicationContext.applicationEndpointURL + "/Products";
var oHeaders = {};
var params = {};
//params.ProductId = updateForm.ID.value;
params.Name = updateForm.prodName.value;
params.CurrencyCode = updateForm.currency.value;
params.ShortDescription = updateForm.description.value;
params.Price = updateForm.price.value;
//HTTP Headers
/*if (csrf) {
oHeaders['X-CSRF-TOKEN'] = csrf;
}*/
oHeaders['Content-Type'] = "application/json";
oHeaders['accept'] = "application/json";
oHeaders['If-Match'] = "*"; //Some services needs If-Match Header for Update/delete
var request = {
headers : oHeaders,
requestUri : updateProductsURL,
method : "PATCH",
data : params
};
OData.request(request, readProducts, errorCallback);
}
function updateRecord() {
var updateForm = document.forms["UpdateForm"];
var updateProductsURL = applicationContext.applicationEndpointURL + "/Products('" + updateForm.ID.value + "')";
var oHeaders = {};
var params = {};
params.Name = updateForm.prodName.value;
params.CurrencyCode = updateForm.currency.value;
params.ShortDescription = updateForm.description.value;
params.Price = updateForm.price.value;
//HTTP Headers
/*if (csrf) {
oHeaders['X-CSRF-TOKEN'] = csrf;
}*/
oHeaders['Content-Type'] = "application/json";
oHeaders['accept'] = "application/json";
oHeaders['If-Match'] = "*"; //Some services needs If-Match Header for Update/delete
var request = {
headers : oHeaders,
requestUri : updateProductsURL,
method : "PUT", //MERGE/PATCH/PUT
data : params
};
OData.request(request, readProducts, errorCallback);
}
function deleteRecord() {
var updateForm = document.forms["UpdateForm"];
var deleteStockURL = applicationContext.applicationEndpointURL + "/Stock('" + updateForm.ID.value + "')";
var deleteProductsURL = applicationContext.applicationEndpointURL + "/Products('" + updateForm.ID.value + "')";
var oHeaders = {};
var params = {};
oHeaders['Content-Type'] = "application/json";
oHeaders['accept'] = "application/json";
oHeaders['If-Match'] = "*"; //Some services needs If-Match Header for Update/delete
var request = {
headers : oHeaders,
requestUri : deleteStockURL,
method : "DELETE",
//data : params
};
OData.request(request, function() {console.log("Delete stock " + updateForm.ID.value + " succeeded.")}, function(e) {console.log("Delete Request failed: " + JSON.stringify(e))}); //might not be any stock entries
request.requestUri = deleteProductsURL;
OData.request(request, readProducts, errorCallback);
}
function clearTable2(tableId) {
var productsTable = document.getElementById(tableId);
while(productsTable.rows.length > 1) {
productsTable.deleteRow(1);
}
}
function clearTable() {
var productsTable = document.getElementById("productsTable");
while(productsTable.rows.length > 1) {
productsTable.deleteRow(1);
}
}
function errorCallback(e) {
alert("An error occurred: " + JSON.stringify(e));
console.log("EventLogging: errorCallback " + JSON.stringify(e));
showScreen("MainDiv");
}
function register() {
updateStatus2("Calling Logon.init");
sap.Logon.init(logonSuccessCallback, logonErrorCallback, appId, context);
}
function unRegister() {
updateStatus2("Calling deleteRegistration");
sap.Logon.core.deleteRegistration(logonUnregisterSuccessCallback, errorCallback);
clearTable();
}
function logonUnregisterSuccessCallback(result) {
updateStatus2("Successfully Unregistered");
console.log("EventLogging: logonUnregisterSuccessCallback " + JSON.stringify(result));
applicationContext = null;
register();
}
function lock() {
sap.Logon.lock(logonLockSuccessCallback, errorCallback);
clearTable();
}
function logonLockSuccessCallback(result) {
console.log("EventLogging: logonLockSuccessCallback " + JSON.stringify(result));
applicationContext = null;
showScreen("LockedDiv"); //sap.Logon.unlock(function () {},function (error) {}); //alternatively show the unlock screen
}
function unlock() {
unlockTime = new Date();
sap.Logon.unlock(logonSuccessCallback, errorCallback);
}
function managePasscode() {
sap.Logon.managePasscode(managePasscodeSuccessCallback, errorCallback);
}
function managePasscodeSuccessCallback() {
console.log("EventLogging: managePasscodeSuccess");
}
function showScreen(screenIDToShow) {
var screenToShow = document.getElementById(screenIDToShow);
screenToShow.style.display = "block";
var screens = document.getElementsByClassName('screenDiv');
for (var i = 0; i < screens.length; i++) {
if (screens[i].id != screenToShow.id) {
screens[i].style.display = "none";
}
}
}
function openStore() {
console.log("EventLogging: openStore");
startTime = new Date();
updateStatus2("store.open called");
var properties = {
"name": "CarrierOfflineStore",
"host": applicationContext.registrationContext.serverHost,
"port": applicationContext.registrationContext.serverPort,
"https": applicationContext.registrationContext.https,
"serviceRoot" : appId,
//"storePath" : cordova.file.externalRootDirectory,
"definingRequests" : {
"ProductsDR" : "/Products",
"StockDR" : "/Stock"
}
};
if (device.platform == "windows") {
var authStr = "Basic " + btoa(applicationContext.registrationContext.user + ":" + applicationContext.registrationContext.password);
properties.streamParams = "custom_header=Authorization:" + authStr;
}
store = sap.OData.createOfflineStore(properties);
store.onrequesterror = onRequestError; //called for each modification error during flush
var options = {};
store.open(openStoreSuccessCallback, errorCallback, options, progressCallback);
}
function onRequestError(error) {
navigator.notification.alert("An error occurred while FLUSHING " + JSON.stringify(error));
console.log("An error occurred while FLUSHING " + JSON.stringify(error));
}
function openStoreSuccessCallback() {
var endTime = new Date();
var duration = (endTime - startTime)/1000;
updateStatus2("Store opened in " + duration + " seconds");
updateStatus1("Store is OPEN.");
console.log("EventLogging: openStoreSuccessCallback. Stored opened in " + duration);
sap.OData.applyHttpClient(); //Offline OData calls can now be made against datajs.
}
function closeStore() {
if (!store) {
updateStatus2("The store must be opened before it can be closed");
return;
}
console.log("EventLogging: closeStore");
updateStatus2("store.close called");
store.close(closeStoreSuccessCallback, errorCallback);
}
function closeStoreSuccessCallback() {
console.log("EventLogging: closeStoreSuccessCallback");
sap.OData.removeHttpClient();
updateStatus1("Store is CLOSED.");
updateStatus2("Store closed");
}
//Removes the physical store from the filesystem
function clearStore() {
console.log("EventLogging: clearStore");
if (!store) {
updateStatus2("The store must be closed before it can be cleared");
return;
}
store.clear(clearStoreSuccessCallback, errorCallback);
}
function clearStoreSuccessCallback() {
console.log("EventLogging: clearStoreSuccessCallback");
updateStatus1("");
updateStatus2("Store is CLEARED");
store = null;
}
function refreshStore() {
console.log("EventLogging: refreshStore");
if (!store) {
updateStatus2("The store must be open before it can be refreshed");
return;
}
startTime = new Date();
updateStatus2("Store refresh called");
store.refresh(refreshStoreCallback, errorCallback, null, progressCallback);
}
function refreshStoreCallback() {
console.log("EventLogging: refreshStoreCallback");
var endTime = new Date();
var duration = (endTime - startTime)/1000;
updateStatus2("Store refreshed in " + duration + " seconds");
}
function flushStore() {
console.log("EventLogging: flushStore");
if (!store) {
updateStatus2("The store must be open before it can be flushed");
return;
}
startTime = new Date();
updateStatus2("Store flush called");
store.flush(flushStoreSuccessCallback, errorCallback, null, progressCallback);
}
function flushStoreSuccessCallback() {
console.log("EventLogging: flushStoreSuccessCallback");
var endTime = new Date();
var duration = (endTime - startTime)/1000;
updateStatus2("Store flushed in " + duration + " seconds");
refreshStore();
}
function viewLog() {
if (sap.Logger) {
sap.Logger.getLogEntries(getLogEntriesSuccess, errorCallback)
}
else {
alert("ensure the kapsel-logger plugin has been added to the project");
}
}
function getLogEntriesSuccess(logEntries) {
var stringToShow = "";
var logArray;
if (device.platform == "windows") {
logArray = logEntries.split("\n");
if (logArray.length > 0) {
for (var i = 0; i < logArray.length; i++) {
stringToShow += logArray[i] + "\n";
}
}
}
else if (device.platform == "iOS") {
logArray = logEntries.split("\n");
if (logArray.length > 0) {
for (var i = 0; i < logArray.length; i++) {
logLineEntries = logArray[i].split(" ");
for (var j = 7; j < logLineEntries.length; j++) {
stringToShow += logLineEntries[j] + " ";
}
stringToShow = stringToShow + "\n";
}
}
}
else { //Android
logArray = logEntries.split('#');
if (logArray.length > 0) {
var numOfMessages = parseInt(logArray.length / 15);
for (var i = 0; i < numOfMessages; i++) {
stringToShow += logArray[i * 15 + 1] + ": " + logArray[i * 15 + 3] + ": " + logArray[i * 15 + 14] + "\n";
}
}
}
alert(stringToShow);
console.log("EventLogging: Device Log follows " + stringToShow);
}
function updateStatus1(msg) {
document.getElementById('statusID').innerHTML = msg + " " + getDeviceStatusString();
console.log("EventLogging: " + msg + " " + getDeviceStatusString());
}
function updateStatus2(msg) {
var d = new Date();
document.getElementById('statusID2').innerHTML = msg + " at " + addZero(d.getHours()) + ":" + addZero(d.getMinutes()) + "." + addZero(d.getSeconds());
if (msg.indexOf("Refreshing Sent:") != 0) { //don't bother logging the messages regarding the progress callback for flush and refresh
console.log("EventLogging: " + msg + " at " + addZero(d.getHours()) + ":" + addZero(d.getMinutes()) + "." + addZero(d.getSeconds()));
}
}
function addZero(input) {
if (input < 10) {
return "0" + input;
}
return input;
}
function getDeviceStatusString() {
if (online) {
return "Device is ONLINE";
}
else {
return "Device is OFFLINE";
}
}
function progressCallback(progressStatus) {
var status = progressStatus.progressState;
var lead = "unknown";
if (status === sap.OfflineStore.ProgressState.STORE_DOWNLOADING) {
lead = "Downloading ";
}
else if (status === sap.OfflineStore.ProgressState.REFRESH) {
lead = "Refreshing ";
}
else if (status === sap.OfflineStore.ProgressState.FLUSH_REQUEST_QUEUE) {
lead = "Flushing ";
}
else if (status === sap.OfflineStore.ProgressState.DONE) {
lead = "Complete ";
}
else {
alert("Unknown status in progressCallback");
}
updateStatus2(lead + "Sent: " + progressStatus.bytesSent + " Received: " + progressStatus.bytesRecv + " File Size: " + progressStatus.fileSize);
}
function deviceOnline() {
online = true;
updateStatus1("");
}
function deviceOffline() {
online = false;
updateStatus1("");
}
function onLoad() {
console.log("EventLogging: onLoad");
}
function onBeforeUnload() {
console.log("EventLogging: onBeforeUnLoad");
}
function onUnload() {
console.log("EventLogging: onUnload");
}
function onPause() {
console.log("EventLogging: onPause");
}
function onResume() {
resumeTime = new Date();
console.log("EventLogging: onResume");
}
function onSapResumeSuccess() {
console.log("EventLogging: onSapResumeSuccess");
var endTime = new Date();
var duration = (endTime - resumeTime)/1000;
console.log("EventLogging: Time from onresume to onSapResumeSuccess " + duration + " seconds");
}
function onSapLogonSuccess() {
console.log("EventLogging: onSapLogonSuccess");
}
function onSapResumeError(error) {
console.log("EventLogging: onSapResumeError " + JSON.stringify(error));
}
function showErrorsSuccessCallback(data, response) {
updateStatus2("ErrorArchive contains " + data.results.length + " records ");
console.log(JSON.stringify(data.results));
clearTable2("ErrorsTable");
showScreen("ErrorsDiv");
var errorsForm = document.getElementById("ErrorsForm");
errorsForm.setAttribute("requestid", "");
var errorsTable = document.getElementById("ErrorsTable");
for (var i = 0; i < data.results.length; i++) {
var row = errorsTable.insertRow(1);
var product = JSON.parse(data.results[i].RequestBody);
var cell1 = row.insertCell(0);
var cell2 = row.insertCell(1);
var cell3 = row.insertCell(2);
var cell4 = row.insertCell(3);
var cell5 = row.insertCell(4);
var cell6 = row.insertCell(5);
if (i == 0) { //any requestID will work in the delete call to the ErrorArchive
errorsForm.setAttribute("requestid", data.results[i].RequestID);
}
cell1.innerHTML = data.results[i].RequestMethod;
cell2.innerHTML = data.results[i].HTTPStatusCode;
if (product) {
cell3.innerHTML = product.Name;
cell4.innerHTML = product.Price;
}
cell5.innerHTML = data.results[i].RequestURL;
cell6.innerHTML = data.results[i].Message;
}
}
//Uses the Error Archive. You can also view rows that are in an error state using a filter or by looking for an annotation. See the read() method
function showErrors() {
if (!store) {
updateStatus2("The store must be opened before viewing the ErrorArchive");
return;
}
updateStatus2("ErrorArchive request started");
clearTable2("ErrorsTable");
var sURL = applicationContext.applicationEndpointURL + "/ErrorArchive";
var oHeaders = {};
var request = {
headers : oHeaders,
requestUri : sURL,
method : "GET"
};
console.log("read using " + sURL);
OData.read(request, showErrorsSuccessCallback, errorCallback);
}
function clearErrors() {
var requestID = document.forms["ErrorsForm"].getAttribute("requestid");
if (requestID == "") {
alert("There are no errors to remove from the ErrorArchive");
return;
}
navigator.notification.confirm(
"Proceeding will revert all operations that are currently in an error state", // message
clearErrors2, // callback to invoke with index of button pressed
"Warning", // title
["Continue","Cancel"] // buttonLabels
);
}
function clearErrors2(buttonIndex) {
if (buttonIndex == 2) {
return
}
console.log("Clearing error!")
updateStatus2("Clear ErrorArchive entry started");
var requestID = document.forms["ErrorsForm"].getAttribute("requestid");
var deleteErrorsURL = applicationContext.applicationEndpointURL + "/ErrorArchive(" + requestID + ")";
var oHeaders = {};
oHeaders['Content-Type'] = "application/json";
oHeaders['accept'] = "application/json";
oHeaders['If-Match'] = "*"; //Some services needs If-Match Header for Update/delete
var request = {
headers : oHeaders,
requestUri : deleteErrorsURL,
method : "DELETE"
};
console.log(deleteErrorsURL);
OData.request(request, showErrors, errorCallback);
}
function checkRequestQueue() {
if (!store) {
updateStatus2("The store must be opened before checking the request queue");
return;
}
store.getRequestQueueStatus(requestQSuccessCallback, errorCallback)
}
function requestQSuccessCallback(qStatus) {
var statusStr = " contains items to be flushed";
if (qStatus.isEmpty) {
statusStr = " is empty";
}
updateStatus2("Request queue" + statusStr);
}
document.addEventListener("deviceready", init, false);
document.addEventListener("pause", onPause, false);
document.addEventListener("resume", onResume, false);
document.addEventListener("online", deviceOnline, false);
document.addEventListener("offline", deviceOffline, false);
document.addEventListener("onSapResumeSuccess", onSapResumeSuccess, false);
document.addEventListener("onSapLogonSuccess", onSapLogonSuccess, false);
document.addEventListener("onSapResumeError", onSapResumeError, false);
</script>
</head>
<body onload="onLoad()" onunload="onUnload()" onbeforeunload="onBeforeUnload()">
<div class="screenDiv" id="LoadingDiv">
<h1>Loading ...</h1>
</div>
<div class="screenDiv" id="LockedDiv" style="display: none">
<h1>Locked</h1>
<button id="unlock2" onclick="unlock()">Unlock</button>
</div>
<div class="screenDiv" id="UpdateDiv" style="display: none">
<h3>Update</h3>
<form id="UpdateForm">
ProductID: <input type="text" name="ID" ><br>
Name: <input type="text" name="prodName" ><br>
Currency: <input type="text" name="currency" ><br>
Description: <input type="text" name="description" ><br>
Price: <input type="text" name="price"><br>
<button type=button onclick="updateRecord()">Update</button>
<button type=button onclick="deleteRecord()">Delete</button>
<button type=button onclick="showScreen('MainDiv')">Back</button>
</form>
</div>
<div class="screenDiv" id="CreateDiv" style="display: none">
<h3>Create</h3>
<form id="CreateForm">
ProductID: <input type="text" name="ID" ><br>
Name: <input type="text" name="prodName" ><br>
Currency: <input type="text" name="currency" ><br>
Description: <input type="text" name="description" ><br>
Price: <input type="text" name="price"><br>
<button type=button onclick="createRecord()">Save</button>
<button type=button onclick="showScreen('MainDiv')">Back</button>
</form>
</div>
<div class="screenDiv" id="ErrorsDiv" style="display: none">
<h3>Errors</h3>
<form id="ErrorsForm">
<table id="ErrorsTable">
<tr>
<th align="left">Op</th>
<th align="left">Code</th>
<th align="left">Name</th>
<th align="left">Price</th>
<th align="left">URL</th>
<th align="left">Message</th>
</tr>
</table>
</form>
<button id="clearError" onclick="clearErrors()">Clear Errors</button>
<button type=button onclick="showScreen('MainDiv')">Back</button>
</div>
<div class="screenDiv" id="MainDiv" style="display: none">
<h1>Offline Sample 2</h1>
<button id="unregister" onclick="unRegister()">Unregister</button>
<button id="read" onclick="readProducts()">Read</button>
<button id="createItem" onclick="createProducts()">Create</button>
<button id="clearLog" onclick="sap.Logger.clearLog();updateStatus2('Cleared the Log')">Clear Log</button>
<button id="viewLog" onclick="viewLog()">View Log</button><br>
<button id="openStore" onclick="setTimeout(openStore, 10);">Open Offline Store</button>
<button id="closeStore" onclick="closeStore()">Close Offline Store</button>
<button id="clearStore" onclick="clearStore()">Clear Offline Store</button><br>
<button id="readlocal" onclick="readProducts(true)">Read Local</button>
<button id="read3" onclick="readProducts(false, true)">Filter on Errors</button>
<button id="sync" onclick="flushStore()">Flush and Refresh</button>
<button id="showErrors" onclick="showErrors()">Error Archive</button>
<button id="requestQueue" onclick="checkRequestQueue()">Request Queue</button><br>
<span id="statusID"></span><br>
<span id="statusID2"></span>
<table id="productsTable"><tr><th align='left'>ID</th><th align='left'>Name</th><th align='left'>Description</th><th align='left'>Price</th><th align='left'>Category</th></tr></table>
</div>
</body>
</html>
cordova run android
or
cordova run windows --archs=x86
or
cordova run ios
$filter=sap.islocal()
{"uri":"https://hcpms-i826567trial.hanatrial.ondemand.com:443/com.kapsel.gs2/Products('HT-101')",
"type":"ESPM.Product"},
"@com.sap.vocabularies.Offline.v1.isLocal":true,
"@com.sap.vocabularies.Offline.v1.inErrorState":true,
"Name":"USB Battery"
...
&$filter=sap.inerrorstate()
store.onrequesterror = onRequestError; //called for each modification error during flush
https://hcpms-XXXXtrial.hanatrial.ondemand.com/com.kapsel.gs2/Products
https://hcpms-i826567trial.hanatrial.ondemand.com/com.kapsel.gs3/Products
function openStore() {
...
var properties = {
"name": "CarrierOfflineStore",
"host": applicationContext.registrationContext.serverHost,
"port": applicationContext.registrationContext.serverPort,
"https": applicationContext.registrationContext.https,
"serviceRoot" : appId,
"definingRequests" : {
"ProductsDR" : "/Products",
"StockDR" : "/Stock"
}
};
store = sap.OData.createOfflineStore(properties);
//var options = {};
store.open(openStoreSuccessCallback, errorCallback/*, options*/);
}
<button id="read2" onclick="readProducts2()">Read Online</button><br>
sUrl = sUrl.replace("gs2", "gs3"); //make the read request against the backend rather than the offline store.
cordova run android
or
cordova run windows --archs=x64
or
cordova run ios
/Products?$filter=indexof(Name, 'wrench') ge 0
Provided value for the property 'Price' is not compatible with the property
Name=com.mycompany.offline5
request_format=atom
case_sensitive_offline_db=no
http://services.odata.org/V2/OData/OData.svc/Products?$format=json&$filter=tolower(Name) eq 'bread'
vs
http://services.odata.org/V2/OData/OData.svc/Products?$format=json&$filter=Name eq 'bread'
The conversion from OData metadata to database metadata has failed
Property "GenderCode" cannot be supported because it is a key or referential constraint property and its MaxLength facet is missing or has a value that is too large
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
11 | |
10 | |
10 | |
9 | |
7 | |
7 | |
7 | |
6 | |
6 | |
6 |