github.com/couchbaselabs/ToDoLite-Android
Q 1:
https://github.com/couchbaselabs/ToDoLite-Android/blob/master/ToDoLite/src/main/java/com/couchbase/todolite/document/Profile.java
profile.java have code :
Document document = database.getDocument("p:" + userId);
But when I see _admin
xxx.ooo. com : 4985/_admin/db/
documents have records, but can not see p:xxxooo document. Debug trace can see document id: p:xxxxooo get. But document file can not find.
why? or how to see?
http://postimg.org/image/v267oc29x/
github.com/couchbaselabs/ToDoLite-Android/blob/master/sync-gateway-config.json
Q2:
use facebook login, login success.
http:/xxx.ooo.com:4985/_admin/db/...../users
I can see users be added for facebook logined.
But I try to debug or trace can not find code to add user.
Anyone know where this code or how to do?
Thank you ~~
A2:
Beacuse this java POST something to _facebook. Then sync_gateway have setting about
"facebook" : {
"register" : true
}
So sync_gateway maybe write some code add user to users.
See this java
D:\Android\project\ToDoLite-Android\libraries\couchbase-lite-java-core\src\main\java\com\couchbase\lite\replicator\ReplicationInternal.java
Find this
Future future = sendAsyncRequest("POST", loginPath, loginParameters, new RemoteRequestCompletionBlock() {
This is my debug values
loginPath /_facebook
loginParameters "access_token" ->
"CAAHQmreG........"
url http://ooo.xxxx.com:4984/todos/_facebook
http://developer.couchbase.com/mobile/develop/guides/sync-gateway/administering-sync-gateway/authenticating-users/index.html
So this is why auto add user to users.
You can delete user
DELETE oooxxx.com:4985/todos/_user/ooo#xxxxx.com
Then set deubg point at
Future future = sendAsyncRequest("POST", loginPath, loginParameters, new RemoteRequestCompletionBlock() {
then check
http://xxxx.oooo.com:4985/_admin/db/todos/users
relogin agoin, debug stop that java, check users no any. then F8 . user be add to users. this is for my debug track.
==========
D:\Android\project\ToDoLite-Android\libraries\couchbase-lite-java-core\src\main\java\com\couchbase\lite\replicator\Replication.java
protected ReplicationInternal replicationInternal;
D:\Android\project\ToDoLite-Android\libraries\couchbase-lite-java-core\src\main\java\com\couchbase\lite\replicator\ReplicationInternal.java
ReplicationInternal() => initializeStateMachine()
ReplicationInternal.this.start();
start() => goOnlineInitialStartup() => checkSession() => checkSessionAtPath("/_session") => login()
Future future = sendAsyncRequest("POST", loginPath, loginParameters, new RemoteRequestCompletionBlock() {
Related
I want to automagically update the UI with a new user setting after the user updates that setting by submitting a user input from.
Currently, I am attempting to use the updateCard() method from the CardService as shown in the below code. The docs are here but they do not contain any example code.
I expect that after the user provides the input and submits it, the current card will be replaced by an updated card that will contain the new setting.
However, what’s actually happening is that the card I expect to update is not updating automatically. To see the change, the user has to manually refresh the app homepage. After, and only after, a manual refresh, does the homepage card update with the new setting.
How do I update the homepage automatically without requiring a manual refresh?
Code.gs
const changedProperty = PropertiesService.getUserProperties().getProperty( MY_SETTING );
const newNavigation = CardService.newNavigation();
const cardPoppedToRoot = newNavigation.popToRoot();
const homepageCard = getCardFromUiConfig( HOMEPAGE_UI_CONFIG, );
const updatedCard = cardPoppedToRoot.updateCard( homepageCard, );
return updatedCard;
I also tried the following code per this answer and the results are exactly the same as with the above code.
Code.gs
return CardService.newActionResponseBuilder()
.setNavigation(
CardService.newNavigation()
.popToRoot()
.updateCard( homepageCard, )
).build();
When I try to configure my appsscript.json file as shown in the answer as follows:
appsscript.json
"homepageTrigger": {
"runFunction": "onHomepage"
},
"contextualTriggers":[
{
"unconditional":{},
"onTriggerFunction": "onHomepage"
}
]
I get the following error:
"appsscript.json" has errors: Invalid manifest: unknown fields: [addOns.common.contextualTriggers]
I think that that is only possible for Gmail add-ons.
contextualTriggers can't be child of common.
From https://developers.google.com/apps-script/manifest/addons#common (links not included):
Common
The manifest configuration for parameters that are common for every host application. Some values defined here are used as a default when specific values for a particular host are omitted.
{
"homepageTrigger": {
object (HomepageTrigger)
},
"layoutProperties": {
object (LayoutProperties)
},
"logoUrl": string,
"name": string,
"openLinkUrlPrefixes": [
string
],
"universalActions": [
{
object (UniversalAction)
}
],
"useLocaleFromApp": boolean
}
AFAIK contextualTriggers can only be used with Gmail add-ons. From https://developers.google.com/apps-script/manifest/gmail-addons (links not included):
Gmail
The Google Workspace add-on manifest configuration for Gmail extensions. See Extending Gmail with Google Workspace add-ons for more information.
{
"authorizationCheckFunction": string,
"composeTrigger": {
object (ComposeTrigger)
},
"contextualTriggers": [
{
object (ContextualTrigger)
}
],
"homepageTrigger": {
object (HomepageTrigger)
}
}
Related
How to fully refresh Google Addon Card (Google Sheets) which includes a drop down menu populated using sheet data when data changes?
I have a question regarding a small issue that I'm having. I've created a widget that will live on the Service Portal to allow an admin to Accept or Reject requests.
The data for the widget is pulling from the Approvals (approval_approver) table. Under my GlideRecord, I have a query that checks for the state as requested. (Ex. addQuery('state', 'requested'))
To narrow down the search, I tried entering addQuery('sys_id', current.sys_id). When I use this query, my script breaks and I get an error on the Service Portal end.
Here's a sample of the GlideRecord script I've written to Accept.
[//Accept Request
if(input && input.action=="acceptApproval") {
var inRec1 = new GlideRecord('sysapproval_approver');
inRec1.addQuery('state', 'requested');
//inRec1.get('sys_id', current.sys_id);
inRec1.query();
if(inRec1.next()) {
inRec1.setValue('state', 'Approved');
inRec1.setValue('approver', gs.getUserID());
gs.addInfoMessage("Accept Approval Processed");
inRec1.update();
}
}][1]
I've research the web, tried using $sp.getParameter() as a work-around and no change.
I would really appreciate any help or insight on what I can do different to get script to work and filter the right records.
If I understand your question correctly, you are asking how to get the sysId of the sysapproval_approver record from the client-side in a widget.
Unless you have defined current elsewhere in your server script, current is undefined. Secondly, $sp.getParameter() is used to retrieve URL parameters. So unless you've included the sysId as a URL parameter, that will not get you what you are looking for.
One pattern that I've used is to pass an object to the client after the initial query that gets the list of requests.
When you're ready to send input to the server from the client, you can add relevant information to the input object. See the simplified example below. For the sake of brevity, the code below does not include error handling.
// Client-side function
approveRequest = function(sysId) {
$scope.server.get({
action: "requestApproval",
sysId: sysId
})
.then(function(response) {
console.log("Request approved");
});
};
// Server-side
var requestGr = new GlideRecord();
requestGr.addQuery("SOME_QUERY");
requestGr.query(); // Retrieve initial list of requests to display in the template
data.requests = []; // Add array of requests to data object to be passed to the client via the controller
while(requestsGr.next()) {
data.requests.push({
"number": requestsGr.getValue("number");
"state" : requestsGr.getValue("state");
"sysId" : requestsGr.getValue("sys_id");
});
}
if(input && input.action=="acceptApproval") {
var sysapprovalGr = new GlideRecord('sysapproval_approver');
if(sysapprovalGr.get(input.sysId)) {
sysapprovalGr.setValue('state', 'Approved');
sysapprovalGr.setValue('approver', gs.getUserID());
sysapprovalGr.update();
gs.addInfoMessage("Accept Approval Processed");
}
...
I am trying to write an activity in Google+ using the dotnet-client. The issue is that I can't seem to get the configuration of my client app correctly. According to the Google+ Sign-In configuration and this SO question we need to add the requestvisibleactions parameter. I did that but it did not work. I am using the scope https://www.googleapis.com/auth/plus.login and I even added the scope https://www.googleapis.com/auth/plus.moments.write but the insert still did not work.
This is what my request url looks like:
https://accounts.google.com/ServiceLogin?service=lso&passive=1209600&continue=https://accounts.google.com/o/oauth2/auth?scope%3Dhttps://www.googleapis.com/auth/plus.login%2Bhttps://www.googleapis.com/auth/plus.moments.write%26response_type%3Dcode%26redirect_uri%3Dhttp://localhost/%26state%3D%26requestvisibleactions%3Dhttp://schemas.google.com/AddActivity%26client_id%3D000.apps.googleusercontent.com%26request_visible_actions%3Dhttp://schemas.google.com/AddActivity%26hl%3Den%26from_login%3D1%26as%3D-1fbe06f1c6120f4d<mpl=popup&shdf=Cm4LEhF0aGlyZFBhcnR5TG9nb1VybBoADAsSFXRoaXJkUGFydHlEaXNwbGF5TmFtZRoHQ2hpa3V0bwwLEgZkb21haW4aB0NoaWt1dG8MCxIVdGhpcmRQYXJ0eURpc3BsYXlUeXBlGgdERUZBVUxUDBIDbHNvIhTeWybcoJ9pXSeN2t-k8A4SUbfhsygBMhQivAmfNSs_LkjXXZ7bPxilXgjMsQ&scc=1
As you can see from there that there is a request_visible_actions and I even added one that has no underscore in case I got the parameter wrong (requestvisibleactions).
Let me say that my app is being authenticated successfully by the API. I can get the user's profile after being authenticated and it is on the "insert moment" part that my app fails. My insert code:
var body = new Moment();
var target = new ItemScope();
target.Id = referenceId;
target.Image = image;
target.Type = "http://schemas.google.com/AddActivity";
target.Description = description;
target.Name = caption;
body.Target = target;
body.Type = "http://schemas.google.com/AddActivity";
var insert =
new MomentsResource.InsertRequest(
// this is a valid service instance as I am using this to query the user's profile
_plusService,
body,
id,
MomentsResource.Collection.Vault);
Moment result = null;
try
{
result = insert.Fetch();
}
catch (ThreadAbortException)
{
// User was not yet authenticated and is being forwarded to the authorization page.
throw;
}
catch (Google.GoogleApiRequestException requestEx)
{
// here I get a 401 Unauthorized error
}
catch (Exception ex)
{
} `
For the OAuth flow, there are two issues with your request:
request_visible_actions is what is passed to the OAuth v2 server (don't pass requestvisibleactions)
plus.moments.write is a deprecated scope, you only need to pass in plus.login
Make sure your project references the latest version of the Google+ .NET client library from here:
https://developers.google.com/resources/api-libraries/download/stable/plus/v1/csharp
I have created a project on GitHub showing a full server-side flow here:
https://github.com/gguuss/gplus_csharp_ssflow
As Brettj said, you should be using the Google+ Sign-in Button as demonstrated in the latest Google+ samples from here:
https://github.com/googleplus/gplus-quickstart-csharp
First, ensure you are requesting all of the activity types you're writing. You will know this is working because the authorization dialog will show "Make your app activity available via Google, visible to you and: [...]" below the text that starts with "This app would like to". I know you checked this but I'm 90% sure this is why you are getting the 401 error code. The following markup shows how to render the Google+ Sign-In button requesting access to Add activities.
<div id="gConnect">
<button class="g-signin"
data-scope="https://www.googleapis.com/auth/plus.login"
data-requestvisibleactions="http://schemas.google.com/AddActivity"
data-clientId="YOUR_CLIENT_ID"
data-accesstype="offline"
data-callback="onSignInCallback"
data-theme="dark"
data-cookiepolicy="single_host_origin">
</button>
Assuming you have a PlusService object with the correct activity type set in data-requestvisibleactions, the following code, which you should be able to copy/paste to see it work, concisely demonstrates writing moments using the .NET client and has been tested to work:
Moment body = new Moment();
ItemScope target = new ItemScope();
target.Id = "replacewithuniqueforaddtarget";
target.Image = "http://www.google.com/s2/static/images/GoogleyEyes.png";
target.Type = "";
target.Description = "The description for the activity";
target.Name = "An example of add activity";
body.Target = target;
body.Type = "http://schemas.google.com/AddActivity";
MomentsResource.InsertRequest insert =
new MomentsResource.InsertRequest(
_plusService,
body,
"me",
MomentsResource.Collection.Vault);
Moment wrote = insert.Fetch();
Note, I'm including Google.Apis.Plus.v1.Data for convenience.
Ah it's that simple! Maybe not? I am answering my own question and consequently accept it as the answer (after a few days of course) so others having the same issue may be guided. But I will definitely up-vote Gus' answer for it led me to the fix for my code.
So according to #class answer written above and as explained on his blog the key to successfully creating a moment is adding the request_visible_actions parameter. I did that but my request still failed and it is because I was missing an important thing. You need to add one more parameter and that is the access_type and it should be set to offline. The OAuth request, at a minimum, should look like: https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/plus.login&response_type=code&redirect_uri=http://localhost/&request_visible_actions=http://schemas.google.com/AddActivity&access_type=offline.
For the complete and correct client code you can get Gus' example here or download the entire dotnet client library including the source and sample and add what I added below. The most important thing that you should remember is modifying your AuthorizationServerDescription for the Google API. Here's my version of the authenticator:
public static OAuth2Authenticator<WebServerClient> CreateAuthenticator(
string clientId, string clientSecret)
{
if (string.IsNullOrWhiteSpace(clientId))
throw new ArgumentException("clientId cannot be empty");
if (string.IsNullOrWhiteSpace(clientSecret))
throw new ArgumentException("clientSecret cannot be empty");
var description = GoogleAuthenticationServer.Description;
var uri = description.AuthorizationEndpoint.AbsoluteUri;
// This is the one that has been documented on Gus' blog site
// and over at Google's (https://developers.google.com/+/web/signin/)
// This is not in the dotnetclient sample by the way
// and you need to understand how OAuth and DNOA works.
// I had this already, see my original post,
// I thought it will make my day.
if (uri.IndexOf("request_visible_actions") < 1)
{
var param = (uri.IndexOf('?') > 0) ? "&" : "?";
description.AuthorizationEndpoint = new Uri(
uri + param +
"request_visible_actions=http://schemas.google.com/AddActivity");
}
// This is what I have been missing!
// They forgot to tell us about this or did I just miss this somewhere?
uri = description.AuthorizationEndpoint.AbsoluteUri;
if (uri.IndexOf("offline") < 1)
{
var param = (uri.IndexOf('?') > 0) ? "&" : "?";
description.AuthorizationEndpoint =
new Uri(uri + param + "access_type=offline");
}
// Register the authenticator.
var provider = new WebServerClient(description)
{
ClientIdentifier = clientId,
ClientSecret = clientSecret,
};
var authenticator =
new OAuth2Authenticator<WebServerClient>(provider, GetAuthorization)
{ NoCaching = true };
return authenticator;
}
Without the access_type=offline my code never worked and it will never work. Now I wonder why? It would be good to have some explanation.
I have a RavenDB Service instance set up on localhost:8080 and am using the OAuth plugin to store a simple user document for authentication. I am using a guid for the id and the users email as the name. The following code is functioning properly to store the user
public AccountUserDocument CreateUser(RegisterModel model)
{
using (IDocumentSession session = DataDocumentStore.Instance.OpenSession())
{
Guid userId = Guid.NewGuid();
session.Store(new AccountUserDocument
{
Name = model.Email,
Id = String.Format("Raven/Users/{0}", userId),
AllowedDatabases = new[] { "*" },
Email = model.Email,
FirstName = model.FirstName,
LastName = model.LastName,
FacebookId = 0,
Expires = DateTime.Now.AddMonths(1),
AccessToken = string.Empty
}.SetPassword(model.Password));
session.SaveChanges();
return session.Load<AccountUserDocument>
(String.Format("Raven/Users/{0}", userId));
}
}
and returns a valid user object. However, when i call
return session.Query<AccountUserDocument>()
.Customize(x => x.WaitForNonStaleResults())
.Where(x => x.Name == email)
.SingleOrDefault();
I get nothing. It had been working a week ago but now it just doesn't. If I open up RavenDB studio, I can see the user and the name is exactly how I am entering it (i have even copy and pasted it into the text field).
I have tried stopping and restarting the service hoping that would solve the problem but it did not.
I was wondering if someone could point me in the direction of how I might debug what is going on here. The full code repository can be found here
https://github.com/jamesamuir/MVC_Facebook_Auth
if anyone is inclined to download it.
Thanks in advance.
Well, I see a lot of things wrong with the code sample you provided - such as mismatched versions of client and server. Also, the Authentication Bundle was a 1.0 feature that has been deprecated in 2.0 which is almost final, so you're writing code you will eventually have to replace.
But to answer your question, the specific reason that you can't get results from your query is that you chose to use a document key starting with "Raven/", which is the convention for RavenDB system documents - which don't get indexed.
If you remove "Raven/" from your id, it will work.
Also, you should not be using .WaitForNonStaleResults() - that's only for unit tests, and is dangerous in production. If you really feel like you need to wait, use .WaitForNonStaleResultsAsOfNow() - which is safer because it provides a cutoff.
I am writing a Windows App on Windows Phone Emulator to communicate with DropBox account and am using DropNet package from NuGet gallery.
The page that I am referring is: http://dkdevelopment.net/what-im-doing/dropnet/
Here are the steps I have done:
Step 1) Creating the client
DropNetClient GlobalClient = new DropNetClient("TOKEN", "SECRET", "testUserName", "testPassword");
I am not sure what goes in userToken and userSecret, it can't be hard-coded username and password!
Step 2) Requesting Token
GlobalClient.GetTokenAsync((userToken) =>
{
//Dont really need to do anything with userLogin,
//DropNet takes care of it for now
},
(error) =>
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(error.Message);
});
});
Step 3) Building authorizing URL
var url = GlobalClient.BuildAuthorizeUrl("http://dkdevelopment.net/BoxShotLogin.htm");
Step 4) Redirecting browser to DropBox Login Page. After this, DropBox does allow to login, but it displays this warning: "The request to link the app is invalid" And more importantly, the browser does not get redirected to http://dkdevelopment.net/BoxShotLogin.htm. This indicates something went wrong. Not sure what though.
Uri testUri = new Uri(url.ToString());
WebBrowserTask task = new WebBrowserTask();
task.Uri = testUri;
task.Show();
Step 5) This does not work. GlobalClient.UserLogin.Token and GlobalClient.UserLogin.Secret does not get set.
GlobalClient.GetAccessTokenAsync((accessToken) =>
{
//Store this token for "remember me" function
GlobalClient.UserLogin.Token = accessToken.Token;
GlobalClient.UserLogin.Secret = accessToken.Secret;
},
(error) =>
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(error.Message);
});
});
Anybody knows how to fix this?
Thanks!
I figured out the problem. In Step 2, request token needs to be stored in order to build correct authorize URL and that request token needs to be passed as the first parameter. However when I try to store in the statement lambda like this, it does not get stored. What's the issue here? I think it's the way I might be using lambdas.
GlobalClient.GetTokenAsync((userToken) =>
{
infoTextBlock.Text = userToken.Token;
GlobalClient.UserLogin.Token = userToken.Token;
}
(error) =>
{
});
Thanks!
The issue is in the following code:
UserLogin.Token = userToken.Token;
Assigning a value to token doesn't mean the underlying credentials are set for the request. This is only done when you call the following:
UserLogin = userToken;
Poor design in the class interface if you ask me. There should be no difference between calling UserLogin.Token = Something, and UserLogin = NewLogin (with regards to underlying business rules).