I am struggeling to find a solution for my Cordova Native App.
I am using OneSignal to send out push notifications once a new Article has been published. The push notifications are working fine but the moment I click onto the notification the normal browser opens and not the app.
NB: When I send a test notification through OneSignal and click on it the App opens.
My code just does not want to work with Wordpress articles.
Here's the code which I am using in my index.js
document.addEventListener('deviceready', function () {
// Enable to debug issues.
// window.plugins.OneSignal.setLogLevel({logLevel: 4, visualLevel: 4});
var notificationOpenedCallback = function(jsonData) {
var additionalData = jsonData.notification.payload.additionalData
if (additionalData && additionalData.myKey)
// Not familiar with Cordova, $state may or may not be available here
$state.go('app.post', {'postId': + additionalData.myKey});
};
window.plugins.OneSignal
.startInit("0b8b7e69-1649-4616-XXXXXXX")
.handleNotificationOpened(notificationOpenedCallback)
.endInit();
// Call syncHashedEmail anywhere in your app if you have the user's email.
// This improves the effectiveness of OneSignal's "best-time" notification scheduling feature.
// window.plugins.OneSignal.syncHashedEmail(userEmail);
}, false);
Has anyone any idea what is possibly wrong with my code?
All you need to put all codes under plugin check on device ready function and make sure you parse the data into array object because they sending raw looks like -
if (window.plugins && window.plugins.OneSignal) {
.startInit(your_appid, your_google_project)
.inFocusDisplaying(window.plugins.OneSignal.OSInFocusDisplayOption.Notification)
.handleNotificationOpened(function(jsonData) {
if (device.platform =="Android") {
var addData = JSON.parse(jsonData.result.notification.payload.additionalData);
} else {
var addData = jsonData.result.notification.payload.additionalData;
}
if (addData != undefined) {
if (addData.posts != undefined) {
var postID = Number(addData.posts);
$state.go('app.post', {postId: postID });
}
}
})
.endInit();
}
Note: According to there doc they update the additional data tree to
result.notification.payload.additionalData
Not notification.payload.additionalData
Put this function on your wordpress theme function.php and make sure you have disable the option "Send notifications additionally to iOS & Android platforms". on WordPress plugin page.
add_filter('onesignal_send_notification', 'onesignal_send_notification_filter', 10, 4);
function onesignal_send_notification_filter($fields, $new_status, $old_status, $post)
{
$postID = $post->id;
$fields['isAndroid'] = true;
$fields['isIos'] = true;
$fields['isAnyWeb'] = false;
$fields['isChrome'] = false;
$fields['data'] = array(
"posts" => $postID
);
return $fields;
}
Just leaving this here, in case someone else can make use of it.
add_filter('onesignal_send_notification', 'onesignal_send_notication_filter_push', 10, 4);
function onesignal_send_notication_filter_push($fields, $new_status, $old_status, $post)
{
$fields['isAndroid'] = true;
$fields['isIos'] = true;
$fields['isAnyWeb'] = false;
$fields['isChrome'] = false;
// overwrite http url with app_url so push notifications opens app instead of phone browser.
$fields['url'] = $fields['app_url'];
return $fields;
}
Related
We have a web application in PHP, for our clients we have prepared connect to google analytics UA. I use "google/apiclient": "^2.0", it works that our clients click on button in our administration and then is runned a followed code:
$this->client = new Google_Client();
$this->client->setApplicationName("xxxx");
$this->client->setClientId("xxxx");
$this->client->setClientSecret("xxxx");
$this->client->setScopes(array("https://www.googleapis.com/auth/analytics.readonly"));
$this->client->setRedirectUri("xxxx");
$this->client->setAccessType('offline');
$this->client->setApprovalPrompt("force");
The credentials i get from https://console.cloud.google.com/ -> OAuth 2.0 Client IDs
then the client is redirected to google where he log in, and allow acces to his GA data for our app. then is redirected back with code is generated access token. With this token i can get his GA UA data and show it to graphs in our administration. It works allright, but now i get information that GA UA will be end, and i need to create the same proces for UA V4. But in documentation to GA V4 what i found: https://developers.google.com/analytics/devguides/reporting/data/v1
Is not information how to process it for our clients. There is only authorisation over service account, that i must donwload my own credentials.json to service account but it allow me only acces to my private account, but i need it to work the same as before, so for other clients without having to upload credentials.json. That is, to be redirected to google via OAuth 2.0 Client IDs and allow access to our application to read their data. Is it even possible?
Thank you for help, and sorry for my bad english
This should give you a start. I am combining the OAuth2 methods from the Google API php client library and applying them to the new library.
Its not optimal but it works. Code below is for installed application not web. Its not going to work hosted.
<?php
require 'vendor/autoload.php';
use Google\Client;
use Google\Analytics\Data\V1beta\BetaAnalyticsDataClient;
putenv('GOOGLE_APPLICATION_CREDENTIALS=C:\YouTube\dev\credentials.json'); // Installed / native / desktop Client credetinals.
$credentials = getenv('GOOGLE_APPLICATION_CREDENTIALS');
$myfile = file_get_contents($credentials, "r") ;
$clientObj = json_decode($myfile);
$client = getClient();
$tokenResponse = $client->getAccessToken();
print_r($tokenResponse);
print_r($tokenResponse["access_token"]);
$service = new BetaAnalyticsDataClient( [
'credentials' => Google\ApiCore\CredentialsWrapper::build( [
'scopes' => [
'https://www.googleapis.com/auth/analytics',
'openid',
'https://www.googleapis.com/auth/analytics.readonly',
],
'keyFile' => [
'type' => 'authorized_user',
'client_id' => $clientObj->installed->client_id,
'client_secret' => $clientObj->installed->client_secret,
'refresh_token' => $tokenResponse["refresh_token"]
],
] ),
] );
$response = $service->runReport([
'property' => 'properties/[YOUR_PROPERTY_ID]'
]);
foreach ($response->getRows() as $row) {
foreach ($row->getDimensionValues() as $dimensionValue) {
print 'Dimension Value: ' . $dimensionValue->getValue() . PHP_EOL;
}
}
function getClient()
{
$client = new Client();
$client->setApplicationName('Google analytics data beta Oauth2');
$client->setScopes('https://www.googleapis.com/auth/analytics');
$client->setAuthConfig(getenv('GOOGLE_APPLICATION_CREDENTIALS'));
$client->setAccessType('offline');
// Load previously authorized token from a file, if it exists.
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
$tokenPath = 'token.json';
if (file_exists($tokenPath)) {
$accessToken = json_decode(file_get_contents($tokenPath), true);
$client->setAccessToken($accessToken);
}
// If there is no previous token or it's expired.
if ($client->isAccessTokenExpired()) {
// Refresh the token if possible, else fetch a new one.
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
} else {
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
printf("Open the following link in your browser:\n%s\n", $authUrl);
print 'Enter verification code: ';
$authCode = trim(fgets(STDIN));
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
// Check to see if there was an error.
if (array_key_exists('error', $accessToken)) {
throw new Exception(join(', ', $accessToken));
}
}
// Save the token to a file.
if (!file_exists(dirname($tokenPath))) {
mkdir(dirname($tokenPath), 0700, true);
}
file_put_contents($tokenPath, json_encode($client->getAccessToken()));
}
return $client;
}
And please how to get list streams of listed properties, I have a code to get GA4 properties:
$this->SetGa4ClientAdmin();
$accounts = $this->ga4_admin->listAccounts();
$this->data['accounts_v4'] = array();
foreach ($accounts as $account) {
$this->data['accounts_v4'][$account->getName()] = array('name' => $account->getDisplayName(), 'childrens' => array());
try {
$properties = $this->ga4_admin->ListProperties('parent:' . $account->getName());
foreach ($properties AS $property) {
$this->data['accounts_v4'][$account->getName()]['childrens'][$property->getName()] = $property->getDisplayName();
}
} catch (Exception $ex) {
die("error: " . $ex->getMessage());
}
}
At every property I need to get measurement ID of GA4 streams.
I need to get this
we are using wordpress JSON API to signon a user and to add / update / remove cart-items. We are doing this with the register_rest_route function.
We use this code to remove a cart item:
function remove_from_cart(WP_REST_Request $req)
{
$resp = null;
$cart_item = $req['cart_item'];
try {
WC()->cart->remove_cart_item($cart_item);
} catch (Exception $e) {
$resp = $e;
}
return rest_ensure_response(new CartResponse());
}
This is working perfectly fine for guests. but as soon as a logged in user tries it, the cart is back to its normal state after a page reload. The response created by new CartResponse() is correctly showing the cart without the removed item. however, after a page reload the item is still there.
As this only happens for logged in users and not for guests I think it is a session issue.
Also, updating the cart with the following method works for logged in users:
function update_cart_item(WP_REST_Request $req)
{
$resp = null;
$cart_item = $req['cart_item'];
try {
if ($cart_item && $cart_item['quantity']) {
WC()->cart->set_quantity($cart_item['key'], $cart_item['quantity']);
}
} catch (Exception $e) {
$resp = $e;
}
return rest_ensure_response(new CartResponse());
}
Unfortunately, setting the quantity to 0 is also not working.
This is how we signon users:
function login_customer(WP_REST_Request $req)
{
$body = $req->get_body();
$input = json_decode($body, TRUE);
$credentials = ['user_login' => $input['email'], 'user_password' => $input['password']];
$user = wp_signon($credentials, false);
if (is_a($user, 'WP_Error') || !$user) {
// if an error occurs, return null
return rest_ensure_response(null);
}
$resp = new CustomerResponse($user->ID);
return rest_ensure_response($resp);
}
And we are not using any caching plugins. What is wrong here?
Here is a list of all session cookies:
EDIT:
I just inspected the cookies while beeing logged in and removing a cart item.
Cart Hash before deleting: bb35785a228a17ceb85f8ed2dc522b16
Cart Hash directly after deleting: d32e22e278d42022e04b6992b7d65816
Cart Hash after page reload: bb35785a228a17ceb85f8ed2dc522b16 again
So it seems like the cart hash is stored somewhere and restored on a reload, but not correctly updated on deleting a cart item
It seems like you need nonces to authenticate DELETE requests.
Now I am adding nonces to each response in a header:
function add_cors_http_header(){
header("X-WP-Nonce: ".wp_create_nonce('wp_rest'));
}
add_action('init','add_cors_http_header');
And in the frontend I set it:
let nonce: string = null;
export const fetchNoAuth = (endpoint: string, method: string = 'GET', data: any = null): Promise<any> => {
let headers: any = {'Content-Type': 'application/json'};
if (nonce) {
headers['X-WP-Nonce'] = nonce;
}
return fetch('http://' + apiUrl + apiPath + endpoint + '?' + debugQuery, {
method,
credentials: 'include',
headers,
body: data ? JSON.stringify(data) : null
})
.then((data) => {
const nonceFromResponse = data.headers.get('X-WP-Nonce');
if (nonceFromResponse) {
nonce = nonceFromResponse;
} else {
nonce = null;
}
return data;
})
};
Make sure that the header in the request is named X-WP-Nonce
I would like to turn off the automatic app refresh in meteor that occurs every time I change a file. How do I do this?
You can start your app with the --once flag, like so: meteor --once.
You can disable HCP (hot code push) by adding this anywhere in your client code:
Meteor._reload.onMigrate(function() {
return [false];
});
After doing that, you'll need to manually refresh the page in order to see any new changes.
Building on those David's response, here's how I've been doing it to let components stop hot code push while they are alive:
let shouldReloadPage = false;
const componentsBlockingHCP = [];
Meteor._reload.onMigrate(function() {
if (componentsBlockingHCP.length) {
shouldReloadPage = true;
return [false];
}
shouldReloadPage = false;
return [true];
});
/*
* Prevent hot push
*/
export const delayHCP = (component) => {
if (componentsBlockingHCP.indexOf(component) < 0)
componentsBlockingHCP.push(component);
};
/*
* Enable, and reload if hot pushed has been requested when it was not available
*/
export const stopHCPDelay = (component) => {
const idx = componentsBlockingHCP.indexOf(component);
if (idx !== -1)
componentsBlockingHCP.splice(idx, 1);
if (shouldReloadPage && !componentsBlockingHCP.length) {
location.reload();
}
};
And then, from a component (with React syntax):
componentDidMount() {
delayHCP(this);
}
componentWillUnmount() {
stopHCPDelay(this);
}
There is a small trick for that. Put # in the end of the url of the page you are working and press Enter, then continue to work on your code. Once you save the file, page will not be refreshed till you refresh it manually (F5 or cmd + R) This way will prevent the page to refresh, but the new code is still pushed to the client and you don't need to disable the HCP for the whole site. Disadvantage: you don't know when the new code is pushed to the client
As the title suggests I would like to provide functionality to allow a user to update the email they use to login to my app using Firebase Simple Login. Cannot figure out an elegant way to do this. App uses AngularFire if that is relevant.
Does one exist or do I need to create a new account and delete the old one using the $removeUser() and $createUser() methods?
Update for Firebase 2.1.x
The Firebase SDK now provides a changeEmail method.
var ref = new Firebase('https://<instance>.firebaseio.com');
ref.changeEmail({
oldEmail: 'kato#domain.com',
newEmail: 'kato2#kato.com' ,
password: '******'
}, function(err) {
console.log(err ? 'failed to change email: ' + err : 'changed email successfully!');
});
Historical answer for Firebase 1.x
In Simple Login, this is equivalent to changing the user's ID. So there is no way to do this on the fly. Simply create the new account, remove the old one as you have already suggested.
If you're user profiles in Firebase, you'll want to move those as well. Here's brute force, safe method to migrate an account, including user profiles. You could, naturally, improve upon this with some objectification and futures:
var ref = new Firebase('URL/user_profiles');
var auth = new FirebaseSimpleLogin(ref);
// assume user has logged in, or we obtained their old UID by
// looking up email address in our profiles
var oldUid = 'simplelogin:123';
moveUser( auth, ref, oldUid, '123#abc.com', '456#def.com', 'xxx' );
function moveUser( auth, usersRef, oldUid, oldId, newId, pass ) {
// execute activities in order; first we copy old paths to new
createLogin(function(user) {
copyProfile(user.uid, function() {
// and once they safely exist, then we can delete the old ones
removeOldProfile();
removeOldLogin();
});
});
function copyProfile(newId, next) {
ref.child(oldUid).once('value', function(snap) {
if( snap.val() !== null ) {
ref.child(newId, snap.val(), function(err) {
logError(err);
if( !err ) { next(); }
});
}
});
}
function removeOldProfile() {
ref.child(oldId).remove(logError);
}
function createLogin(next) {
auth.createUser(newId, pass, function(err, user) {
logError(err);
if( !err ) { next(user); }
});
}
function removeOldLogin() {
auth.removeUser(oldId, pass, logError);
}
}
function logError(err) {
if( err ) { console.error(err); }
}
I'm trying to make a simple app with Phonegap, compiled with Adobe Phonegap builder. I've found and used the well documented example for using navigator.connection.type which is below, and to which I've added another line, to generate an alert box when the device is ready. It doesn't even get that far. I've had it at some points show that endless spinning circle, by moving this code from the head to the body of the page, but that is no help in the end. Testing on iOs and Android devices gives the same result, and the config.xml does include:-
<plugin name="NetworkStatus" value="CDVConnection" />
<plugin name="NetworkStatus" value="org.apache.cordova.NetworkManager" />
Any help greatly appreciated.
// Wait for Cordova to load
//
document.addEventListener("deviceready", onDeviceReady, false);
// Cordova is loaded and it is now safe to make calls Cordova methods
//
function onDeviceReady() {
alert('Device is ready');
checkConnection();
}
function checkConnection() {
var networkState = navigator.connection.type;
var states = {};
states[Connection.UNKNOWN] = 'Unknown connection';
states[Connection.ETHERNET] = 'Ethernet connection';
states[Connection.WIFI] = 'WiFi connection';
states[Connection.CELL_2G] = 'Cell 2G connection';
states[Connection.CELL_3G] = 'Cell 3G connection';
states[Connection.CELL_4G] = 'Cell 4G connection';
states[Connection.CELL] = 'Cell generic connection';
states[Connection.NONE] = 'No network connection';
alert('Connection type: ' + states[networkState]);
}
</script>
You should also wait until all your scripts are loaded. Wrap everything in a onBodyLoad like so:
function onBodyLoad() {
// these are useful later in the app, might as well set early
window.isRipple = (window.tinyHippos != null);
window.isPhoneGap = /^file:\/{3}[^\/]/i.test(window.location.href);
window.isIOS = !window.isRipple && navigator.userAgent.match(/(ios|iphone|ipod|ipad)/gi) != null;
window.isAndroid = !window.isRipple && navigator.userAgent.match(/(android)/gi) != null;
// stuff I use for debugging in chrome
if (window.isPhoneGap) {
document.addEventListener("deviceready", onDeviceReady, false);
} else {
onDeviceReady();
}
}
And add to your body tag:
<body onload="onBodyLoad()">
And the rest of my code for additional references:
function checkOffLine(minutes) {
if (window.lastCheckTime == null) {
window.lastCheckTime = 0;
}
var currentTime = (new Date()).getTime();
if (currentTime < (window.lastCheckTime + minutes * 60000)) return;
window.lastCheckTime = currentTime;
// ios does not allow you to exit the application so just warn
// ios also require you to warn or your app get rejected
if (window.isIOS) {
navigator.notification.alert('This application may not function properly without an internet connection.');
} else {
navigator.notification.confirm(
'This application may not function properly without an internet connection. Continue working offline?', // message
function(button) // callback to invoke with index of button pressed
{
if (button == 1) {
navigator.app.exitApp();
}
},
'Warning', // title
'Exit,Continue' // buttonLabels
);
}
}
function checkConnection() {
// your check connection type code here or just use navigator.onLine
if (!navigator.onLine) {
// don't be annoying, only confirm for once every every 5 minutes
checkOffLine(5);
}
}
// initial phonegap deviceready handler
function onDeviceReady() {
console.log('Application started');
angular.bootstrap(document, ['assetsApp']);
if (window.isPhoneGap) {
document.addEventListener("offline", checkConnection, false);
checkConnection();
}
};
function onBodyLoad() {
// these are useful later in the app, might as well set early
window.isRipple = (window.tinyHippos != null);
window.isPhoneGap = /^file:\/{3}[^\/]/i.test(window.location.href);
window.isIOS = !window.isRipple && navigator.userAgent.match(/(ios|iphone|ipod|ipad)/gi) != null;
window.isAndroid = !window.isRipple && navigator.userAgent.match(/(android)/gi) != null;
// stuff I use for debugging in chrome
if (window.isPhoneGap) {
document.addEventListener("deviceready", onDeviceReady, false);
} else {
onDeviceReady();
}
}
I had the same issue and found I had to run "cordova build" and then the status was returned correctly.
BEWARE
When I run cordova build, it appears to take everything in my ~/app/www directory and overried everything in app/platforms/android/assets/www/
My "install process" is as follows:
cordova create app com.app "App"
cd app
cordova platform add android
cordova plugin add org.apache.cordova.network-information
cordova plugin add org.apache.cordova.camera
cordova plugin add org.apache.cordova.geolocation
cordova build
I can then do code changes in app/www and when happy, 'deploy' it using 'cordova build' (which seems to always copy the files to app/platforms/android/assets/www/.
If I add another plugin using: (for example)
cordova plugin add org.apache.cordova.file
then I need to run
cordova build
to have it work.
I hope this helps
(I am using cordova 3.3.1-0.1.2 )