Working with OneNote Page Content via the Graph API - microsoft-graph-api

I have a number of issues when working with OneNote via the graph api.
I understand that what OneNote uses is not straight html and they need to convert it back and forward for the API... but this would not appear to be that complex a task.
I am processing tags in the content and need to be able to update the tag element. It is fine except for where I have a list of items (p or li) tags where there are indents.
UPDATED
If there are items like this in my onenote
Dont have the rights to embed an image... so here is the link
https://xomq6w.ch.files.1drv.com/y4mc-mJZRhkI5c_fvOAbHGOnzEKVNKgxTHLop-OHfNofncLcC2gUc_Z_aYi0sgrc3hxIxxWpnvwRFc7p-reL5mX8J5pp1ePaY0V2McibdocMTplud7lxcso0EwAGJpHpBkcyLbcsLxDUpoYZ9T5XgCCxxmfUwQhHFPFQdFscfQoDPp7ZA-vCNbErgqyz0FK7prVaeMjs8LpiftMKu6-Xcv7Rg?width=160&height=541&cropmode=none
I hit the graph api to get the content with the following url via the graph api explorer
GET - https://graph.microsoft.com/beta/users/myname/onenote/pages/0-c9861926a1e8080518ca3750afa63800!1-2B303C571455A20B!102505/content?includeIDs=true
the html that comes back will look like this (note that the indents are not there)
<body data-absolute-enabled="true" style="font-family:Calibri;font-size:11pt">
<div id="div:{f115c5ea-0481-0f03-31aa-07b94321c323}{65}" style="position:absolute;left:264px;top:139px;width:624px">
<p id="p:{f115c5ea-0481-0f03-31aa-07b94321c323}{67}" data-tag="to-do" style="margin-top:0pt;margin-bottom:0pt">Item 1</p>
<p id="p:{f115c5ea-0481-0f03-31aa-07b94321c323}{74}" data-tag="to-do" style="margin-top:0pt;margin-bottom:0pt">Item 2</p>
<p id="p:{f115c5ea-0481-0f03-31aa-07b94321c323}{71}" data-tag="to-do" style="margin-top:0pt;margin-bottom:0pt">Item 2a</p>
<p id="p:{f115c5ea-0481-0f03-31aa-07b94321c323}{73}" data-tag="to-do" style="margin-top:0pt;margin-bottom:0pt">Item 2b</p>
<p id="p:{f115c5ea-0481-0f03-31aa-07b94321c323}{76}" data-tag="to-do" style="margin-top:0pt;margin-bottom:0pt">Item 3</p>
</div>
</body>
And if I patch the ones that have child items like this to set ids (leaving the id in there... turns it into a permanent data-id) for later updates or even to close a tag:
PATCH - https://graph.microsoft.com/beta/users/myname/onenote/pages/0-c9861926a1e8080518ca3750afa63800!1-2B303C571455A20B!102505/content?includeIDs=true
[
{
'target':'p:{f115c5ea-0481-0f03-31aa-07b94321c323}{74}',
'action':'replace',
'content':'<p id="p:{f115c5ea-0481-0f03-31aa-07b94321c323}{74}" data-tag="to-do" style="margin-top:0pt;margin-bottom:0pt">Item 2</p>'
}
]
The update will be processed and then the content will be updated to this:
Image here: https://kw9psa.ch.files.1drv.com/y4mplcvzocTmhN9DZBpFt4Sic3AOpxe2Ik4r7VX_0Joxs5ay9woTUYZufKSr0ojcfwQckovsJv5__fSrDx1GqWK9cFa2yQ7uFPE8JQOBwPyYn6PQZHDMM4N9JB4IXF4Rs7dIDoqa0XYfjz93qGiI97Ais99l04QxWZyV1Og5eXRguUYlBoNTwXIuzFEbKZfA03mt_ynxXCjbM7CFHYP99_QgQ?width=160&height=134&cropmode=none
<body data-absolute-enabled="true" style="font-family:Calibri;font-size:11pt">
<div id="div:{f115c5ea-0481-0f03-31aa-07b94321c323}{65}" style="position:absolute;left:264px;top:139px;width:624px">
<p id="p:{f115c5ea-0481-0f03-31aa-07b94321c323}{67}" data-tag="to-do" style="margin-top:0pt;margin-bottom:0pt">Item 1</p>
<p id="p:{f115c5ea-0481-0f03-31aa-07b94321c323}{71}" data-tag="to-do" style="margin-top:0pt;margin-bottom:0pt">Item 2a</p>
<p id="p:{f115c5ea-0481-0f03-31aa-07b94321c323}{73}" data-tag="to-do" style="margin-top:0pt;margin-bottom:0pt">Item 2b</p>
<p id="p:{78cb2ca5-efec-4e6f-a763-12e9cac6a2b5}{2}" lang="en-US" data-tag="to-do" data-id="p:{f115c5ea-0481-0f03-31aa-07b94321c323}{74}" style="margin-top:0pt;margin-bottom:0pt">Item 2</p>
<p id="p:{f115c5ea-0481-0f03-31aa-07b94321c323}{76}" data-tag="to-do" style="margin-top:0pt;margin-bottom:0pt">Item 3</p>
</div>
</body>
The same essentially happens for nested ul/ol items... I also tried replacing the whole list instead of individual items, but the api call errors out with no additional information. Nested bulleted lists also seem to lose the parent bullet.
I have tried batching all updates for all items, doing one at a time, reversing the order.
Funnily enough if I do an insert then the text indents and order are retained, but there is no delete/remove patch to clean out the old text... e.g.
[
{
'target':'p:{56d579c0-1203-0224-0587-6fe03fb82539}{34}',
'action':'insert',
'position':'before',
'content':'<p data-id="My_own_id" data-tag="to-do" style="margin-top:0pt;margin-bottom:0pt">Item 1</p>'
},
{
'target':'p:{56d579c0-1203-0224-0587-6fe03fb82539}{34}',
'action':'<tried replace or delete/remove hoping it was undocumented>',
'content':'<tried blank or just a span>'
}
]
Other issues include
tables losing their show gridlines properties
tags with no text not returning or even just disappearing if I update any text on the page
Partial Answer
Here is a partial answer for the non bulletted list issue... thanks to codeye for pointing me in the right direction.
By inserting my element before the existing one it retains the order.
[
{
'target':'p:{20781e6d-ba99-4fca-9994-622720cad7f8}{249}',
'action':'insert',
'position':'before',
'content':'<p data-id="test" data-tag="to-do" style="margin-top:0pt;margin-bottom:0pt">Item 2</p>'
}
]
Then removing the old one with the following it looks to retain the formatting. And as it is an empty div OneNote doesn't add the element.
[
{
'target':'p:{20781e6d-ba99-4fca-9994-622720cad7f8}{249}',
'action':'replace',
'content':'<div></div>'
}
]

