sample page update request in python -
headers: {'content-type': 'multipart/form-data; boundary=NoteBoundary','Authorization': 'Bearer '+token}
data:
--NoteBoundary
Content-Disposition: form-data; name="Commands"
Content-Type: application/json
[{'action': 'replace', 'content': 'last call', 'target': 'title'}, {'action': 'replace', 'content': '<html><head><title>last call</title><meta name="created" content="2017-04-04T07:12:36"</meta></head><body style="font-size:13px"><span style="font-size:12px;color: #3d7fba;margin-left: 3px;position: relative;;cursor: pointer;"></span><div><p>w</p>\n\n<p>96y09</p></div></body></html>', 'target': 'body'}]
--NoteBoundary--
request_url: 'https://www.onenote.com/api/v1.0/me/notes/pages/'+onenote_id
requests.patch(request_url, data = data, headers = headers)
When I update onenote page multiple times, it creates 1 duplicate copy of that note,
further update request does not update duplicate note but continue updating the older note
Majorly on second update call it creates 1 duplicate copy
Are you sure the "duplicate page" you are seeing isn't a conflict page?
https://efficiency365.com/2015/07/02/how-to-handle-onenote-sync-conflicts/
Related
I just set up Google Drive API Change Notifications, specifying the folderId of a certain shared folder that I have on My Drives. It sends notifications if I change the folder name, but doesn't send notifications if I add files.
I set up the channel successfully, as upon issuing these PHP commands
$client = new Google_Client();
$client->setApplicationName('Some Name');
$client->setAuthConfig( __DIR__ . '/service_account.json');
$client->setScopes(Google_Service_Drive::DRIVE);
$client->fetchAccessTokenWithAssertion();
$token = $client->getAccessToken();
$service = new Google\Service\Drive($client);
$httpclient = new \GuzzleHttp\Client();
$folderid = $someid;
$body = [
'kind' => "api#channel",
'id' => uniqid(),
'type' => 'web_hook',
'resourceId' => $folderid,
'resourceUri' => 'https://www.googleapis.com/drive/v3/files/'.$folderid,
'address' => 'myendpoint'
];
$apiendpoint = 'https://www.googleapis.com/drive/v3/files/' . $folderid . '/watch';
$result = $httpclient->post($apiendpoint, [
'headers' => ['Content-Type' => 'application/json', 'Authorization' => "Bearer {$token['access_token']}"],
'body' => json_encode($body),
"http_errors" => false]);
I see a successful return, and my channel immediately sends a sync message to my endpoint, where I have this
function myendpoint (WP_REST_Request $request) {
$headers= getallheaders();
write_log('in google drive webhook with headers '.print_r($headers, true));
$body = $request->get_body();
write_log('in google drive webhook with body '.print_r($body, true));
global $wpdb;
return http_response_code(200);
}
Via my logging, I see
[07-Nov-2021 12:52:35 UTC] in google drive webhook with headers Array
(
[Host] => courses-test.com
[User-Agent] => APIs-Google; (+https://developers.google.com/webmasters/APIs-Google.html)
[Content-Length] => 0
[Accept] => */*
[Accept-Encoding] => gzip, deflate, br
[X-Forwarded-For] => 66.102.8.121
[X-Forwarded-Proto] => https
[X-Goog-Channel-Expiration] => Sun, 07 Nov 2021 13:51:26 GMT
[X-Goog-Channel-Id] => 6187cbc82a08e
[X-Goog-Message-Number] => 1
[X-Goog-Resource-Id] => resid
[X-Goog-Resource-State] => sync
[X-Goog-Resource-Uri] => https://www.googleapis.com/drive/v3/files/folderid?acknowledgeAbuse=false&supportsAllDrives=false&supportsTeamDrives=false&alt=json
[X-Original-Host] => mydomain
)
I read here
https://developers.google.com/drive/api/v3/push
where it says
"To indicate success, you can return any of the following status codes: 200, 201, 202, 204, or 102."
and hence I am returning return http_response_code(200);
and also read here
https://developers.google.com/drive/api/v3/reference/files/watch
and I don't see anything obvious going on here.
Here
https://developers.google.com/search/docs/advanced/crawling/apis-user-agent?visit_id=637718909398077288-2640249237&rd=1
there is this remark
"Avoid unnecessary retry requests by ensuring that your application is well-designed and responds promptly to notification messages (within seconds)."
Now, when I first set up the channel, my endpoint returns right away, so that would seem to negate the above point.
Also, after I get channel set up, I also run these lines
$optParams = array(
'pageSize' => 10,
'fields' => 'nextPageToken, files(id, name)'
);
$results = $service->files->listFiles($optParams);
and indeed see a listing of files.
So I truly at a loss. When I go into the folder, whose folderId I specified to set up the watch channel, and add a file, or edit a file, I don't receive any notifications. The folder in question is shared, but as I understand, sharing only becomes an issue when dealing with shared drives, which is not my case.
And I read here,
https://developers.google.com/drive/api/v3/push#understanding-drive-api-notification-events
that watching for files being added should indeed work on a file watch, when the file is a folder, since add is one of the events for a resource, and it applies to Files.
**Update:
Ok, I seem to have things working, but not based on an add event. The only thing I changed, is that instead of using Guzzle to make the POST, as I show above, I use this
$channel->setId(uniqid());
$channel->setAddress($body['address']);
$channel->setType('web_hook');
$test = $service->files->watch($folderid, $channel);
Maybe adding some of those extra params in the $body like kind, resourceId, was messing things up when I was using POST via Guzzle. Or now that I know resourceId is not same as folderid, maybe that threw a flag on google's end so that a limited number of notifications were being sent. In any case, just using id', address and type seems to be the way to go, which I think is in the Google Drive Push docs. Just that in those docs, also mention of adding those other params in the request, like 'resourceId'. I suspect if I redo using Guzzle, but specify correct resourceId, or just leave it out and use id', address and type, that it would work.
So now, I get this sort of header in my endpoint when I add a file:
(
[Host] => courses-test.com
[User-Agent] => APIs-Google; (+https://developers.google.com/webmasters/APIs-Google.html)
[Content-Length] => 0
[Accept] => */*
[Accept-Encoding] => gzip, deflate, br
[X-Forwarded-For] => someip
[X-Forwarded-Proto] => https
[X-Goog-Changed] => children
[X-Goog-Channel-Expiration] => Mon, 08 Nov 2021 09:46:10 GMT
[X-Goog-Channel-Id] => chanid
[X-Goog-Message-Number] => num
[X-Goog-Resource-Id] => someid
[X-Goog-Resource-State] => update
[X-Goog-Resource-Uri] => https://www.googleapis.com/drive/v3/files/folderid?acknowledgeAbuse=false&supportsAllDrives=false&supportsTeamDrives=false&alt=json
[X-Original-Host] => mydomain
)
So I can detect [X-Goog-Changed] as being children and just keep track of my current uploaded files and see what the new file is. Or maybe using the fields query string somehow I can directly get the new file id?
Still seems odd that getting info about a new file is such a pain. Maybe it is easier in Google Cloud API and Google wants more people signing up for Cloud. In fact that add event only seems to apply to Cloud buckets.
In short: When watching a folder with files().watch() it is currently not possible to receive notifications about new files added to this folder
There are several workarounds that you can find along with explanations in answers to similar questions:
Use changes().watch():
https://stackoverflow.com/a/26551651/11599789 and https://stackoverflow.com/a/67787626/11599789
Pass the folderId to the token:
https://stackoverflow.com/a/33896300/11599789
Make copy of all file's metadata and query for changes:
https://stackoverflow.com/a/18151189/11599789
Also: There is a relevant feature request on Google's Issue Tracker.
It has been closed due to inactivity, but it is worth "starring" and commenting on it to increase visibility.
i used Canvas api to call for information such as user ID, log in and log out time...but the APIs can only call for one user at a time, so i made this code to first call for a list of user IDs:
import requests
import json
def get_all_time_entries():
url_address = "mywebsite.com/api/v1/courses/1111/users?per_page=50"
headers = {"Authorization": "Bearer " + "this is my bearer"
}
all_time_entries = []
for page in range(1,15):
url = "mywebsite.com/api/v1/courses/1111/users?per_page=50&page="+str(page)
response = requests.get(url=url, headers=headers).json()
all_time_entries.append(response)
return all_time_entries
print(get_all_time_entries())
I've managed to call for a list of users like this:
{
"created_at": "time",
"email": "email",
"id": ID number, (***)
"integration_id": null,
"login_id": "email",
"name": "Name",
"short_name": "Name ",
"sis_import_id": 111,
"sis_user_id": "Name ",
"sortable_name": ", Name "
},
Now i want to use a new loop to call for API with this link: mywebsite.com/api/v1/audit/authentication/users/:user_id (:user_id are the IDs got in the last api call of mine, marked with (***)) and get all the information to a list. How should i use loop with all those IDs?
i've managed to find a solution for calling next APIs based on the value i got from the previous list with this code:
import requests
import json
def get_all_time_entries():
url = "mywebsite.com/api/v1/courses/1111/users?per_page=50"
headers = {
"Authorization": "Bearer " + "this is my bearer",}
uri = "mywebsite.com/api/v1/audit/authentication/users/"
result = []
all_time_entries = []
# loop through all pages and return list of users of certain pages
for page in range(1,15):
url = "mywebsite.com/api/v1/courses/1111/users?per_page=50&page="+str(page)
response = requests.get(url=url, headers=headers).json()
all_time_entries.append(response)
return all_time_entries
# Search for id value in all_time_entries
value = [res['id'] for res in all_time_entries]
# Loop through user log in api call by 'id'
for x in value:
url = "mywebsite.com/api/v1/audit/authentication/users/"+str(x)
callback = requests.get(url=url, headers=headers).json()
result.append(callback)
return result
print(get_all_time_entries())
Now the problem is this code keeps returning the value of the "all_time_entries" not the "result" one. i've also tried to add a certain value for the list "all_time_entries" (with no loops and calls):
all_time_entries = [{'id': 11, 'name': 'name', 'created_at': '2021-01-23T22:34:30+07:00', 'sortable_name': ', name', 'short_name': 'name', 'sis_user_id': 'name#email.com', 'integration_id': None, 'sis_import_id': 1, 'login_id': 'name#email.com', 'email': 'name#email.com'}, {'id': 22, 'name': 'name2', 'created_at': '2021-01-23T22:34:19+07:00', 'sortable_name': ', name2', 'short_name': 'name2', 'sis_user_id': 'name2#email.com', 'integration_id': None, 'sis_import_id': 1, 'login_id': 'name2#email.com', 'email': 'name2#email.com'}]
...and it worked
So i dont know if this method cannot work with a very long list or something, and is there a way to work with long list?
I'd like to access some content from https://bato.to/ that requires me to login first. Their login page is: https://bato.to/forums/index.php?app=core&module=global§ion=login
I've opened chrome's web developer tools to inspect the POST that's sent when I click login. The 'Form Data' inside the POST is:
auth_key:880ea6a14ea49e853634fbdc5015a024
referer:https://bato.to/forums/
ips_username:startwinkling
ips_password:password1
rememberMe:1
So I've tried to implement this with the code:
Code so far
from requests import session
import re
AUTH_KEY = re.compile(r"<input type='hidden' name='auth_key' value='(.*?)' \/>")
payload = {
'ips_username': 'startwinkling',
'ips_password': 'password1',
'rememberMe' : '1',
'referer' : 'https://bato.to/forums/'
}
with session() as c:
login_url = 'https://bato.to/forums/index.php?app=core&module=global§ion=login'
page = c.get(login_url)
auth_key = AUTH_KEY.search(page.text).group(1)
payload['auth_key'] = auth_key
print("auth_key: %s" % auth_key)
page = c.post(login_url, data=payload)
page = c.get('https://bato.to/reader#4b57865eb3a9a9a6')
print(page.text)
I believe I'm grabbing and passing in the auth_key properly since the code outputs:
auth_key: 880ea6a14ea49e853634fbdc5015a024
But the HTML that's printed out indicate that I haven't been able to successfully log in. What am I missing here?
The URL you use for POST is not correct.
The correct one should be https://bato.to/forums/index.php?app=core&module=global§ion=login&do=process, it's not the same as login landing page, notice the extra do=process part.
Codes:
from requests import session
import re
AUTH_KEY = re.compile(r"<input type='hidden' name='auth_key' value='(.*?)' \/>")
payload = {
'ips_username': 'startwinkling',
'ips_password': 'password1',
'rememberMe' : '1',
'referer' : 'https://bato.to/forums/'
}
with session() as c:
login_url = 'https://bato.to/forums/index.php?app=core&module=global§ion=login'
page = c.get(login_url)
auth_key = AUTH_KEY.search(page.text).group(1)
payload['auth_key'] = auth_key
print("auth_key: %s" % auth_key)
page = c.post(login_url + '&do=process', data=payload)
page = c.get('https://bato.to/reader#4b57865eb3a9a9a6')
print(page.text)
P.S. I would suggest you to add some headers(not use default headers) as well, you might not want to appear as User-Agent: python-requests/1.2.3 CPython/2.7.3 Windows/7 on their analytics, also in case they set some limits on certain pages for "non-browser" visit.
I've been searching for an answer to this issue for several hours now, but have not found anything that relates to my case.
My setup:
I've create a custom TinyMCE button with a popup for a WordPress theme that allows the user to select a custom post type post from a dropdown (in this case a "Customer Review") and then have its ID inserted into the shortcode. Since the sites that this theme will run on have well over 1,000 reviews, i thought it would be better to json encode the data for the dropdown and then use select2 to search through a list of limited results (paged results) and keep the whole thing from blowing up. All of that is working successfully, except for 2 items:
The json encoded data shows up, but when i enter a search term, the select2 dropdown shows me a list of all reviews with the first selected. It does not find a result, even though the searched text is in the list
from above, once i enter a search term, select2 shows me all the results instead of just 10, or whatever limit i set.
Here is how i'm json encoding the data (in a file called bpl_content.php):
$args = array('post_type' => 'customer_reviews', 'posts_per_page' => -1, 'fields' => 'ids');
$posts = get_posts($args);
if( $posts ) :
$reviews = array();
foreach( $posts as $post ) : setup_postdata( $post );
$title = get_the_title();
$the_ID = get_the_ID();
$reviews[] = array (
'id' => $the_ID,
'text' => $title
);
endforeach;
endif;
echo json_encode( $reviews )
which returns
[{"id":12286,"text":"John Doe"},{"id":12285,"text":"Jane Doe"},...]
(There are over 800 items returned, so the above just shows 2, but you get the idea)
Here is the javascript i'm using to get my <select> menu populated with the json data:
$(".js-data-example-ajax").select2({
placeholder: "Select a Review",
ajax: {
url: "_bpl_content/bpl_content.php",
type: 'POST',
params: {
contentType: 'application/json; charset=utf-8'
},
dataType: 'json',
delay: 250,
data: function (term, page) {
return JSON.stringify({ q: term, page_limit: 10 });
},
processResults: function (data) {
return {
results: data
};
},
cache: true
},
minimumInputLength: 3
});
I can't for the life of me figure out why everything is working, except for the search and pagination. Any ideas?
You do not appear to have any code in your server side code for filtering and paginating results. Select2 realizes that it's more efficient for this to be done on the server side and expects developers to implement it there. The search term will be passed in as q and the page will be passed in as page (if it's available).
If you do not want to implement searching and pagination on the server side, or only have an endpoint which returns all results, you can still work around it. You just need to initialize Select2 with the JSON results as data instead of using the AJAX functionality.
$.ajax({
url: "_bpl_content/bpl_content.php",
type: 'POST',
contentType: 'application/json; charset=utf-8'
}).then(function (response) {
$(".js-data-example-ajax").select2({
placeholder: "Select a Review",
minimumInputLength: 3,
data: response
});
});
I am sending email using the Mail gem. Here's my code:
require 'mail'
require 'net/smtp'
Mail.defaults do
delivery_method :smtp, { :address => "smtp.arrakis.es",
:port => 587,
:domain => 'webmail.arrakis.com',
:user_name => 'myname#domain.com',
:password => 'pass',
:authentication => 'plain',
:enable_starttls_auto => true }
end
Mail::ContentTypeField.new("text/html") #this doesnt work
msgstr= File.read('text2.txt')
list.each do |entity|
begin
Mail.deliver do
from 'myname#domain.com'
to "#{entity}"
subject 'a good subject'
body msgstr
end
rescue => e
end
end
end
I don't know how to set up the content type, so that I can format my email as html for example. Though I actually just wish to be able to define bold text like my email client does: bold text. Does anybody know which content-type I need to specify in order to achieve this, and how to implement it with mail?
Just a note, the code above works fine for sending plain text email.
From the documentation
Writing and sending a multipart/alternative (html and text) email
Mail makes some basic assumptions and makes doing the common thing as
simple as possible.... (asking a lot from a mail library)
mail = Mail.deliver do
to 'nicolas#test.lindsaar.net.au'
from 'Mikel Lindsaar <mikel#test.lindsaar.net.au>'
subject 'First multipart email sent with Mail'
text_part do
body 'This is plain text'
end
html_part do
content_type 'text/html; charset=UTF-8'
body '<h1>This is HTML</h1>'
end
end
#Simone Carletti's answer is essentially correct, but I was struggling with this and didn't want a plain text portion to my email and a separate HTML portion. If you just want the entire email to be HTML, something like this will work:
mail = Mail.deliver do
to 'nicolas#test.lindsaar.net.au'
from 'Mikel Lindsaar <mikel#test.lindsaar.net.au>'
subject 'First email sent with Mail'
content_type 'text/html; charset=UTF-8'
body '<h1>This is HTML</h1>'
end
I may have missed it, I didn't see anything in the Mail gem documentation describing how to do that, which I would think would be more common than making a multipart message. The documentation only seems to cover plain text messages and multipart messages.