- Home
- Blog
Blog
On 04/02/2025
Les RegEx de définir des motifs de recherche complexes et de les appliquer à des chaînes de texte. Voici une explication détaillée de leur fonctionnement :
1. Création d'une Expression Régulière
En JavaScript, vous pouvez créer une expression régulière de deux manières :
- Littérale : En utilisant des barres obliques `/`.
- Objet `RegExp` : En utilisant le constructeur `RegExp`.
Exemple Littéral :
javascript
let regex = /motif/;
Exemple avec `RegExp` :
javascript
let regex = new RegExp("motif");
2. Utilisation des Expressions Régulières
Les expressions régulières peuvent être utilisées avec plusieurs méthodes de chaînes de caractères et d'objets `RegExp`.
Méthodes de Chaînes de Caractères :
- `search()` : Recherche une correspondance et retourne l'index de la première occurrence.
- `match()` : Recherche toutes les correspondances et retourne un tableau des résultats.
- `replace()` : Remplace les correspondances par une autre chaîne.
- `split()` : Divise une chaîne en un tableau basé sur les correspondances.
Méthodes de l'Objet `RegExp` :
- `test()` : Vérifie si une chaîne contient une correspondance.
- `exec()` : Recherche une correspondance et retourne un tableau d'informations sur la correspondance.
3. Exemples Pratiques
Rechercher une Correspondance :
javascript
let str = "Bonjour, monde!";
let regex = /monde/;
let result = str.search(regex); // Retourne 9
Trouver Toutes les Correspondances :
javascript
let str = "Bonjour, monde! Bonjour, tout le monde!";
let regex = /Bonjour/g; // Le flag 'g' signifie global, pour trouver toutes les occurrences
let result = str.match(regex); // Retourne ["Bonjour", "Bonjour"]
Remplacer des Correspondances :
javascript
let str = "Bonjour, monde!";
let regex = /monde/;
let result = str.replace(regex, "univers"); // Retourne "Bonjour, univers!"
Diviser une Chaîne :
javascript
let str = "un,deux,trois";
let regex = /,/;
let result = str.split(regex); // Retourne ["un", "deux", "trois"]
Tester une Correspondance :
javascript
let str = "Bonjour, monde!";
let regex = /monde/;
let result = regex.test(str); // Retourne true
Utiliser `exec()` :
javascript
let str = "Bonjour, monde!";
let regex = /monde/;
let result = regex.exec(str); // Retourne ["monde", index: 9, input: "Bonjour, monde!", groups: undefined]
4. Flags (Drapeaux)
Les expressions régulières peuvent inclure des flags pour modifier leur comportement :
- `i` : Ignore la casse (case-insensitive).
- `g` : Recherche globalement (trouve toutes les occurrences).
- `m` : Mode multi-ligne (traiter chaque ligne séparément).
- `s` : Mode dotAll (le point `.` correspond aussi aux sauts de ligne).
- `u` : Mode Unicode.
- `y` : Mode sticky (recherche à partir de la position `lastIndex`).
Exemple avec Flags :
javascript
let regex = /bonjour/i; // Ignore la casse
let result = regex.test("Bonjour"); // Retourne true
5. Motifs de Recherche
Les expressions régulières permettent de définir des motifs complexes :
- `^` : Début de la chaîne.
- `$` : Fin de la chaîne.
- `.` : N'importe quel caractère sauf un saut de ligne.
- `\d` : Un chiffre.
- `\w` : Un caractère alphanumérique.
- `\s` : Un espace blanc.
- `*` : Zéro ou plusieurs occurrences du motif précédent.
- `+` : Une ou plusieurs occurrences du motif précédent.
- `?` : Zéro ou une occurrence du motif précédent.
- `{n}` : Exactement `n` occurrences du motif précédent.
- `{n,m}` : Entre `n` et `m` occurrences du motif précédent.
Exemple de Motif Complexe :
javascript
let regex = /^\d{3}-\d{2}-\d{4}$/; // Correspond à un numéro de sécurité sociale américain
let result = regex.test("123-45-6789"); // Retourne true
Conclusion
Les expressions régulières en JavaScript sont un outil puissant pour manipuler des chaînes de caractères. Elles permettent de définir des motifs de recherche complexes et de les appliquer à des chaînes de texte de manière flexible et efficace. En maîtrisant les motifs de recherche et les méthodes associées, vous pouvez accomplir des tâches de traitement de texte avancées.
Understanding Regular Expressions RegEX Javascript
On 04/02/2025
Understanding Regular Expressions (RegEX) in JavaScript
Regular expressions (RegEX) in JavaScript are a powerful tool for searching and manipulating strings. They allow you to define complex search patterns and apply them to text strings. Here is a detailed explanation of how they work:
1. Creating a Regular Expression
In JavaScript, you can create a regular expression in two ways:
- Literal: Using slashes
/
. - RegExp Object: Using the
RegExp
constructor.
Literal Example:
let regex = /pattern/;
RegExp Object Example:
let regex = new RegExp("pattern");
2. Using Regular Expressions
Regular expressions can be used with several string methods and RegExp object methods.
String Methods:
search()
: Searches for a match and returns the index of the first occurrence.match()
: Searches for all matches and returns an array of results.replace()
: Replaces matches with another string.split()
: Splits a string into an array based on matches.
RegExp Object Methods:
test()
: Checks if a string contains a match.exec()
: Searches for a match and returns an array of information about the match.
3. Practical Examples
Search for a Match:
let str = "Hello, world!"; let regex = /world/; let result = str.search(regex); // Returns 7
Find All Matches:
let str = "Hello, world! Hello, everyone!";
let regex = /Hello/g; // The 'g' flag means global, to find all occurrences
let result = str.match(regex); // Returns ["Hello", "Hello"]
Replace Matches:
let str = "Hello, world!"; let regex = /world/; let result = str.replace(regex, "universe"); // Returns "Hello, universe!"
Split a String:
let str = "one,two,three"; let regex = /,/; let result = str.split(regex); // Returns ["one", "two", "three"]
Test for a Match:
let str = "Hello, world!"; let regex = /world/; let result = regex.test(str); // Returns true
Use exec():
let str = "Hello, world!"; let regex = /world/; let result = regex.exec(str); // Returns ["world", index: 7, input: "Hello, world!", groups: undefined]
4. Flags
Regular expressions can include flags to modify their behavior:
i
: Case-insensitive.g
: Global search (find all occurrences).m
: Multi-line mode (treat each line separately).s
: DotAll mode (the dot.
matches newlines).u
: Unicode mode.y
: Sticky mode (search from thelastIndex
position).
Example with Flags:
let regex = /hello/i; // Case-insensitive let result = regex.test("Hello"); // Returns true
5. Search Patterns
Regular expressions allow you to define complex patterns:
^
: Start of the string.$
: End of the string..
: Any character except a newline.\d
: A digit.\w
: An alphanumeric character.\s
: A whitespace character.*
: Zero or more occurrences of the preceding pattern.+
: One or more occurrences of the preceding pattern.?
: Zero or one occurrence of the preceding pattern.{n}
: Exactlyn
occurrences of the preceding pattern.{n,m}
: Betweenn
andm
occurrences of the preceding pattern.
Complex Pattern Example:
let regex = /^\d{3}-\d{2}-\d{4}$/; // Matches a US Social Security Number
let result = regex.test("123-45-6789"); // Returns true
Conclusion
Regular expressions in JavaScript are a powerful tool for manipulating strings. They allow you to define complex search patterns and apply them to text strings flexibly and efficiently. By mastering search patterns and associated methods, you can accomplish advanced text processing tasks.
On 03/02/2025
How to give permission for an Azure app you has permission : Sites.Selected
You must be admin of Azure
In Graph Explorer use this request to get the Azure Id of you Site
Add for permission Sites.selected and Site.FullControl for the account (in modify permissions Tab)
https://graph.microsoft.com/v1.0/sites/m365x87466739.sharepoint.com:/sites/allcompany?$select=id
replace the m365x87466739 by your Tenant name, and allcompany by the name / url of your site,
it wiil returns you the azure id of your site
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#sites(id)/$entity",
"id": "m365x87466739.sharepoint.com,1dc83cb4-d304-4afa-a0ff-cf33270f1c8b,e615f00e-ac05-4dfc-928e-e51688e8273b"
}
Then post a news query https://graph.microsoft.com/v1.0/sites/m365x87466739.sharepoint.com,1dc83cb4-d304-4afa-a0ff-cf33270f1c8b,e615f00e-ac05-4dfc-928e-e51688e8273b/permissions
{
"roles": ["write"],//permission level you want to give
"grantedToIdentities": [{
"application": {
"id": "9fb5c53a-5f25-4100-ba33-9a4595390c27",//this is you App Id
"displayName": "AppSharePointDocebo"
}
}]
}
then you can use Connect-PnPOnline -Url $siteUrl -ClientId $clientId -Thumbprint $thumbprint -Tenant $tenantId
You can use site.url /_layouts/15/appinv.aspx opage, but you must be site coll Admin, and admin SharePoint on Azure
<AppPermissionRequests AllowAppOnlyPolicy="true">
<AppPermissionRequest Scope="https://myTrenant.sharepoint.com/sites/sites/site1" Right="FullControl" />
</AppPermissionRequests>
Fileshare Migration to SharePoint
On 07/01/2025
Use SharePoint Migration Tool to upload your folder in a document library, here base settings in a json file
{
"Tasks": [
{
"SourcePath": "\\\\mySite.fr\\divisions\\Processed matrices\\Doc1",
"TargetPath": "https://test.sharepoint.com/sites/test1",
"TargetList": "FDbase",
"TargetListRelativePath": "Satellites/Doc1",
"Options": {
"PreserveFileSharePermissions": false,
"MigrateHiddenFiles": true,
"MigrateReadOnlyFiles": true,
"PreserveLastModifiedTime": true,
"PreserveCreatedTime": true,
"SkipEmptyFolders": false
}
}
]
}
On 18/12/2024
Base hooks context
import { WebPartContext } from "@microsoft/sp-webpart-base";
export interface iMAContext {
context: WebPartContext;
}
component
import * as React from 'react'; import styles from './MaCountries.module.scss'; import type { IMaCountriesProps } from './IMaCountriesProps'; import { iMAContext } from '../../../entities/iMAContext'; //import { escape } from '@microsoft/sp-lodash-subset'; export const MaCountriesContext = React.createContext
<({} as iMAContext); export const MaCountries: React.FunctionComponent = (props: IMaCountriesProps) => { return ( MaCountriesContext
.Provider value={{ context: props.context }}><div></
dfvdfv</div>MaCountriesContext
.Provider >
); };
webpart
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
type IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import { IReadonlyTheme } from '@microsoft/sp-component-base';
import * as strings from 'MaCountriesWebPartStrings';
import { MaCountries } from './components/MaCountries';
import { IMaCountriesProps } from './components/IMaCountriesProps';
export interface IMaCountriesWebPartProps {
description: string;
}
export default class MaCountriesWebPart extends BaseClientSideWebPart {
private _isDarkTheme: boolean;
public render(): void {
const element: React.ReactElement = React.createElement(
MaCountries,
{
description: this.properties.description,
context: this.context,
isDarkTheme: this._isDarkTheme
}
);
ReactDom.render(element, this.domElement);
}
protected onInit(): Promise {
return this._getEnvironmentMessage().then(message => {
});
}
private _getEnvironmentMessage(): Promise {
if (!!this.context.sdks.microsoftTeams) { // running in Teams, office.com or Outlook
return this.context.sdks.microsoftTeams.teamsJs.app.getContext()
.then(context => {
let environmentMessage: string = '';
switch (context.app.host.name) {
case 'Office': // running in Office
environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentOffice : strings.AppOfficeEnvironment;
break;
case 'Outlook': // running in Outlook
environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentOutlook : strings.AppOutlookEnvironment;
break;
case 'Teams': // running in Teams
case 'TeamsModern':
environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentTeams : strings.AppTeamsTabEnvironment;
break;
default:
environmentMessage = strings.UnknownEnvironment;
}
return environmentMessage;
});
}
return Promise.resolve(this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentSharePoint : strings.AppSharePointEnvironment);
}
protected onThemeChanged(currentTheme: IReadonlyTheme | undefined): void {
if (!currentTheme) {
return;
}
this._isDarkTheme = !!currentTheme.isInverted;
const {
semanticColors
} = currentTheme;
if (semanticColors) {
this.domElement.style.setProperty('--bodyText', semanticColors.bodyText || null);
this.domElement.style.setProperty('--link', semanticColors.link || null);
this.domElement.style.setProperty('--linkHovered', semanticColors.linkHovered || null);
}
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('description', {
label: strings.DescriptionFieldLabel
})
]
}
]
}
]
};
}
}
hide native CSS
@import '~@fluentui/react/dist/sass/References.scss';
div[data-automation-id="pageHeader"] {
display: none;
}
Shared css
@import '~@fluentui/react/dist/sass/References.scss';
/* remove this color TODO*/
html,
body {
background-color: #d2d9dd !important;
}
:global root-148.root-148.root-148.root-148.root-148 {
background-color: #d2d9dd !important;
}
:global CanvasSection {
background-color: #d2d9dd !important;
}
//global, hide not decored css or native hoverride
:global .fui-DatePicker__popupSurface {
background-color: #fff;
}
/* end remove this color TODO*/
$eulightBlue: #007dba;
$eulightDark: #10377A;
$euAlert: #AB0000;
$euGrey: #ECF6FA;
$euligthGrey: #D8E4EA;
$euTextColor: #4C7A8F;
$euAlertOrange: #E38100;
$euOkGreen: #00B400;
$red: #FA505C;
//bold
$fontBoldWeight: 600;
$fontBoldSize: 14px;
//bolder
$fontBolderWeight: 600;
$fontBolderSize: 16px;
//normal
$fontNormalWeight: 500;
$fontNormalSize: 12px;
//small
$fontSmallWeight: 500;
$fontSmallSize: 10px;
.shared {
.eutclear {
display: block;
clear: both;
line-height: 0;
}
.eutclearfix:before,
.eutclearfix:after {
display: table;
clear: both;
content: '';
}
.container {
// border: 1px solid black;
width: 100%;
// margin-right: auto;
// margin-left: auto;
}
@media (min-width: 576px) {
.container {
max-width: 540px;
}
}
@media (min-width: 768px) {
.container {
max-width: 720px;
}
}
@media (min-width: 992px) {
.container {
max-width: 960px;
}
}
@media (min-width: 1200px) {
.container {
max-width: 1140px;
}
}
.containerFluid {
width: 100%;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
}
.row {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
}
.col3 {
margin: auto;
-webkit-box-flex: 0;
max-width: 100%;
flex: none;
-ms-flex: none;
}
@media (min-width: 576px) {
.col3 {
max-width: 100%;
flex: none;
-ms-flex: none;
}
}
@media (min-width: 768px) {
.col3 {
-webkit-box-flex: 0;
-ms-flex: 0 0 32%;
flex: 0 0 32%;
max-width: 32%;
}
}
@media (min-width: 992px) {
.col3 {
-webkit-box-flex: 0;
-ms-flex: 0 0 31%;
flex: 0 0 31%;
max-width: 31%;
}
}
@media (min-width: 1200px) {
.col3 {
-webkit-box-flex: 0;
-ms-flex: 0 0 32%;
flex: 0 0 32%;
max-width: 32%;
}
}
}
On 16/12/2024
Group Data Table
Id | Group | Name | Group | Count | |
---|---|---|---|---|---|
1 | Group1 | Dietrich | Group1 | 3 | |
2 | Group1 | toto | Group2 | 2 | |
3 | Group1 | titi | Group3 | 1 | |
4 | Group2 | nana | |||
5 | Group2 | popo | |||
6 | Group3 | pipi |
Unique value : =UNIQUE(B2:B7), for a specific sheet : =UNIQUE(Append1_1!F2:F337888)// here Append1_1 is the name of the sheet
Count unique value : =COUNTIF(B2:B7,F1)
Group | Count |
---|---|
Group1 | 3 |
Group2 | 2 |
Group3 | 1 |
On 16/12/2024
Power Query Functions with Examples
Function | Description | Example |
---|---|---|
Table.AddColumn |
Adds a new column to a table. | Table.AddColumn(Source, "NewColumn", each [OldColumn] * 2) |
Table.RemoveColumns |
Removes one or more columns from a table. | Table.RemoveColumns(Source, {"Column1", "Column2"}) |
Table.SelectRows |
Filters rows based on a condition. | Table.SelectRows(Source, each [Age] > 18) |
Table.Sort |
Sorts a table by one or more columns. | Table.Sort(Source, {{"ColumnName", Order.Ascending}}) |
Text.Upper |
Converts text to uppercase. | Text.Upper("hello") returns HELLO |
Text.Lower |
Converts text to lowercase. | Text.Lower("HELLO") returns hello |
Text.Combine |
Combines a list of text values into one. | Text.Combine({"Hello", "World"}, " ") returns Hello World |
Number.Round |
Rounds a number to a specified number of digits. | Number.Round(3.14159, 2) returns 3.14 |
DateTime.LocalNow |
Returns the current date and time. | DateTime.LocalNow() |
List.Sum |
Calculates the sum of a list of numbers. | List.Sum({1, 2, 3, 4}) returns 10 |
List.Distinct |
Removes duplicate values from a list. | List.Distinct({1, 2, 2, 3}) returns {1, 2, 3} |
Record.Field |
Gets the value of a field in a record. | Record.Field([Record], "FieldName") |
Record.AddField |
Adds a field to a record. | Record.AddField([Record], "NewField", 123) |
Table.AddColumn |
Adds a new column to a table. | Table.AddColumn(Source, "NewColumn", each [OldColumn] * 2) |
Table.RemoveColumns |
Removes one or more columns from a table. | Table.RemoveColumns(Source, {"Column1", "Column2"}) |
Table.SelectRows |
Filters rows based on a condition. | Table.SelectRows(Source, each [Age] > 18) |
Table.Sort |
Sorts a table by one or more columns. | Table.Sort(Source, {{"ColumnName", Order.Ascending}}) |
Text.Upper |
Converts text to uppercase. | Text.Upper("hello") returns HELLO |
Text.Lower |
Converts text to lowercase. | Text.Lower("HELLO") returns hello |
Text.Combine |
Combines a list of text values into one. | Text.Combine({"Hello", "World"}, " ") returns Hello World |
Number.Round |
Rounds a number to a specified number of digits. | Number.Round(3.14159, 2) returns 3.14 |
DateTime.LocalNow |
Returns the current date and time. | DateTime.LocalNow() |
List.Sum |
Calculates the sum of a list of numbers. | List.Sum({1, 2, 3, 4}) returns 10 |
List.Average |
Calculates the average of a list of numbers. | List.Average({1, 2, 3, 4}) returns 2.5 |
List.Max |
Returns the maximum value from a list. | List.Max({1, 2, 3, 4}) returns 4 |
List.Min |
Returns the minimum value from a list. | List.Min({1, 2, 3, 4}) returns 1 |
List.Count |
Counts the number of items in a list. | List.Count({1, 2, 3, 4}) returns 4 |
List.Distinct |
Removes duplicate values from a list. | List.Distinct({1, 2, 2, 3}) returns {1, 2, 3} |
List.Length |
Returns the length of a list. | List.Length({1, 2, 3, 4}) returns 4 |
Record.Field |
Gets the value of a field in a record. | Record.Field([Record], "FieldName") |
Record.AddField |
Adds a field to a record. | Record.AddField([Record], "NewField", 123) |
SharePoint Get All Documents Permissions
On 13/12/2024
Get all RoleAssignments from a document library even more than 5000 elements
only when HasUniqueRoleAssignments == true
let currentSort = { column: null, direction: 'asc' }; // Store the current sort state
// Creates the style element
function createStyleElement(id, content) {
var style = document.createElement("style");
style.type = "text/css";
style.id = id;
style.innerHTML = content;
if (style.styleSheet) {
style.styleSheet.cssText = content;
} else {
let st = document.getElementById(id);
if (st == undefined) {
var head = document.head || document.getElementsByTagName("head")[i];
head.appendChild(style);
} else {
st.innerHTML = content;
}
}
return style;
}
// Function to filter the table based on dropdown selection
function filterTable(columnIndex, value) {
let table, tr, td, i, select, selectedValue, txtValue;
table = document.querySelector("table");
tr = table.getElementsByTagName("tbody")[0].getElementsByTagName("tr");
select = table.getElementsByTagName("select")[columnIndex];
//debugger;
selectedValue = value;
// Loop through all table rows and hide those that don't match the filter
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td")[columnIndex];
if (td) {
txtValue = td.textContent || td.innerText;
if (selectedValue === "" || txtValue === selectedValue) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
}
function sortTable(columnIndex, direction) {
let table, rows, switching, i, x, y, shouldSwitch;
table = document.querySelector("table");
switching = true;
let tbody = table.querySelector("tbody");
// Set the current sort state
currentSort.column = columnIndex;
currentSort.direction = direction;
while (switching) {
switching = false;
rows = tbody.rows;
for (i = 0; i < rows.length - 1; i++) {
shouldSwitch = false;
x = rows[i].getElementsByTagName("td")[columnIndex];
y = rows[i + 1].getElementsByTagName("td")[columnIndex];
let isNumber = false;
if (!isNaN(x.innerHTML)) {
// Check if rows should switch based on ascending or descending order
if (direction === 'asc') {
if (parseFloat(x.innerHTML) > parseFloat(y.innerHTML)) {
shouldSwitch = true;
break;
}
} else if (direction === 'desc') {
if (parseFloat(x.innerHTML) < parseFloat(y.innerHTML)) {
shouldSwitch = true;
break;
}
}
}
else {
// Check if rows should switch based on ascending or descending order
if (direction === 'asc') {
if (x.innerHTML.toLowerCase() > y.innerHTML.toLowerCase()) {
shouldSwitch = true;
break;
}
} else if (direction === 'desc') {
if (x.innerHTML.toLowerCase() < y.innerHTML.toLowerCase()) {
shouldSwitch = true;
break;
}
}
}
}
if (shouldSwitch) {
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
switching = true;
}
}
}
// Function to generate the table
function generateTableFromJson2(jsonArray, select, addHeaders = true) {
const style = `
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 8px 12px;
text-align: left;
border: 1px solid #ddd;
}
tbody tr{
max-height: 15px;
}
th {
background-color: #f4f4f4;
color: #000;
}
/* Scrollable table wrapper */
.table-wrapper {
max-height: 800px;
overflow-y: auto;
border: 1px solid #ddd;
}
/* Style for dropdowns in header */
select {
width: 100%;
padding: 4px;
margin-top: 5px;
}
/* Style for the sorting arrows */
.sort-arrows {
cursor: pointer;
margin-left: 5px;
}
`;
createStyleElement("fdiStyle", style);
// Create table element
let table = document.createElement('table');
// Create table header
let header = table.createTHead();
let headerRow = header.insertRow(0);
// Get keys (headers) from the first object in the JSON array
//let keys = Object.keys(jsonArray[0]);
let keys = select.split(",");
if (addHeaders) {
keys.forEach((key, index) => {
if (key !== "__metadata") {
let th = document.createElement('th');
th.innerHTML = key;
// Create a dropdown (select) for filtering
let select = document.createElement('select');
select.addEventListener('change', function () {
const selectedValue = select.value;
filterTable(index, selectedValue);
});
// Populate dropdown with unique values from the JSON data
let uniqueValues = [...new Set(jsonArray.map(item => item[key]))];
// Add a default "All" option for no filter
let optionAll = document.createElement('option');
optionAll.value = "";
optionAll.text = `All`;
select.appendChild(optionAll);
// Create an option for each unique value
if (typeof (uniqueValues[0]) === typeof (1)) {
const pp = uniqueValues.sort((a, b) => {
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
});
pp.forEach(value => {
let option = document.createElement('option');
option.value = value;
option.text = value;
select.appendChild(option);
});
} else
uniqueValues.sort().forEach(value => {
let option = document.createElement('option');
option.value = value;
option.text = value;
select.appendChild(option);
});
// Sort arrows for sorting the columns
let upArrow = document.createElement('span');
upArrow.innerHTML = '⬆️';
upArrow.classList.add('sort-arrows');
upArrow.onclick = () => sortTable(index, 'asc');
let downArrow = document.createElement('span');
downArrow.innerHTML = '⬇️';
downArrow.classList.add('sort-arrows');
downArrow.onclick = () => sortTable(index, 'desc');
th.appendChild(select); // Append the dropdown to the header
th.appendChild(upArrow); // Append the dropdown to the header
th.appendChild(downArrow); // Append the dropdown to the header
headerRow.appendChild(th);
}
});
}
// Create table body and populate rows with data
let tbody = document.createElement('tbody');
jsonArray.forEach((item) => {
let row = tbody.insertRow();
keys = select.split(",");
keys.forEach((key) => {
let cell = row.insertCell();
if (key !== "__metadata") {
cell.setAttribute("nowrap", "nowrap");
if (key === "RoleDefinitionBindings") {
cell.appendChild(generateTableFromJson2(item.RoleDefinitionBindings.results, "Name,Id", false));
} else if (key.indexOf("/") > 0) {
cell.innerHTML = item[key.split("/")[0]][key.split("/")[1]]
} else
cell.innerHTML = item[key]; // Insert each value from the JSON into the table cell
}
});
});
// Append the body to the table
table.appendChild(tbody);
return table;
}
function removeSlasches(select, datas) {
const ret = [];
const fields = select.split(',');
for (let i = 0; i < datas.length; i++) {
const toAdd = {};
for (let j = 0; j < fields.length; j++) {
if (fields[j].indexOf('/') > 0) {
const splitted = fields[j].split('/');
toAdd[splitted.join('')] = datas[i][splitted[0]][splitted[1]];
} else
toAdd[fields[j]] = datas[i][fields[j]];
}
ret.push(toAdd);
}
console.log("removeSlasches", ret);
return ret;
}
async function GetPermissionsInFolder(siteUrl, listUrl1, query) {
// Fetch options with headers for authentication and response format
const fetchOptions = {
method: 'GET',
headers: {
'Accept': 'application/json;odata=verbose'
}
};
//get web relativeUrl
var req = `${siteUrl}/_api/web?$select=ServerRelativeUrl`;
const webServerRelativUrl = (await (await fetch(req, fetchOptions)).json()).d.ServerRelativeUrl;
let query1 = "";
if (`${query}`.trim() !== "") {
query1 = `&$filter=${query}`;
query = ` and ${query}`;
}
//get firstId
req = `${siteUrl}/_api/web/getlist('${webServerRelativUrl}/${listUrl1}')/items?$select=Id&$top=1&$orderby=Id asc`;
console.log("req", req);
const firstId = 0;//(await (await fetch(req, fetchOptions)).json()).d.results[0].Id;
console.log("firstId", firstId);
//get lastId
//req = `${siteUrl}/_api/web/getlist('${webServerRelativUrl}/${listUrl1}')/items?$select=Id&$filter=Id gt 170 and Id lt 533&$top=1&$orderby=Id desc`;
req = `${siteUrl}/_api/web/getlist('${webServerRelativUrl}/${listUrl1}')/items?$select=Id&$top=1&$orderby=Id desc`;
console.log("last", req);
const lastId = (await (await fetch(req, fetchOptions)).json()).d.results[0].Id;
console.log("lastId", lastId);
let startId = firstId;
let endId = firstId + 5000;
var allItems = [];
console.log(`startId ${startId} endId ${endId} lastId ${lastId}`)
console.log("query", query);
do {
var select = "?$select=FileDirRef,RoleAssignments,HasUniqueRoleAssignments,Id,Title,FileLeafRef,Modified,Created";
req = `${siteUrl}/_api/web/getlist('${webServerRelativUrl}/${listUrl1}')/items${select}&$filter=Id ge ${startId} and Id lt ${endId}${query}&$orderby=Id asc&$top=5000`;
console.log("req", req);
// Send the asynchronous GET request to the REST API endpoint
var respList1 = await fetch(req, fetchOptions);
const items = (await respList1.json()).d.results;
//get only items with unique permissions
const withUniquePermissions = items.filter(user => user.HasUniqueRoleAssignments);
console.log("withUniquePermissions", withUniquePermissions.length);
allItems.push(...withUniquePermissions);
startId += 5000;
endId += 5000;
console.log(`startId ${startId} endId ${endId} lastId ${lastId}`)
}
while (endId < lastId);
return allItems;
}
async function batchFetchPermissions(siteUrl, itemIds, listUrl1) {
const batchBoundary = "batch_" + new Date().getTime();
const fetchOptions = {
method: 'GET',
headers: {
'Accept': 'application/json;odata=verbose'
}
};
const webServerRelativUrl = (await (await fetch(`${siteUrl}/_api/web?$select=ServerRelativeUrl`, fetchOptions)).json()).d.ServerRelativeUrl;
const batchBody = itemIds.map((item) => {
return `
--${batchBoundary}
Content-Type: application/http
Content-Transfer-Encoding: binary
GET ${siteUrl}/_api/web/getlist('${webServerRelativUrl}/${listUrl1}')/items(${item.Id})/RoleAssignments?$expand=Member,RoleDefinitionBindings HTTP/1.1
`;
}).join("\n") + `\n--${batchBoundary}--`;
const digest = await GetDigestValue(siteUrl);//
//console.log("digest", digest);
const headers = {
Accept: "application/json;odata=verbose",
"X-RequestDigest": digest,
"Content-Type": `multipart/mixed; boundary="${batchBoundary}"`,
};
console.log("itemIds", itemIds.length);
const response = await fetch(`${siteUrl}/_api/$batch`, {
method: "POST",
headers,
body: batchBody,
});
const text = await response.text(); // Parse response manually (multipart)
return parseBatchResponse(text, itemIds);
}
async function GetDigestValue(siteUrl) {//
const fetchOptions = {
method: 'POST',
headers: {
'Accept': 'application/json;odata=verbose',
'Content-type': 'application/json;odata=verbose'
}
};
const response = await fetch(siteUrl + "/_api/contextinfo", fetchOptions);
return (await response.json()).d.GetContextWebInformation.FormDigestValue;
}
function nodeParser2(xml) {
var parser = new DOMParser();
var doc = parser.parseFromString(xml, "application/xml");
// Define a namespace resolver
var nsResolver = function (prefix) {
var ns = {
'atom': 'http://www.w3.org/2005/Atom',
'd': 'http://schemas.microsoft.com/ado/2007/08/dataservices',
'm': 'http://schemas.microsoft.com/ado/2007/08/dataservices/metadata',
'georss': 'http://www.georss.org/georss',
'gml': 'http://www.opengis.net/gml'
};
return ns[prefix] || null;
};
// Use the namespace resolver in the XPath evaluation
var result = doc.evaluate('/atom:feed/atom:entry', doc, nsResolver, XPathResult.ANY_TYPE, null);
var entries = [];
var entry = result.iterateNext();
while (entry) {
// Check if the entry contains a category with term="SP.User"
var category = entry.getElementsByTagNameNS('http://www.w3.org/2005/Atom', 'category');
let userName = "";
let permissions = [];
for (var i = 0; i < category.length; i++) {//
if (category[i].getAttribute('term') === 'SP.User') {
// Get the d:Email value
let email = entry.getElementsByTagNameNS('http://schemas.microsoft.com/ado/2007/08/dataservices', 'Email');
if (email.length > 0 && email[0].textContent.trim() !== "") {
userName = email[0].textContent;//
} else {
email = entry.getElementsByTagNameNS('http://schemas.microsoft.com/ado/2007/08/dataservices', 'Title');
userName = email[0].textContent;//
}
}
else if (category[i].getAttribute('term') === 'SP.Group') {
// Get the d:Email value
let email = entry.getElementsByTagNameNS('http://schemas.microsoft.com/ado/2007/08/dataservices', 'Email');
if (email.length > 0 && email[0].textContent.trim() !== "") {
userName = email[0].textContent;//
} else {
email = entry.getElementsByTagNameNS('http://schemas.microsoft.com/ado/2007/08/dataservices', 'Title');
userName = email[0].textContent;//
}
}
else if (category[i].getAttribute('term') === 'SP.RoleDefinition') {
var Name = entry.getElementsByTagNameNS('http://schemas.microsoft.com/ado/2007/08/dataservices', 'Name');
if (Name.length > 0) {
permissions.push(Name[0].textContent);
}
}
}
if (userName === "") {
debugger;
}
entries.push({
"userName": userName,
"permissions": permissions,
});
entry = result.iterateNext();
}
//console.log("entries:", entries);
return entries;
}
// Helper to parse batch responses
function parseBatchResponse(responseText, itemIds) {
//console.log("responseText", responseText);
var results = [];
const parts = responseText.split("--batchresponse");
let i = 0;
for (const part of parts) {
if (part.includes("HTTP/1.1 200 OK")) {
const xmlStart = part.indexOf("");
if (xmlStart > -1 && xmlEnd > -1) {
let xmlString = part.substring(xmlStart, xmlEnd + 8);
results.push({
"item": itemIds[i],
"permissions": nodeParser2(xmlString)
})
i++;
}
}
}
return results;
}
function chunkArray(array, chunkSize) {
let result = [];
for (let i = 0; i < array.length; i += chunkSize) {
// Utilise slice pour découper le tableau
result.push(array.slice(i, i + chunkSize));
}
return result;
}
const siteUrl = _spPageContextInfo.webAbsoluteUrl || "https://test.sharepoint.com/sites/BIPvvvv";
const withUniquePermissions = await GetPermissionsInFolder(siteUrl, "Shared%20Documents", "");
//"startswith(FileDirRef, '/sites/BIPvvvv/Shared%20Documents/General/07%20-%20Release%201')"
console.log("withUniquePermissions", withUniquePermissions);
console.log("withUniquePermissions length", withUniquePermissions.length);
const permmissionItemsAll = [];
if (withUniquePermissions.length > 0) {
const withUniquePermissionsCutted = chunkArray(withUniquePermissions, 30);
for (let i = 0; i < withUniquePermissionsCutted.length; i++) {
//debugger;
const permmissionItems = await batchFetchPermissions(siteUrl, withUniquePermissionsCutted[i], "Shared%20Documents");
permmissionItemsAll.push(...permmissionItems)
}
console.log("permmissionItems", permmissionItemsAll);
}
//FileDirRef,RoleAssignments,HasUniqueRoleAssignments,Id,Title,FileLeafRef,Modified,Created
const toDisplay = [];
let csv = "Id;Title;Modified;Created;UserPermission;Permissions;FileLeafRef;FileDirRef\n";
for (let i = 0; i < permmissionItemsAll.length; i++) {
const permmissionItem = permmissionItemsAll[i];
for (let j = 0; j < permmissionItem.permissions.length; j++) {
for (let k = 0; k < permmissionItem.permissions[j].permissions.length; k++) {
//debugger;
const item = {
"Id": permmissionItem.item.Id,
"FileDirRef": permmissionItem.item.FileDirRef,
"HasUniqueRoleAssignments": permmissionItem.item.HasUniqueRoleAssignments,
"Title": permmissionItem.item.Title,
"FileLeafRef": permmissionItem.item.FileLeafRef,
"FileDirRef": permmissionItem.item.FileDirRef,
"Modified": permmissionItem.item.Modified,
"Created": permmissionItem.item.Created,
"UserPermission": permmissionItem.permissions[j].userName,
"Permissions": permmissionItem.permissions[j].permissions[k]
}
csv += `${item.Id};${item.Title};${item.Modified};${item.Created};${item.UserPermission};${item.Permissions};${item.FileLeafRef};${item.FileDirRef}` + "\n";
toDisplay.push(item);
}
}
}
document.body.innerHTML = `<div id="tableContainer" class="table-wrapper"></div>`;
const table = generateTableFromJson2(toDisplay, "Id,Title,Modified,Created,UserPermission,Permissions,FileLeafRef,FileDirRef");
console.log("csv", csv);
// Append the table to the container
document.getElementById('tableContainer').appendChild(table);
navigator.clipboard.writeText(csv);