A solution for table borders
When work with tables use border attribute instead of style.
API returns you a table with borders as <table style="border:1px solid;border-collapse:collapse">...</table>. Use table.style.border to find if there is a border (it can be 1px or 0px). To set border for the table use border attribute: <table border="1">...</table>.
The border attribute for td will be ignored.

To the specific problem with the to-do list I can confirm the issue where the indented items shift up and "detach" from the element being updated.
It looks like the only reliable way to update a list with indents is to replace the entire list (a similar limitation is placed on tables).
In your example all 5 paragraphs seem to need to be updated to maintain alignment.
If that doesn't work try replacing the surrounding div
[
{
'target':'div:{9dfa7be6-2de0-b802-01ae-20c164255f9d}{30}',
'action':'replace',
'content':'<div><p data-tag="to-do">Item 1</p>
<div><p data-tag="to-do">Item 2</p>
<ul>
<p data-tag="to-do:completed">Item 2a</p>
<p data-tag="to-do:completed">Item 2b</p>
</ul>
<div><p data-tag="to-do">Item 3</p>
<div><p data-tag="to-do">Item 4</p>
</div>'
}
]
As to how to remove an element the below should work
[{
'target': 'p:{f5837c83-d816-4337-ab6d-a52abde869a6}{13}',
'action': 'replace',
'content': '<br/>'
}]
This will not work in the middle of a list as you get a vertical gap.

Related

Why doesn't my Mapbox GL JS map display properly on first visit via Turbolinks?

