Cornell Note Taking Method Custom Pdf Generator Sharepoint
- Cornell Note Taking Method Custom Pdf Generator Sharepoint Templates
- Cornell Note Taking Method Custom Pdf Generator Sharepoint Word
Study Smarter has a quick online PDF generator to generate such custom sheets: blank (Cornell Style), ruled, or graph style. You can also enter your name, class name and date onto the PDF template. Pretty handy for taking note at class.
-->Learn to construct KQL queries for Search in SharePoint. This syntax reference describes KQL query elements and how to use property restrictions and operators in KQL queries.
- Cornell Notes PDF Generator. You can use the Cornell Notes note-taking method to take better notes in any. Cornell Method PDF Generator. The Cornell Note-taking Method. There are a few neat little webapps out there that produce custom PDF's of. Cornell Notetaking Method Custom PDF Generator September 15, 2005. Cornell Notetaking Method Custom.
- The Cornell Note-taking System 2 1/2” 6” 2” Note-taking Column Cue Column Summary After class, use this space at the bottom of each page to summarize the notes on that page. Record: During the lecture, use the note-taking column to record the lecture using telegraphic sentences.
- Cornell Note Taking Method Custom Pdf Generator.
Elements of a KQL query
A KQL query consists of one or more of the following elements:
Free text-keywords—words or phrases
Property restrictions
You can combine KQL query elements with one or more of the available operators.
If the KQL query contains only operators or is empty, it isn't valid. KQL queries are case-insensitive but the operators are case-sensitive (uppercase).
Note
The length limit of a KQL query varies depending on how you create it. If you create the KQL query by using the default SharePoint search front end, the length limit is 2,048 characters. However, KQL queries you create programmatically by using the Query object model have a default length limit of 4,096 characters. You can increase this limit up to 20,480 characters by using the MaxKeywordQueryTextLength property or the DiscoveryMaxKeywordQueryTextLength property (for eDiscovery).
Constructing free-text queries using KQL
When you construct your KQL query by using free-text expressions, Search in SharePoint matches results for the terms you chose for the query based on terms stored in the full-text index. This includes managed property values where FullTextQueriable is set to true.
Free text KQL queries are case-insensitive but the operators must be in uppercase. You can construct KQL queries by using one or more of the following as free-text expressions:
A word (includes one or more characters without spaces or punctuation)
A phrase (includes two or more words together, separated by spaces; however, the words must be enclosed in double quotation marks)
To construct complex queries, you can combine multiple free-text expressions with KQL query operators. If there are multiple free-text expressions without any operators in between them, the query behavior is the same as using the AND operator.
Using words in the free-text KQL query
When you use words in a free-text KQL query, Search in SharePoint returns results based on exact matches of your words with the terms stored in the full-text index. You can use just a part of a word, from the beginning of the word, by using the wildcard operator (*) to enable prefix matching. In prefix matching, Search in SharePoint matches results with terms that contain the word followed by zero or more characters.
For example, the following KQL queries return content items that contain the terms 'federated' and 'search':
federated search
federat* search
search fed*
KQL queries don't support suffix matching.
Using phrases in the free-text KQL query
When you use phrases in a free-text KQL query, Search in SharePoint returns only the items in which the words in your phrase are located next to each other. To specify a phrase in a KQL query, you must use double quotation marks.
KQL queries don't support suffix matching, so you can't use the wildcard operator before a phrase in free-text queries. However, you can use the wildcard operator after a phrase.
Property restriction queries in KQL
Using KQL, you can construct queries that use property restrictions to narrow the focus of the query to match only results based on a specified condition.
Specifying property restrictions
A basic property restriction consists of the following:
<Property Name><Property Operator><Property Value>
Table 1 lists some examples of valid property restrictions syntax in KQL queries.
Table 1. Valid property restriction syntax
Syntax | Returns |
---|---|
author:'John Smith' | Returns content items authored by John Smith. |
filetype:docx | Returns Microsoft Word documents. |
filename:budget.xlsx | Returns content items with the file name budget.xlsx . |
The property restriction must not include white space between the property name, property operator, and the property value, or the property restriction is treated as a free-text query. The length of a property restriction is limited to 2,048 characters.
In the following examples, the white space causes the query to return content items containing the terms 'author' and 'John Smith', instead of content items authored by John Smith:
author: 'John Smith'
author :'John Smith'
author : 'John Smith'
In other words, the previous property restrictions are equivalent to the following:
author 'John Smith'
Specifying property names for property restrictions
You must specify a valid managed property name for the property restriction. By default, Search in SharePoint includes several managed properties for documents.
To specify a property restriction for a crawled property value, you must first map the crawled property to a managed property. See Managed and crawled properties in Plan the end-user search experience.
The managed property must be Queryable so that you can search for that managed property in a document. In addition, the managed property may be Retrievable for the managed property to be retrieved. However, the managed property doesn't have to be Retrievable to carry out property searches.
Property operators that are supported in property restrictions
Search in SharePoint supports several property operators for property restrictions, as shown in Table 2.
Table 2. Valid property operators for property restrictions
Operator | Description | Supported managed property type |
---|---|---|
: | Returns results where the value specified in the property restriction is equal to the property value that is stored in the Property Store database, or matches individual terms in the property value that is stored in the full-text index. | Text DateTime Integer Decimal Double YesNo |
= | Returns search results where the property value is equal to the value specified in the property restriction. Note: We do not recommend combining the = operator together with asterisk ( *) when you do exact matching. | Text DateTime Integer Decimal Double YesNo |
< | Returns results where the property value is less than the value specified in the property restriction. | DateTime Integer Decimal Double |
> | Returns search results where the property value is greater than the value specified in the property restriction. | DateTime Integer Decimal Double |
<= | Returns search results where the property value is less than or equal to the value specified in the property restriction. | DateTime Integer Decimal Double |
>= | Returns search results where the property value is greater than or equal to the value specified in the property restriction. | DateTime Integer Decimal Double |
<> | Returns search results where the property value does not equal the value specified in the property restriction. | DateTime Integer Decimal Text Double YesNo |
. | Returns search results where the property value falls within the range specified in the property restriction. For example, the range A.B represents a set of values from A to B where both A and B are inclusive. For date ranges this means from the beginning of day A to the end of day B. | DateTime Integer Decimal Double |
Specifying property values
You must specify a property value that is a valid data type for the managed property's type. Table 3 lists these type mappings.
Table 3. Valid data type mappings for managed property types
Managed type | Data type |
---|---|
Text | String |
Integer | Int64 |
Double | System.Double |
Decimal | Decimal |
DateTime() | DateTime |
YesNo | Boolean |
Text property values
For text property values, the matching behavior depends on whether the property is stored in the full-text index or in the search index.
Property values in the full-text index
Property values are stored in the full-text index when the FullTextQueriable property is set to true for a managed property. You can configure this only for string properties. Property values that are specified in the query are matched against individual terms that are stored in the full-text index. Use the NoWordBreaker property to specify whether to match with the whole property value.
For example, if you're searching for a content item authored by Paul Shakespear, the following KQL query returns matching results:
author:Shakespear
author:Paul
Prefix matching is also supported. You can use the wildcard operator (*), but isn't required when you specify individual words. Continuing with the previous example, the following KQL query returns content items authored by Paul Shakespear as matches:
author:Shakesp*
When you specify a phrase for the property value, matched results must contain the specified phrase within the property value that is stored in the full-text index. The following query example returns content items with the text 'Advanced Search' in the title, such as 'Advanced Search XML', 'Learning About the Advanced Search web part', and so on:
title:'Advanced Search'
Prefix matching is also supported with phrases specified in property values, but you must use the wildcard operator (*) in the query, and it is supported only at the end of the phrase, as follows:
title:'Advanced Sear*'
The following queries do not return the expected results:
title:'Advan* Search'
title:'Advanced Sear'
Numerical values for properties
For numerical property values, which include the Integer, Double, and Decimal managed types, the property restriction is matched against the entire value of the property.
Date or time values for properties
KQL provides the datetime data type for date and time.The following ISO 8601-compatible datetime formats are supported in queries:
YYYY-MM-DD
YYYY-MM-DDThh:mm:ss
YYYY-MM-DDThh:mm:ssZ
YYYY-MM-DDThh:mm:ssfrZ
In these datetime formats:
YYYY specifies a four-digit year.
MM specifies a two-digit month. For example, 01 = January.
DD specifies a two-digit day of the month (01 through 31).
T specifies the letter 'T'.
hh specifies a two-digits hour (00 through 23); A.M./P.M. indication is not allowed.
mm specifies a two-digit minute (00 through 59).
ss specifies a two-digit second (00 through 59).
fr specifies an optional fraction of seconds, ss; between 1 to 7 digits that follows the . after the seconds. For example, 2012-09-27T11:57:34.1234567.
All date/time values must be specified according to the UTC (Coordinated Universal Time), also known as GMT (Greenwich Mean Time) time zone. The UTC time zone identifier (a trailing 'Z' character) is optional.
Relevant date intervals supported by KQL
KQL enables you to build search queries that support relative 'day' range query, with reserved keywords as shown in Table 4. Use double quotation marks (') for date intervals with a space between their names.
Matches would include items modified today:
LastModifiedTime=today
Matches would include items from the beginning of the current year until the end of the current year:
LastModifiedTime='this year'
Matches would include items from January 1st of 2019 until April 26th of 2019:
LastModifiedTime>=2019-01-01 AND LastModifiedTime<=2019-04-26
Table 4. Date interval reserved keywords
Name of date interval | Description |
---|---|
today | Represents the time from the beginning of the current day until the end of the current day. |
yesterday | Represents the time from the beginning of the day until the end of the day that precedes the current day. |
this week | Represents the time from the beginning of the current week until the end of the current week. The culture in which the query text was formulated is taken into account to determine the first day of the week. |
this month | Represents the time from the beginning of the current month until the end of the current month. |
last month | Represents the entire month that precedes the current month. |
this year | Represents the time from the beginning of the current year until the end of the current year. |
last year | Represents the entire year that precedes the current year. |
Using multiple property restrictions within a KQL query
Search in SharePoint supports the use of multiple property restrictions within the same KQL query. You can use either the same property for more than one property restriction, or a different property for each property restriction.
When you use multiple instances of the same property restriction, matches are based on the union of the property restrictions in the KQL query. Matches would include content items authored by John Smith or Jane Smith, as follows:
author:'John Smith' author:'Jane Smith'
This functionally is the same as using the OR Boolean operator, as follows:
author:'John Smith' OR author:'Jane Smith'
When you use different property restrictions, matches are based on an intersection of the property restrictions in the KQL query, as follows:
author:'John Smith' filetype:docx
Matches would include Microsoft Word documents authored by John Smith. This is the same as using the AND Boolean operator, as follows:
author:'John Smith' AND filetype:docx
Grouping property restrictions within a KQL query
Applies to: Office 365 SharePoint Online SharePoint 2019
You may use parenthesis ()
to group multiple property restrictions related to a specific property of type Text with the following format:
<Property Name>:(<Expression>)
More advanced queries might benefit from using the ()
notation to construct more condensed and readable query expressions.
The query:
author:'John Smith' AND author:'Jane Smith'
can be rewritten as:
author:('John Smith' 'Jane Smith')
The query:
title:Advanced title:Search title:Query NOT title:'Advanced Search Query'
can be rewritten as:
title:((Advanced OR Search OR Query) -'Advanced Search Query')
The query:
title:Advanced XRANK(cb=1) title:Search XRANK(cb=1) title:Query
can be rewritten as:
title:(Advanced XRANK(cb=1) Search XRANK(cb=1) Query)
Note
When using ()
to group an expression on a property query the number of matches might increase as individual query words are lemmatized, which they are not otherwise. Phrases in quotes are not lemmatized.

title:page
return matches with the exact term page while title:(page)
also return matches for the term pages.
KQL operators for complex queries
KQL syntax includes several operators that you can use to construct complex queries.
Boolean operators
You use Boolean operators to broaden or narrow your search. You can use Boolean operators with free text expressions and property restrictions in KQL queries. Table 5 lists the supported Boolean operators.
Table 5. Boolean operators supported in KQL
Operator | Description |
---|---|
AND | Returns search results that include all of the free text expressions, or property restrictions specified with the AND operator. You must specify a valid free text expression and/or a valid property restriction both preceding and following the AND operator. This is the same as using the plus ('+') character. |
NOT | Returns search results that don't include the specified free text expressions or property restrictions. You must specify a valid free text expression and/or a valid property restriction following the NOT operator. This is the same as using the minus ('-') character. |
OR | Returns search results that include one or more of the specified free text expressions or property restrictions. You must specify a valid free text expression and/or a valid property restriction both preceding and following the OR operator. |
Proximity operators
You use proximity operators to match the results where the specified search terms are within close proximity to each other. Proximity operators can be used with free-text expressions only; they are not supported with property restrictions in KQL queries. There are two proximity operators: NEAR and ONEAR.
NEAR operator
The NEAR operator matches the results where the specified search terms are within close proximity to each other, without preserving the order of the terms. The syntax for NEAR is as follows:
<expression> NEAR(n=4) <expression>
Where n is an optional parameter that indicates maximum distance between the terms. The value of n is an integer >= 0 with a default of 8.
The parameter n can be specified as n=v
where v represents the value, or shortened to only v; such as NEAR(4)
where v is 4.
For example:
'acquisition' NEAR 'debt'
This query matches items where the terms 'acquisition' and 'debt' appear within the same item, where an instance of 'acquisition' is followed by up to eight other terms, and then an instance of the term 'debt'; or vice versa. The order of the terms is not significant for the match.
If you need a smaller distance between the terms, you can specify it. The following query matches items where the terms 'acquisition' and 'debt' appear within the same item, where a maximum distance of 3 between the terms. Once again the order of the terms does not affect the match.
'acquisition' NEAR(n=3) 'debt'
Note
In SharePoint the NEAR operator no longer preserves the ordering of tokens. In addition, the NEAR operator now receives an optional parameter that indicates maximum token distance. However, the default value is still 8. If you must use the previous behavior, use ONEAR instead.
ONEAR operator
The ONEAR operator matches the results where the specified search terms are within close proximity to each other, while preserving the order of the terms. The syntax for ONEAR is as follows, where n is an optional parameter that indicates maximum distance between the terms. The value of n is an integer >= 0 with a default of 8.
<expression> ONEAR(n=4) <expression>
The parameter n can be specified as n=v
where v represents the value, or shortened to only v; such as ONEAR(4)
where v is 4.
For example, the following query matches items where the terms 'acquisition' and 'debt' appear within the same item, where an instance of 'acquisition' is followed by up to eight other terms, and then an instance of the term 'debt'. The order of the terms must match for an item to be returned:
'acquisition' ONEAR 'debt'
If you require a smaller distance between the terms, you can specify it as follows. This query matches items where the terms 'acquisition' and 'debt' appear within the same item, where a maximum distance of 3 between the terms. The order of the terms must match for an item to be returned:
'acquisition' ONEAR(n=3) 'debt'
Synonym operators
You use the WORDS operator to specify that the terms in the query are synonyms, and that results returned should match either of the specified terms. You can use the WORDS operator with free text expressions only; it is not supported with property restrictions in KQL queries.
The following query example matches results that contain either the term 'TV' or the term 'television'. This matching behavior is the same as if you had used the following query:
WORDS(TV, Television)
TV OR Television
These queries differ in how the results are ranked. When you use the WORDS operator, the terms 'TV' and 'television' are treated as synonyms instead of separate terms. Therefore, instances of either term are ranked as if they were the same term. For example, a content item that contained one instance of the term 'television' and five instances of the term 'TV' would be ranked the same as a content item with six instances of the term 'TV'.
Wildcard operator
You use the wildcard operator—the asterisk character (' * ')—to enable prefix matching. You can specify part of a word, from the beginning of the word, followed by the wildcard operator, in your query, as follows. This query would match results that include terms beginning with 'serv', followed by zero or more characters, such as serve, server, service, and so on:
serv*
Inclusion and exclusion operators
You can specify whether the results that are returned should include or exclude content that matches the value specified in the free text expression or the property restriction by using the inclusion and exclusion operators, described in Table 6.
Table 6. Operators for including and excluding content in results
Name | Operator | Behavior |
---|---|---|
Inclusion | ' + ' | Includes content with values that match the inclusion. This is the default behavior if no character is specified. This is the same as using the AND operator. |
Exclusion | ' - ' | Excludes content with values that match the exclusion. This is the same as using the NOT operator. |
Dynamic ranking operator
You use the XRANK operator to boost the dynamic rank of items based on certain term occurrences within the match expression, without changing which items match the query. An XRANK expression contains one component that must be matched, the match expression, and one or more components that contribute only to dynamic ranking, the rank expression. At least one of the parameters, excluding n, must be specified for an XRANK expression to be valid.
Note
Query latency (and probability of timeout) increases when using complex queries and especially when using xrank operators. The increase in query latency depends on the number of XRANK operators and the number of hits in the match expression and rank expression components in the query tree.
Match expressions may be any valid KQL expression, including nested XRANK expressions. Rank expressions may be any valid KQL expression without XRANK expressions. If your KQL queries have multiple XRANK operators, the final dynamic rank value is calculated as a sum of boosts across all XRANK operators.
Note
Use parenthesis to explicitly indicate the order of computation for KQL queries that have more than one XRANK operator at the same level.
You can use the XRANK operator in the following syntax:
<match expression> XRANK(cb=100, rb=0.4, pb=0.4, avgb=0.4, stdb=0.4, nb=0.4, n=200) <rank expression>
The XRANK operator's dynamic ranking calculation is based on this formula:
Table 7 lists the basic parameters available for the XRANK operator.
Table 7. XRANK operator parameters
Parameter | Value | Description |
---|---|---|
n | <integer_value> | Specifies the number of results to compute statistics from. This parameter does not affect the number of results that the dynamic rank contributes to; it is just a means to exclude irrelevant items from the statistics calculations. Default: 0. A zero value carries the semantic of all documents . |
nb | <float_value> | The nb parameter refers to normalized boost. This parameter specifies the factor that is multiplied with the product of the variance and average score of the rank values of the results set. f in the XRANK formula. |
Typically, normalized boost, nb, is the only parameter that is modified. This parameter provides the necessary control to promote or demote a particular item, without taking standard deviation into account.
The following advanced parameters are also available. However, typically they're not used.
Table 8. Advanced parameters for XRANK
Parameter | Value | Description |
---|---|---|
cb | <float_value> | The cb parameter refers to constant boost. Default: 0. a in the XRANK formula. |
stdb | <float_value> | The stdb parameter refers to standard deviation boost. Default: 0. e in the XRANK formula. |
avgb | <float_value> | The avgb parameter refers to average boost. Default: 0. d in the XRANK formula. |
rb | <float_value> | The rb parameter refers to range boost. This factor is multiplied with the range of rank values in the results set. Default: 0. b in the XRANK formula. |
pb | <float_value> | The pb parameter refers to percentage boost. This factor is multiplied with the item's own rank compared to the minimum value in the corpus. Default: 0. c in the XRANK formula. |
Examples
Example 1. The following expression matches items for which the default full-text index contains either 'cat' or 'dog'. The expression increases dynamic rank of those items with a constant boost of 100 for items that also contain 'thoroughbred'.
(cat OR dog) XRANK(cb=100) thoroughbred
Example 2. The following expression matches items for which the default full-text index contains either 'cat' or 'dog'. The expression increases dynamic rank of those items with a normalized boost of 1.5 for items that also contain 'thoroughbred'.
(cat OR dog) XRANK(nb=1.5) thoroughbred
Example 3. The following expression matches items for which the default full-text index contains either 'cat' or 'dog'. The expression increases dynamic rank of those items with a constant boost of 100 and a normalized boost of 1.5, for items that also contain 'thoroughbred'.
(cat OR dog) XRANK(cb=100, nb=1.5) thoroughbred
Example 4. The following expression matches all items containing the term 'animals', and boosts dynamic rank as follows:
Dynamic rank of items that contain the term 'dogs' is boosted by 100 points.
Dynamic rank of items that contain the term 'cats' is boosted by 200 points.
Dynamic rank of items that contain both the terms 'dogs' and 'cats' is boosted by 300 points.
(animals XRANK(cb=100) dogs) XRANK(cb=200) cats
Parenthesis
You can combine different parts of a keyword query by using the opening parenthesis character ' ( ' and closing parenthesis character ' ) '. Each opening parenthesis ' ( ' must have a matching closing parenthesis ' ) '. A white space before or after a parenthesis does not affect the query.
See also
Whats up Flow Fans!
This week we have a special FOTW From one of our MVP's – Paul Culmsee!
Paul Culmsee (@paulculmsee) is a management and technology consultant, Microsoft MVP and award winning author from Perth, Western Australia. He co-founded Seven Sigma Business Solutions (http://www.sevensigma.com.au ) and specialises in sensemaking, helping organisations (re)discover their purpose, knowledge management, strategic planning, IT governance, facilitation and all facets of SharePoint and Office365 delivery
Sympathy for the on-site technician
Hi everyone.
Imagine a company where service technicians perform on-site repair of equipment. Inevitably, in doing this sort of work, the technician will need to refer to equipment drawings, service history, past photos, specifications and/or operating manuals.
These days PowerApps is fast-becoming a great option for such a scenario because many field workers prefer to use their phone or a tablet. I have made many apps like this and PowerApps is a great solution for this use-case. But PowerApps also has some limitations, and right now that is around the display of documents from SharePoint. For a start, it is impossible to display office documents natively in PowerApps at this time, and there are authentication-related issues in certain circumstances when pulling content from SharePoint.
But fear not… with a 6-step flow, it is possible to solve this problem. This flow allows a remote user to securely request a document from SharePoint, but importantly, converts that document to a PDF on the fly.
There are two big benefits from this:
- A reduction in time and effort for document controllers. If a document frequently changes, it is most likely in word, excel or PowerPoint format. They do not have to worry about converting it to PDF.
- It allows the document to be viewed in PowerApps natively (As a result of #2, on top of some Flow kung-fu, we will learn some PowerApp tricks in this article too :-).
Now in the (admittedly large article to follow, I go into detail on how to set this up, but if you prefer to see this in video form, we also have you covered…
Step 1: Setting up a SharePoint library
Since this is a field-worker scenario, let’s make a SharePoint library and be good digital citizens by throwing in an extra couple of columns so we can tag documents by their type. I’ll keep it deliberately simple for the purpose of this post, but you can extend this type of library setup in whatever manner suits you.
I created a new SharePoint site, and added the following columns to the default document library:
- Equipment – Single Line of text
- DocumentType – Single Line of text
Now upload some documents and tag them by type and by equipment. In the diagram below, take note that I’ve also thrown in a folder with some documents inside. While I won’t win any information architecture awards for doing this, it is very much the reality for many organisations and it demonstrates that the flow we will build accommodates complex folder structures also.
Oh… also keep the documents relatively simple. I can’t guarantee this auto-pdf goodness works with crazy large documents or those with embedded bits and pieces.
Building the Flow
The flow we are going to build is going to be triggered from PowerApps. PowerApps will pass in the ID and folder path of a particular file, and the flow will do a small bit of data cleansing before using a very powerful action called Send an HTTP request to SharePoint to bring it back as a PDF.
Please note this flow is deceptively simple as it only requires as little as 5 actions, but we are using some nice trickery so take your time…
Step 1: Create a new Blank Flow, and choose the PowerApps trigger
Step 2: Add an Initialize Variable Action.
This variable will hold the ID of the file sent from PowerApps. For reasons that will become apparent later, rename this action to FileID, add a variable called ID, make it String format.
For the Value, click Ask in PowerApps from the Dynamic Content panel (click see more if this is missing)
If you do this right, you will see a FileID_Value parameter name in the textbox. Now when we invoke this flow from PowerApps, this is the name of the parameter that the user will see. Eg:
Why is this? Well, behind the scenes, PowerApps used the name of the action to generate this parameter name, hence why it made sense to rename it something simple so you don’t make the PowerApps view confusing.
Note: I tend to add comments to my flow actions to make the intention clear as shown below.
Step 3: Add another Initialize Variable Action.
This variable will hold the folder path of the file and will also be sent from PowerApps. Rename this action to FolderPath, add a variable of the same name. This time make it a string and once, again use “Ask In PowerApps” to set a parameter called “FolderPath_Value” as shown below…
Step 3a. Sanity check interlude!
Please note this common trap for new players… if you clicked Ask in PowerApps more than once, then you might have some additional unwelcome parameters. I recommend being really (really) careful here to ensure you only have two parameters defined, otherwise it will make working with PowerApps a bit of a pain later. So, check that in Dynamic content, you only see the two parameters as shown below…
Step 4: Clean up the data.
A common occurrence in the world of messing with low-level stuff is that data is not always in the format we need it to be. In this case, it turns out that when PowerApps sends the folder path to Flow, it will send it in this format.
DocumentLibraryName/Folder/
Unfortunately, when we send this to SharePoint to get the PDF URL, it needs to be in a format that includes the site collection URL and the removal of the trailing slash.
/Sites/SiteName/DocumentLibraryName/Folder
Adding the site collection URL is easy – and we will do that later. But the trailing slash needs a flow expression to strip the trailing slash from the path. The expression will look like this:
substring(<path>,0,sub(length(<path>),1))
If you are new to Flow expressions, then yes – I agree that they are ugly. But trust me, they do become quite intuitive over time and actually can really bring your flows to life. For the record, what we are doing here is using a substring function to grab all except the final character of FolderPath variable. To exclude the final character, I take the length of FolderPath and subtract it by 1.
Now we could sort this out by adding another initialize variable action to reformat the path in the way we want it. But that means an extra step and variable. So, let’s be cool and modify the previous step to do it in one hit. So, go back to your FolderPath action and delete the FolderPath_Value reference in the Value textbox.
Now click Expression in the Dynamic content panel and check out this little-known trick. Type in the word substring( and you will see a somewhat annoying pop-up like so…
Now, make sure your cursor position is in between the two brackets after the word substring! Then click the Dynamic Content tab, and you can now choose the FolderPath parameter. It will add it into your expression. Neat huh?
Now your expression will look like the following:
substring(triggerBody()['FolderPath_Value'])
This neat trick allows you to write flow expressions and refer to variables or parameters without manually having to type them in. Now all we need to do is fill out the rest of the expression using this reference to the FilePath parameter.
From:substring(<path>,0,sub(length(<path>),1))we replace <path> with triggerBody()['FolderPath_Value']:
substring(triggerBody()['FolderPath_Value'],0,sub(length(triggerBody()['FolderPath_Value']),1))
Your flow action will now look like the screenshot below… note that I completed this task by adding the above expression to the comment for the action to make it clear what is going on…
Step 5: Get the PDF Information from SharePoint
Now if you want to just get down to business, feel free to skip to bit where I show the next action to add. But if you want to know what we are going to do, read on!
This Flow leverages a little-known capability of SharePoint that among other things, allows us to generate image thumbnails and PDF’s of documents. This capability is an API with the nerdy name of RenderListDataAsStream. In a nutshell, it is possible to pass a reference to a document and it will dutifully spit out the URL to a PDF version.
To do this, we need to pass 4 things to the API.
- The document library where we want to get the file from
- The folder where this file resides in the library
- The SharePoint ID of the file we want to PDF
- A special code that tells the API to bring back a URL of the generated PDF
For reference, a sample API call for a file with an ID of 14 in the default SharePoint document library in a folder called “test” would look like this.
https://culmsee.sharepoint.com/sites/flowoftheweek/_api/web/lists/GetbyTitle(‘Documents’)/RenderListDataAsStream?FilterField1=ID&FilterValue1=14
Don’t try the above link in the browser, as it is a POST request. Additionally, we need to send some information in the request body too, namely the folder where the file resides and the code to get the PDF URL. The body looks like this:
{
'parameters': {
'RenderOptions' : 4103,
'FolderServerRelativeUrl' : '/Sites/FlowOfTheWeek/Documents/test'
}
}
Ok, so what is the deal with the RenderOption number above? Well, this API does lots more than just generate a PDF, and that parameter allows you to specify what information you want back. The documentation includes a table of different interesting things you can return, which you can do by adding the values together.
Label | Description | Value |
ContextInfo | Return list context information | 1 |
ListData | Return list data | 2 |
ListSchema | Return list schema | 4 |
EnableMediaTAUrls | Enables URLs pointing to Media TA service, such as .thumbnailUrl, .videoManifestUrl, .pdfConversionUrls. | 4096 |
So, we are asking this API not just to bring back the data associated with a list item, but also some additional useful stuff. The last entry is particularly interesting as it mentions a mysterious beast known as the Media TA service which I assume means either “translation” or “totally awesome” :-). Basically, what happens is if we total the numbers listed in the above table (4103), we will end up all the data we need to do PDF conversion.
Okay enough talk!
Add a SharePoint action called Send an HTTP request to SharePoint. Set the Site Address to the site that contains your document library and set the Method to POST. Set the URI to _api/web/lists/GetbyTitle(<docLib>)/RenderListDataAsStream?FilterField1=ID&FilterValue1=, where <Doclib> is the name you specified for the document library
( for example, mine is _api/web/lists/GetbyTitle(‘Documents’)/RenderListDataAsStream?FilterField1=ID&FilterValue1= ).
Finally, on the end of the URI, click Dynamic Content and choose the ID variable as shown below:
In the Body section, paste the following configuration (watch the quotes when pasting from this article):
{
'parameters': {
'RenderOptions' : 4103,
'FolderServerRelativeUrl' : '/<your site collection URL>/'
}
}
In my case the FolderServerRelativeURL was “/sites/flowoftheweek/” but if you use the root site collection, it will simply be a slash “/”.
Finally, place your cursor just after the slash in the FolderServerRelativeURL parameter and from Dynamic content, choose the FolderPath variable.
Step 6: Save and Test the Flow
Cornell Note Taking Method Custom Pdf Generator Sharepoint Templates
At this point, click the Test icon in the top right of the screen. Choose the option I’ll perform the trigger action and click the Save & Test button. On the popup that follows, click the Continue button and on the next screen, type in the ID number of one of the documents in your library and the folder path the document resides in.
For example: The first document uploaded to the library will likely be ID 1 and if is the default SharePoint document library, the folder will be Shared Documents/
Click the Run Flow button. Your flow will start and you can click Done. Assuming it worked, you will see a green tick of happiness in the history.
Click on the Send an HTTP Request to SharePoint action to expand it. We need to grab the output from the API call for the next action. Find the OUTPUTS section and copy the entire contents to the clipboard….
Note: If you made an error (e.g., you asked for a file that does not exist or the document library is empty), then the subsequent steps will fail. You can quickly sense-check this by looking at the clipboard output in notepad.
If there is no file found, you will see a line “Row”: [] in the ListData section like so…
{
'wpq': ',
'Templates': {},
'ListData': {
'Row': []
But if you did retrieve a file, there will be heaps of data inside Row like so…
{
'wpq': ',
'Templates': {},
'ListData': {
'Row': [
{
'ID': '1',
https://powerfulge.weebly.com/blog/wd-my-book-studio-2tb-for-mac. 'PermMask': '0x7ffffffffffbffff',
[snip a heap of stuff]
}
Note: The output needs to have row data before moving to the next step
Step 6: Add an action to help us work with APi output
Go back to edit mode and add a Data Operations action called Parse JSON to your flow. This action will allow us to make use of the output of the API call in the subsequent flow step.
Click the Use sample payload to generate schema link, paste your clipboard contents into the window and click the Done button.
In the Content field, go to Dynamic content panel and choose Body from the Send an HTTP Request to SharePoint action.
Step 7: Construct the PDF URL
Now when this API is called, a lot of data is returned. The purpose of the Parse JSON action in step 6 was to process all this output and turn it into flow objects to make our life easier here. This allows us to quickly grab only certain data from the output of the previous flow step without having to parse the output ourselves via more complex expressions.
At this point, you might be thinking that one of those is a nicely formatted PDF URL all done for us. Unfortunately, this is not the case. Microsoft give you all the bits you need, but it is up to you to put it all together. As a result, we need to do some more work to create the URL by trawling through some of the data returned by the previous step.
Of all the output currently in your clipboard, the main one that interests us is this entry…
“.pdfConversionUrl”: “{.mediaBaseUrl}/transform/pdf?provider=spo&inputFormat={.fileType}&cs={.callerStack}&docid={.spItemUrl}&{.driveAccessToken}”
This parameter is basically a template for generating the PDF URL. All the stuff in curly braces are tokens that have to be replaced by the actual values that are also returned as part of the API call. For example, if I search the clipboard content for the first token in .pdfConversionURL called {.mediabaseURL}, I find this entry…
“.mediaBaseUrl”: “https://australiasoutheast1-mediap.svc.ms”
Now go and look at .pdfConversionUrl again. Replace {.mediabaseUrl} with https://australiasoutheast1-mediap.svc.ms and now we have
“.pdfConversionUrl”: “https://australiasoutheast1-mediap.svc.ms /transform/pdf?provider=spo&inputFormat={.fileType}&cs={.callerStack}&docid={.spItemUrl}&{.driveAccessToken}
Get the idea? We need to replace the remaining tokens ( {.fileType}, {.callerStack}, {spItemUrl} and {driveAccessToken} and replace them. Once we have done this, we finally have created our PDF URL. When we subsequently access that URL, we will receive the converted document in PDF format without needing to store the PDF. The source document can stay in its native office format!
Phew! Now let’s get this done…
Add an Initialize Variable action to your flow, name the variable PDFURL (or something similar) and set its Type to String format.
Now we come to the most complex bit of the flow where we have to substitute the tokens we just examined. Be careful here as this is the most likely place to make an error. In the Value textbox, click the Dynamic content flyout and find .mediaBaseUrl from the Parse JSON action…
Next, add the following text to the Value textbox, taking care not to delete what you just added in the previous step.
/transform/pdf?provider=spo&inputFormat=
Now we come to a slightly tricky bit. The next bit of content we need is the file type of the document we are dealing with. The bit that is tricky about this even though we are only asking for a single file when we call the API, it comes back as an array. Why? Well this API allows you to process multiple files in one go, so it always returns an array in the output, even if you only requested a single file.
The offending bit of data returned by the API is shown below. Inside a ListData object, we have a an array of Row objects:
'ListData': {
'Row': [
{
'ID': '1',
We need to get the file type of the document, so the PDF converter knows what it is dealing with. Like the way we dealt with removing the trailing slash from the FilePath variable in step 4, we can use another expression to handle it. Click the Expression tab and type in the following:
first(body('Parse_JSON')?['ListData']?['Row'])?['File_x0020_Type']
What this expression is doing is assuming we are only handling one file at a time and grabbing the first element of the Rows array and then grabbing the File_x0020_Type property.
Next, add the following text to the Value textbox, taking care not to delete what you just added in the previous step.
&cs=
Also, be super careful here because at the time of writing, the cursor in this textbox can randomly move and wipe out your edits…
Now in the Value textbox, click the Dynamic content panel and find .callerStack from the Parse JSON action…
Next, add the following text to the Value textbox, taking care not to delete what you just added in the previous step.
&docid=
Now we come to another array that needs to be handled. This is the URL of the document we are dealing with. Click the Expression tab and type in the following:
first(body('Parse_JSON')?['ListData']?['Row'])?['.spItemUrl']
Okay we are almost done… Add an ampersand ( & ) to the Value textbox, and then click the Dynamic content panel and find .driveAccessToken from the Parse JSON action…
Whew! We are done. I realise that was a bit of effort for one flow action, but it will all be worth it in the end. As a final step, rename this task to “Generate PDF URL”.
Step 8: Send the PDF URL back to PowerApps
Add the PowerApps – Respond to PowerApps action to the flow. Click the Add an output icon and choose Text from the list of output types. Name the output PDFURL and set the value to the variable you created in step 12 (PDFURL).
Ok we completed the flow. Save it and give it a quick review. It should look something like this…
If you have not done so already, save your Flow and give it a short, sharp name like GetMyPDF.
Building a sample PowerApp
Now let’s build a sample field worker iPad app. This app will allow a field worker to choose an equipment type, and then based on the equipment type, choose the available document types for it. While we will not win any design awards for this proof of concept, the added-bonus is you can learn how to do cascading dropdowns using SharePoint metadata.
The concept is shown below. First our trusty technician picks the equipment they are dealing with (step 1), and then they further refine by document type (step 2). Since the dropdowns cascade, only document types that exist for a chosen piece of equipment will be selectable.
This in turn refines a list of matching documents, irrespective of which folder they actually live in. From there the user clicks the “go” button (step 3) to display the PDF version (step 4).
To create the app, in the PowerApps portal, make a canvas app from blank and choose the Tablet form factor…
Next, let’s connect the app to the SharePoint document library we used at the start of this article. From the View menu, choose Data Sources and click the Add data source button. Make or use a SharePoint connection and choose the site collection where you uploaded the documents.
Click the Go button and manually type in the name of your document library and click Connect (at the time of writing, you still have to link to SharePoint document libraries by typing in their name).
From the Insert menu, choose Gallery and add a Blank vertical gallery. Set the data source to the connection you just made…
Click on the pencil in the gallery, and then from the Insert menu, choose Label. Click on this label and in the Text property, change it to ThisItem.'{FilenameWithExtension}'. You should now see the files and folders from the document library listed.
Note: {FilenameWithExtension} is one of a number of properties of a SharePoint library that PowerApps provides. We will soon use another built-in one as well…
Now let’s make use of the SharePoint columns we created at the start of this article. Recall that I made a column called DocumentType and one called Equipment. We are going to use these to filter this gallery so that only certain files are shown. After all, our field workers want to quickly get to the documents they need…
Click outside of the gallery, and then from the Insert menu, choose Controls and then pick Dropdown from the list
Set the Items property for this dropdown to Distinct(Documents, Equipment). If you then hold the Alt key, you will be able to click the dropdown and see the equipment. This list has been built from the SharePoint document library. The distinct function returns all unique values for the specified column (Equipment) without hardcoding the values.
Note”: We are seeing a blank value in the dropdown, because of the folder I created in this library which has not been tagged to a device or document type. But the empty value is seen as something unique by the Distinct function. If you want to make it go away, try this alternative setting for the Items property. This will remove the empty value…
Filter(Distinct(Documents, Equipment), !IsBlank(Result))
Insert another dropdown and place it under the first one. Set the Items property for this one to
Distinct(Filter(Documents, Equipment = Dropdown1.Selected.Value ), DocumentType )
What this does is filter the document library to only items that match the currently selected equipment. Then it uses distinct to get all the unique document types for that equipment. This pretty much means you now have a cascading set of dropdowns. The values in the second dropdown are based on what was selected in the first dropdown.
Now let’s return to our gallery and modify it so that it only shows items based on the dropdowns. In the Items property of the gallery, change it to:
Filter(Documents, Equipment=Dropdown1.Selected.Value, DocumentType=Dropdown2.Selected.Value)
Note that depending on the name of your dropdown controls, you might need to modify the text above.
Test by changing the values of the dropdowns and confirm that different documents are listed in the gallery.
Next let’s add a button to the gallery so a user can load the PDF. Select your gallery and click the pencil icon. From the Insert menu, add a Button and set the label to “go”. Position it to the right of the file name. Also double check that you placed the button inside the gallery and not on the screen by confirming the button sits as a child control of the gallery in the left hand navigation.
Click on the newly minted button and let’s now call our PDF generation flow. From the Action menu, click on Flows and in the data panel, find your Flow.
If this worked, the flow will be asking you for the two “Ask in PowerApps” parameters that you set up earlier. For the first parameter, enter Gallery1.Selected.ID and for the second, enter Gallery1.Selected.'{Path}'. Your formulae should look something like this…
E.g.: GetMyPDF.Run(Gallery1.Selected.ID, Gallery1.Selected.'{Path}')
Note: I am assuming your Gallery is called Gallery1.
We are not quite done though, because we need to capture the output of running this flow as a variable. This will give us the PDF URL. To achieve this, modify your expression so that the flow is inside the Set function …
Set(MyPDF, GetMyPDF.Run(Gallery1.Selected.ID, Gallery1.Selected.'{Path}') )
If you want to test things at this point, simply preview the app and press the button. If flow has worked, you can see the variable via the View menu so see if a URL has been created.
Finally, let’s display the PDF.
From the Insert menu, choose Controls and add a PDF Viewer control. Place it to the right of the gallery and set the Document property to MyPDF.pdfurl as shown below:
If all things go to plan, you should be viewing PDF’s. Use the dropdowns to find a different document and click the button to see the PDF. It may take a few seconds to load the first time, particularly if the original document is large, but it should happily display.
Try some different document types… For example, below is a sample excel document that I loaded into the library…
Taking it further (and caveats)
In the real world, I have used this technique to deliver documents to field staff with much more sophisticated navigation and searching capability. It has been used on large construction sites to share safety bulletins and it has been integrated with more extensive PowerApps solutions for managing callouts/jobs and managing assets. Field staff were stoked that they were able to quickly call up and view key information without having to navigate through a file share or SharePoint.
By the way, this method can also can be used to generate image thumbnails too, making it excellent for apps that are photo heavy and bandwidth sensitive. I even utilised this the thumbnail variation of this technique to add photos to activity feeds in apps and have even used to create rich action cards in Microsoft Teams.
Another use for this approach is its utility as a free PDF conversion tool. Paul O'Flaherty pointed this out to me that is more elegant than the commonly used OneDrive Flow action that many people use. Using a variation of the flow I outlined in this article, we can save a html file directly to a SharePoint library, and then use that file’s ID and Path to get the PDF URL of it. Finally, we can use the flow HTTP action to get a hold of that PDF and save it into SharePoint. Neat eh? We don’t need to use OneDrive connector anymore.
So what’s the catch? The main one is the common caveat – potentially lots of flow runs. Remember that with this method, each time a user clicks a button, a flow run is used to generate the PDF URL. One can improve this by leveraging the fact that we can call the RenderListDataAsStream API and send it multiple documents to convert to PDF. Thus, a single flow run can actually generate a lot of PDF (and thumbnail) URL’s.
Finally, Ashlee and I recorded a video of this technique so you can follow along with that over at YouTube.
Phew! We are finally done. Thank for sticking with me and please let us know your feedback and how you plan to utilise this approach. This feedback often gives me ideas and new directions to explore.
Till next time
Paul Culmsee
Company: www.sevensigma.com.au
Books: www.hereticsguidebooks.com
Cornell Note Taking Method Custom Pdf Generator Sharepoint Word
Blog: www.cleverworkarounds.com