I'm trying to add Turbolinks to my Rails 6 app (I'm late to the party) and I'm having display issues with TailwindCSS & Mapbox GL JS.
If I start my journey on the page with the map (new tab or refresh the page), then everything works as expected for the duration of my visits (I can leave and return to the page with the map repeatedly & each time everything works as expected). I can even visit pages with other instances of Mapbox on it & those display properly (I'm slowly transitioning from isolated script tags on each page into StimulusJS in a way that leaves me with sevaral Mapbox-providing controllers e.g. mapcity_controller & mapstreet_controller - I don't think this is part of the problem, but I don't want to leave anything to chance).
However, if I start my journey on a different page that does not have a map (new tab or refresh the page), then navigate to a page with Mapbox on it, the fixed and w-* styles don't work. If I scroll the page, the map flows up with the content & the map doesn't fill up the entire w-2/3 space that it should.
<div data-controller="mapbox" class="flex flex-col-reverse flex-wrap mt-10 md:flex-row">
<div class="h-full p-2 mt-64 overflow-scroll md:mt-0 lg:mt-0 md:w-1/2 lg:w-1/3">
CONTENT
</div>
<div class="md:w-1/2 lg:w-2/3">
<div id="map" class="fixed w-full h-64 md:w-1/2 lg:w-2/3 md:bottom-0 md:right-0 md:h-full"></div>
</div>
</div>
I thought it might have been the parent md:w-1/2 lg:w-2/3 container div, but removing that does not change behavior at all:
<div data-controller="mapbox" class="flex flex-col-reverse flex-wrap mt-10 md:flex-row">
<div class="h-full p-2 mt-64 overflow-scroll md:mt-0 lg:mt-0 md:w-1/2 lg:w-1/3">
CONTENT
</div>
<div id="map" class="fixed w-full h-64 md:w-1/2 lg:w-2/3 md:bottom-0 md:right-0 md:h-full"></div>
</div>
If I remove Mapbox from the equation, everything works as expected in either visiting scenario (I always have a full width, non-scrolling right pane regardless of whether or not the page is my first visit):
<div class="flex flex-col-reverse flex-wrap mt-10 md:flex-row">
<div class="h-full p-2 mt-64 overflow-scroll md:mt-0 lg:mt-0 md:w-1/2 lg:w-1/3">
LOTS OF MULTI-LINE CONTENT TO CAUSE SCROLLING
</div>
<div class="fixed w-full h-64 bg-blue-300 md:w-1/2 lg:w-2/3 md:bottom-0 md:right-0 md:h-full">OTHER CONTENT</div>
</div>
I'm guessing that something about my StimulusJS controller isn't working with Turbolinks properly. I've removed all of my extra code (setting zoom levels, adding sources & layers, etc), and the issue persists. I've tried using connect as well as initialize, with the same results.
import { Controller } from 'stimulus'
import mapboxgl from 'mapbox-gl'
export default class extends Controller {
connect () {
mapboxgl.accessToken = "my-access-token"
this.map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/satellite-streets-v11',
center: [0,0],
zoom: 2,
minZoom: 1
})
}
disconnect () {
this.map.remove()
this.map = undefined
}
}
Just in case, here is my index.js file:
import { Application } from 'stimulus'
import { definitionsFromContext } from 'stimulus/webpack-helpers'
const application = Application.start()
const context = require.context('controllers', true, /_controller\.js$/)
application.load(definitionsFromContext(context))
Here's my packs/application.js file:
require('#rails/ujs').start()
require('turbolinks').start()
require('stylesheets/application.scss')
require('stylesheets/custom.scss')
require('src')
import 'controllers'
The application.scss file contains:
#import "tailwindcss/base";
#import "tailwindcss/components";
#import "tailwindcss/utilities";
None of the custom styles in custom.scss apply to the map or its container divs.
deep breath
ok 😅 this is really embarrassing 😬
When I originally implemented this site, I took the approach of a <%= yield :styles %> in the head and a <%= yield :scripts %> at the bottom of the page with <% content_for(:scripts) do %> blocks wherever I wanted javascript.
Narrator's Voice: He should not have done this.
<% content_for(:styles) do %>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css' rel='stylesheet' />
<% end %>
If I remove my :styles block & put the stylesheet tag directly in the head, everything works as expected.

md-switch/md-button inside md-card

I want to show md-switch and md-button inside md-card. I am able to show text inside md-card at correct place, but switch connected to that text is shown at some other position.
Card is shown to the user on hovering over some content.
DemoCode.html
<md-card>
<md-card-header>
<md-card-header-text>
Sample 1
</md-card-header-text>
</md-card-header>
<md-card-content>
</md-card-content>
<md-card-actions>
<md-switch ng-model="data.cb2" aria-label="Switch" ng-true-value="'true'" ng-false-value="'false'">
{{ data.cb2 }}
</md-switch>
</md-card-actions>
</md-card>
I'm not sure I follow your question title and question content but here's an example of placing elements within a card - CodePen
If this is not exactly what you need please explain further in an edit.
Markup
<div ng-controller="AppCtrl" ng-cloak="" ng-app="MyApp">
<md-card>
<md-card-header layout="row">
<md-card-header-text>
Sample 1
</md-card-header-text>
</md-card-header>
<md-card-content>
</md-card-content>
<md-card-actions layout="row" layout-align="start center">
<label>My Switch</label>
<md-switch ng-model="data.cb2" aria-label="Switch" ng-true-value="'true'" ng-false-value="'false'">
{{ data.cb2 }}
</md-switch>
<md-button class="md-raised">Hello</md-button>
</md-card-actions>
</md-card>
</div>

ASP.net MVC - Why each element on a different row?

I'm creating a simple ASP.net MVC form using bootstrap. My form looks like:
#using(Html.BeginForm()){
<div class="row">
<div class="col-md-4">#Html.TextBox("City", "", new { placeholder = "City" })</div>
<div class="col-md-4">#Html.TextBox("State", "", new { placeholder = "State", maxlength = "2" })</div>
<div class="col-md-4">#Html.TextBox("Zip", "", new { placeholder = "Zip Code" })</div>
</div>
}
Although I'm using appropriate classes for columns and textboxes should be inline, they are appearing in different rows. To my understanding, as long as the column widths are within a limit of 12 columns, inline elements should appear in the same row. Why is there a line break between these elements?
N.B: If I remove divs for individual columns and put all textboxes in a single div, they appear in the same row correctly.
To my understanding, as long as the column widths are within a limit
of 12 columns, inline elements should appear in the same row.
Cut off for col-md-x is 970px. It means if the browser size is less than 970px, they will be rendered as individual row. You can read here.
If you always want them to appear them in a single row, you want to use col-xs-x, or mix classes like this -
<div class="row">
<div class="col-sm-4 col-xs-12"></div>
<div class="col-sm-4 col-xs-12"></div>
<div class="col-sm-4 col-xs-12"></div>
</div>
If you want your textboxes inline, they should be put inside a single div tag. but if you want to put it in different divs, you can try to remove "col-md-4" in your divs and use css instead, like this:
.row {
display: flex;
}
Also, add some extra "padding" or "margin" property for spacing between those textboxes.
If there is anything wrong about my answer, please feel free to correct it. Thanks!

Select unique set of nodes with XPath?

I want to select a set of elements as nodes (the content of div[#class="Adres"]):
<div class="KolumnaStyl">
<div class="Nazwa">ABCD</div>
<div class="Adres">
12-345 Warszawa
<br/>
ul. Krasnobrodzka 5
<br/>
Mazowieckie
This can be done with:
//div[#class="KolumnaStyl"]/div[#class="Adres"]/node()
As it happens, there are two identical div[#class="Adres"] on the page, which means that node() currently selects the content of both of them. I can't, however, call //div[#class="KolumnaStyl"][1] - that doesn't select the first occurrence.
How can I select a unique set of nodes if the parent directory exists multiple times?
Take a look at "XPath Get first element of subset".
Basically, predicates have higher precedence than the / and // operators.
So, (//div[#class="KolumnaStyl"]//(div[#class="Adres"]))[1] should return your desired result.
Also check out the spec for more background info.
If you want the first matched one, then do :
(//div[#class="KolumnaStyl"]//div[#class="Adres"])[1]/node()
In XPATH first means 1, not 0.
Here is a sample HTML :
<body>
<div class="foo">
<p> 12 </p>
</div>
<div class="foo">
<p> 112 </p>
</div>
</body>
XPATH expression:
(//div[#class = 'foo'])[1]
output
<div class="foo">
<p>12</p>
</div>

events are not triggerring after added by append method in jquery

<div class="sortable-sections">
<fieldset class="section draggable" id="flSectionType">
<div class="btn-drag section-handle hidden-view">
</div>
<div class="section-header">
<span class="section-label">Job Requirements</span> <a class="section-info" href="#more-info"
title="Minimum Education, Minimum Work Experience, Required Licenses/Certifications, Required Skills, Knowledge, and Abilities needed to perform the job.">
?</a>
</div>
<div class="sortable-items">
</div>
<a class="section-add-item hidden-view" id="addJR" href="#add-item" data-item='{"id":"addJR","template":"requirement","item_type":"Job Requirement"}'>
Add a new Job Requirement</a>
</fieldset>
<fieldset class="section draggable">
</fieldset>
<fieldset class="section draggable">
</fieldset>
<div>
The above code will generate a list of sections which can be dragged with in the sortable-sectons.
I am using the following script to remove sections manually and adding it back to sortable-sections, but it is not registering the jquery events.
var $section = $('.sortable-sections').find($('#flSectionType');
$('.sortable-sections').find($('#flSectionType').remove();
......
.......
$('.sortable-sections').append($section.val());
After appending the section, the event which are registered for the section-add-item css classes are not triggering.
Note:
instead of "on" used the "live" method. but it is not holding all attributes.
Edit:
Event code:
$('.section-add-item').on('click', function (e) {
that.addSection(this);
e.preventDefault();
});
instead of drag and drop, manually i am ordering the section on initial load.
.section-add-item must already exist when adding the event and the usage of on is wrong
$('body').on('click', '.section-add-item', function (e) {
});
instead of the body you can add another parent of .section-add-item that already exists before .section-add-item is created.

Resources